diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 25ccb0912a040037509d742883416659528cbb5d..832b7541625a90ee06cf3f235117ddf73c3623cd 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -95,8 +95,7 @@ cargo-check-benches: stage: test <<: *docker-env script: - - ./scripts/build.sh --locked - - time cargo check --benches + - BUILD_DUMMY_WASM_BINARY=1 time cargo check --benches --all - sccache -s @@ -107,7 +106,7 @@ cargo-check-subkey: - /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 script: - cd ./subkey - - time cargo check --release # makes sense to save artifacts for building it + - BUILD_DUMMY_WASM_BINARY=1 time cargo check --release # makes sense to save artifacts for building it - sccache -s @@ -122,29 +121,62 @@ test-linux-stable: &test-linux variables: - $DEPLOY_TAG script: - - ./scripts/build.sh --locked - time cargo test --all --release --verbose --locked - sccache -s + +test-srml-staking: &test-srml-staking + stage: test + <<: *docker-env + variables: + # Enable debug assertions since we are running optimized builds for testing + # but still want to have debug assertions. + RUSTFLAGS: -Cdebug-assertions=y + RUST_BACKTRACE: 1 + except: + variables: + - $DEPLOY_TAG + only: + changes: + - .gitlab-ci.yml + - srml/staking/* + script: + - cd srml/staking/ + - time cargo test --release --verbose --no-default-features --features std + - sccache -s + + + + + + test-linux-stable-int: <<: *test-linux except: refs: - - /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 + - /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 variables: - $DEPLOY_TAG script: - - ./scripts/build.sh --locked - - time RUST_LOG=sync=trace,consensus=trace,client=trace,state-db=trace,db=trace,forks=trace,state_db=trace,storage_cache=trace - cargo test -p node-cli --release --verbose --locked -- --ignored --test-threads=1 + - echo "___Logs will be partly shown at the end in case of failure.___" + - echo "___Full log will be saved to the job artifacts only in case of failure.___" + - RUST_LOG=sync=trace,consensus=trace,client=trace,state-db=trace,db=trace,forks=trace,state_db=trace,storage_cache=trace + time cargo test -p node-cli --release --verbose --locked -- --ignored --test-threads=1 + &> ${CI_COMMIT_SHORT_SHA}_int_failure.log - sccache -s - allow_failure: true + after_script: + - awk '/FAILED/,0' ${CI_COMMIT_SHORT_SHA}_int_failure.log + artifacts: + name: $CI_COMMIT_SHORT_SHA + when: on_failure + expire_in: 24 hrs + paths: + - ${CI_COMMIT_SHORT_SHA}_int_failure.log check-web-wasm: stage: test <<: *docker-env - allow_failure: true except: - /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 script: @@ -161,9 +193,12 @@ check-web-wasm: - 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-offchain - 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 @@ -188,7 +223,6 @@ build-linux-release: variables: - $DEPLOY_TAG script: - - ./scripts/build.sh --locked - time cargo build --release --verbose - mkdir -p ./artifacts - mv ./target/release/substrate ./artifacts/. @@ -206,6 +240,28 @@ build-linux-release: - cp -r scripts/docker/* ./artifacts - sccache -s +build-linux-subkey: + stage: build + <<: *collect-artifacts + <<: *docker-env + # <<: *build-only + except: + variables: + - $DEPLOY_TAG + script: + - cd ./subkey + - BUILD_DUMMY_WASM_BINARY=1 time cargo build --release --verbose + - cd .. + # - time cargo build --release + - sccache -s + - mkdir -p ./artifacts + - mv ./target/release/subkey ./artifacts/. + - echo -n "Subkey version = " + - ./artifacts/subkey --version | + sed -n -r 's/^subkey ([0-9.]+.*)/\1/p' | + tee ./artifacts/SUBKEY-VERSION; + - sha256sum ./artifacts/subkey | tee ./artifacts/subkey.sha256 + build-rust-doc-release: stage: build <<: *docker-env @@ -218,9 +274,8 @@ build-rust-doc-release: - ./crate-docs <<: *build-only script: - - ./scripts/build.sh --locked - rm -f ./crate-docs/index.html # use it as an indicator if the job succeeds - - time cargo +nightly doc --release --all --verbose + - BUILD_DUMMY_WASM_BINARY=1 time cargo +nightly doc --release --all --verbose - cp -R ./target/doc ./crate-docs - echo "" > ./crate-docs/index.html - sccache -s @@ -231,6 +286,7 @@ build-rust-doc-release: stage: publish dependencies: - build-linux-release + - build-linux-subkey <<: *build-only <<: *kubernetes-build @@ -289,6 +345,7 @@ publish-s3-release: - aws s3 ls s3://${BUCKET}/${PREFIX}/latest/ --recursive --human-readable --summarize + publish-s3-doc: stage: publish image: parity/awscli:latest diff --git a/Cargo.lock b/Cargo.lock index c8590dec462265cc48be8d19987d20fbfc900a68..f33170df8fe39a48802c49d0e8fdb684af74bf7f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,10 +38,10 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -49,10 +49,10 @@ name = "aio-limited" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (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-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -91,7 +91,7 @@ dependencies = [ [[package]] name = "asn1_der" -version = "0.6.1" +version = "0.6.2" 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)", @@ -103,7 +103,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -116,8 +116,8 @@ name = "atty" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "termion 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -128,23 +128,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "backtrace" -version = "0.3.30" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace-sys 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "backtrace-sys" -version = "0.1.28" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -188,14 +187,14 @@ dependencies = [ "cfg-if 0.1.9 (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)", - "env_logger 0.6.1 (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.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "which 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -209,6 +208,11 @@ name = "bitmask" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "bitvec" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "blake2" version = "0.8.0" @@ -216,7 +220,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -246,7 +250,7 @@ dependencies = [ "block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -254,7 +258,7 @@ name = "block-cipher-trait" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -272,20 +276,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bstr" -version = "0.1.4" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-automata 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "build_const" -version = "0.2.1" +name = "build-helper" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "bumpalo" -version = "2.4.3" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -318,11 +328,32 @@ dependencies = [ "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "c2-chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "c_linked_list" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "cargo_metadata" +version = "0.8.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)", + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cast" version = "0.2.2" @@ -333,7 +364,7 @@ name = "cc" version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -361,9 +392,10 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "libc 0.2.59 (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)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", @@ -375,8 +407,8 @@ version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "libloading 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -429,7 +461,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -437,14 +469,6 @@ name = "core-foundation-sys" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "crc" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "crc32fast" version = "1.2.0" @@ -462,19 +486,19 @@ dependencies = [ "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)", "criterion-plot 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "csv 1.0.7 (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.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (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.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "rand_xoshiro 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon-core 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.40 (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.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -513,15 +537,6 @@ dependencies = [ "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "crossbeam-deque" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "crossbeam-deque" version = "0.6.3" @@ -540,20 +555,6 @@ dependencies = [ "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "crossbeam-epoch" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "crossbeam-epoch" version = "0.7.1" @@ -575,14 +576,6 @@ dependencies = [ "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "crossbeam-utils" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "crossbeam-utils" version = "0.6.5" @@ -616,27 +609,28 @@ name = "crypto-mac" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", "subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "csv" -version = "1.0.7" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "csv-core 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "bstr 0.2.1 (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 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "csv-core" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -645,7 +639,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -682,7 +676,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)", "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "subtle 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -700,7 +694,20 @@ dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "derive_more" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.3.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.12 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -718,10 +725,10 @@ dependencies = [ [[package]] name = "digest" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -733,6 +740,11 @@ dependencies = [ "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "doc-comment" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "ed25519-dalek" version = "1.0.0-pre.1" @@ -760,13 +772,13 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -780,7 +792,7 @@ name = "erased-serde" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -788,7 +800,7 @@ name = "error-chain" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.30 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)", "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -797,7 +809,7 @@ name = "exit-future" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -806,7 +818,7 @@ name = "failure" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.30 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)", "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -817,7 +829,7 @@ 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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -831,31 +843,29 @@ name = "fdlimit" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "finality-grandpa" -version = "0.7.2" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "hashmap_core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec-derive 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "fixed-hash" -version = "0.3.2" +version = "0.4.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)", - "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.5.6 (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)", @@ -863,14 +873,15 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.7" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", "miniz-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "miniz_oxide_c_api 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz_oxide_c_api 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -896,7 +907,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "fork-tree" version = "2.0.0" dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -905,8 +916,17 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "libloading 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -931,7 +951,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "futures" -version = "0.1.27" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures-channel-preview" +version = "0.3.0-alpha.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-core-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-core-preview" +version = "0.3.0-alpha.17" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -939,10 +973,71 @@ name = "futures-cpupool" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-executor-preview" +version = "0.3.0-alpha.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-channel-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "futures-io-preview" +version = "0.3.0-alpha.17" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures-preview" +version = "0.3.0-alpha.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-channel-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-executor-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-io-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-sink-preview" +version = "0.3.0-alpha.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-core-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-timer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-util-preview" +version = "0.3.0-alpha.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-io-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "gcc" version = "0.3.55" @@ -959,7 +1054,7 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.12.0" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -972,7 +1067,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "c_linked_list 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "get_if_addrs-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -982,7 +1077,16 @@ 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.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "getrandom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -997,41 +1101,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "globset" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "aho-corasick 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "bstr 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", + "bstr 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "h2" -version = "0.1.23" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", "indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "string 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "hash-db" -version = "0.12.2" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "hash256-std-hasher" -version = "0.12.2" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1072,30 +1176,13 @@ name = "hex" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "hex-literal" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "hex-literal-impl 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "hex-literal" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "hex-literal-impl 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hex-literal-impl" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro-hack 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1103,7 +1190,7 @@ name = "hex-literal-impl" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro-hack 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1122,7 +1209,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1151,14 +1238,14 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-buf 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "httparse" -version = "1.3.3" +version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1175,7 +1262,7 @@ version = "0.10.16" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1189,31 +1276,31 @@ dependencies = [ [[package]] name = "hyper" -version = "0.12.29" +version = "0.12.31" 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.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "h2 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", + "h2 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "http 0.1.17 (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.3 (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)", "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-buf 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (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-reactor 0.1.9 (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.14 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "want 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "want 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1228,10 +1315,10 @@ dependencies = [ [[package]] name = "impl-codec" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1240,7 +1327,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1248,7 +1335,7 @@ name = "impl-serde" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1271,7 +1358,7 @@ name = "iovec" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1295,67 +1382,68 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "js-sys" -version = "0.3.22" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "wasm-bindgen 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.47 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "jsonrpc-client-transports" -version = "12.0.0" +version = "12.1.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)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.12.29 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.12.31 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 12.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-pubsub 12.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", "websocket 0.22.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "jsonrpc-core" -version = "12.0.0" +version = "12.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "jsonrpc-core-client" -version = "12.0.0" +version = "12.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "jsonrpc-client-transports 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-client-transports 12.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "jsonrpc-derive" -version = "12.0.0" +version = "12.1.0" 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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "jsonrpc-http-server" -version = "12.0.0" +version = "12.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "hyper 0.12.29 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-server-utils 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.12.31 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 12.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-server-utils 12.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1364,38 +1452,38 @@ dependencies = [ [[package]] name = "jsonrpc-pubsub" -version = "12.0.0" +version = "12.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "jsonrpc-core 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 12.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "jsonrpc-server-utils" -version = "12.0.0" +version = "12.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "globset 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "globset 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 12.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "unicase 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "jsonrpc-ws-server" -version = "12.0.0" +version = "12.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "jsonrpc-core 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-server-utils 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 12.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-server-utils 12.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1409,12 +1497,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "keccak-hasher" -version = "0.12.2" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hash256-std-hasher 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hash256-std-hasher 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1456,7 +1544,7 @@ dependencies = [ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "rocksdb 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1469,6 +1557,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "lazy_static" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "lazycell" @@ -1477,12 +1568,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.58" +version = "0.2.59" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libloading" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1491,53 +1582,53 @@ dependencies = [ [[package]] name = "libp2p" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core-derive 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-deflate 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-dns 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-floodsub 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-identify 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-kad 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-mdns 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-mplex 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-noise 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-ping 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-plaintext 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-ratelimit 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-secio 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-tcp 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-uds 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-wasm-ext 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-websocket 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-yamux 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core-derive 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-deflate 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-dns 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-floodsub 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-identify 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-kad 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-mdns 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-mplex 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-noise 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-ping 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-plaintext 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-ratelimit 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-secio 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-tcp 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-uds 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-wasm-ext 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-websocket 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-yamux 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-multiaddr 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-multihash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "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-executor 0.1.7 (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.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "libp2p-core" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "asn1_der 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "asn1_der 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "bs58 0.2.2 (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)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "libsecp256k1 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1545,49 +1636,49 @@ dependencies = [ "parity-multiaddr 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-multihash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "protobuf 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", "rw-stream-sink 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (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)", "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.1 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "libp2p-core-derive" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "libp2p-deflate" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "flate2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "flate2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "libp2p-dns" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-dns-unofficial 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1595,16 +1686,16 @@ dependencies = [ [[package]] name = "libp2p-floodsub" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bs58 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "cuckoofilter 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "protobuf 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 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)", @@ -1614,28 +1705,28 @@ dependencies = [ [[package]] name = "libp2p-identify" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "parity-multiaddr 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "protobuf 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-timer 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "libp2p-kad" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1644,13 +1735,13 @@ dependencies = [ "bs58 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "parity-multiaddr 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-multihash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "protobuf 2.7.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)", "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1658,18 +1749,18 @@ dependencies = [ "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)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-timer 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "libp2p-mdns" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "data-encoding 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "dns-parser 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "parity-multiaddr 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1679,18 +1770,18 @@ dependencies = [ "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-udp 0.1.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.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "libp2p-mplex" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "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)", @@ -1700,33 +1791,33 @@ dependencies = [ [[package]] name = "libp2p-noise" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "curve25519-dalek 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "protobuf 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", "snow 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", "x25519-dalek 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "libp2p-ping" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "parity-multiaddr 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1734,50 +1825,50 @@ 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)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-timer 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "libp2p-plaintext" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "libp2p-ratelimit" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "aio-limited 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (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)", ] [[package]] name = "libp2p-secio" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "aes-ctr 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "asn1_der 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "asn1_der 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "ctr 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "hmac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "parity-send-wrapper 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "protobuf 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", "rw-stream-sink 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1785,21 +1876,21 @@ dependencies = [ "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", "twofish 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-futures 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", - "web-sys 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.47 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-futures 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", + "web-sys 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "libp2p-tcp" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (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)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "tk-listen 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1808,40 +1899,40 @@ dependencies = [ [[package]] name = "libp2p-uds" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "libp2p-wasm-ext" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-send-wrapper 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-futures 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.47 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-futures 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "libp2p-websocket" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "rw-stream-sink 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "soketto 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "soketto 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-rustls 0.10.0-alpha.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1851,11 +1942,11 @@ dependencies = [ [[package]] name = "libp2p-yamux" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", "yamux 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1869,7 +1960,7 @@ dependencies = [ "bindgen 0.47.3 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1885,6 +1976,17 @@ dependencies = [ "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "libz-sys" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "linked-hash-map" version = "0.5.2" @@ -1939,6 +2041,16 @@ dependencies = [ "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "malloc_size_of_derive" +version = "0.1.0" +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)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "matches" version = "0.1.8" @@ -1946,10 +2058,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "memchr" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1959,12 +2071,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "memory-db" -version = "0.12.2" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", "hashmap_core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-util-mem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1997,12 +2109,12 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "miniz_oxide" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2010,13 +2122,13 @@ dependencies = [ [[package]] name = "miniz_oxide_c_api" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", - "crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "miniz_oxide 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz_oxide 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2028,7 +2140,7 @@ dependencies = [ "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)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2053,7 +2165,7 @@ 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)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2074,7 +2186,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "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)", @@ -2096,7 +2208,7 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "openssl 0.10.23 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2104,7 +2216,7 @@ dependencies = [ "schannel 0.1.15 (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)", - "tempfile 3.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2113,7 +2225,7 @@ version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2125,7 +2237,7 @@ dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2134,20 +2246,23 @@ name = "node-cli" version = "2.0.0" dependencies = [ "exit-future 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "hex-literal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "node-executor 2.0.0", "node-primitives 2.0.0", "node-runtime 2.0.0", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", + "srml-balances 2.0.0", + "srml-contracts 2.0.0", "srml-finality-tracker 2.0.0", "srml-indices 2.0.0", + "srml-system 2.0.0", "srml-timestamp 2.0.0", - "structopt 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", "substrate-basic-authorship 2.0.0", "substrate-cli 2.0.0", "substrate-client 2.0.0", @@ -2155,6 +2270,7 @@ dependencies = [ "substrate-consensus-aura-primitives 2.0.0", "substrate-consensus-common 2.0.0", "substrate-finality-grandpa 2.0.0", + "substrate-finality-grandpa-primitives 2.0.0", "substrate-inherents 2.0.0", "substrate-keyring 2.0.0", "substrate-keystore 2.0.0", @@ -2164,7 +2280,7 @@ dependencies = [ "substrate-service-test 2.0.0", "substrate-telemetry 2.0.0", "substrate-transaction-pool 2.0.0", - "tokio 0.1.21 (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", ] @@ -2174,7 +2290,7 @@ version = "2.0.0" dependencies = [ "node-primitives 2.0.0", "node-runtime 2.0.0", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", "srml-balances 2.0.0", @@ -2193,7 +2309,7 @@ dependencies = [ "substrate-state-machine 2.0.0", "substrate-test-client 2.0.0", "substrate-trie 2.0.0", - "trie-root 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", + "trie-root 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", "wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2201,9 +2317,9 @@ dependencies = [ name = "node-primitives" version = "2.0.0" dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "sr-std 2.0.0", "substrate-primitives 2.0.0", @@ -2214,10 +2330,10 @@ dependencies = [ name = "node-rpc-client" version = "2.0.0" dependencies = [ - "env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.12.29 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core-client 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.12.31 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core-client 12.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "node-primitives 2.0.0", "substrate-rpc 2.0.0", @@ -2229,21 +2345,24 @@ version = "2.0.0" dependencies = [ "integer-sqrt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "node-primitives 2.0.0", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "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.92 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "sr-std 2.0.0", "sr-version 2.0.0", "srml-aura 2.0.0", + "srml-authorship 0.1.0", "srml-balances 2.0.0", + "srml-collective 2.0.0", "srml-contracts 2.0.0", - "srml-council 2.0.0", "srml-democracy 2.0.0", + "srml-elections 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-session 2.0.0", "srml-staking 2.0.0", @@ -2257,6 +2376,7 @@ dependencies = [ "substrate-keyring 2.0.0", "substrate-offchain-primitives 2.0.0", "substrate-primitives 2.0.0", + "substrate-wasm-builder-runner 1.0.2", ] [[package]] @@ -2266,10 +2386,10 @@ dependencies = [ "ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "derive_more 0.14.1 (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.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "node-template-runtime 2.0.0", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "substrate-basic-authorship 2.0.0", @@ -2282,8 +2402,8 @@ dependencies = [ "substrate-primitives 2.0.0", "substrate-service 2.0.0", "substrate-transaction-pool 2.0.0", - "tokio 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-root 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", + "trie-root 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", "vergen 3.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2291,9 +2411,9 @@ dependencies = [ name = "node-template-runtime" version = "2.0.0" dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.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.92 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", "sr-std 2.0.0", @@ -2310,6 +2430,7 @@ dependencies = [ "substrate-consensus-aura-primitives 2.0.0", "substrate-offchain-primitives 2.0.0", "substrate-primitives 2.0.0", + "substrate-wasm-builder-runner 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2327,7 +2448,7 @@ name = "nom" version = "4.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2353,7 +2474,7 @@ name = "num_cpus" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2392,7 +2513,7 @@ dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-sys 0.9.47 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2408,9 +2529,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", - "vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2444,12 +2565,13 @@ source = "git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7 [[package]] name = "parity-codec" -version = "3.5.1" +version = "4.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "bitvec 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec-derive 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2460,7 +2582,7 @@ 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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2475,7 +2597,7 @@ dependencies = [ "data-encoding 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "parity-multihash 0.1.2 (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.92 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "unsigned-varint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2498,6 +2620,16 @@ name = "parity-send-wrapper" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "parity-util-mem" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "malloc_size_of_derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "parity-wasm" version = "0.31.3" @@ -2548,7 +2680,7 @@ name = "parking_lot_core" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2559,7 +2691,7 @@ name = "parking_lot_core" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2571,7 +2703,7 @@ name = "parking_lot_core" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2585,9 +2717,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2599,7 +2731,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "paste-impl 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2607,10 +2739,10 @@ name = "paste-impl" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro-hack 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.8 (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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2620,7 +2752,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2633,11 +2764,21 @@ name = "percent-encoding" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "pin-utils" +version = "0.1.0-alpha.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "pkg-config" version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "ppv-lite86" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "pretty_assertions" version = "0.5.1" @@ -2660,13 +2801,13 @@ dependencies = [ [[package]] name = "primitive-types" -version = "0.2.4" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "fixed-hash 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "impl-codec 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "fixed-hash 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "impl-codec 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "impl-serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "uint 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "uint 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2679,27 +2820,14 @@ dependencies = [ [[package]] name = "proc-macro-hack" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro-hack-impl 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "proc-macro-hack" -version = "0.5.7" +version = "0.5.8" 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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "proc-macro-hack-impl" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "proc-macro2" version = "0.4.30" @@ -2710,7 +2838,7 @@ dependencies = [ [[package]] name = "protobuf" -version = "2.6.2" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -2738,7 +2866,7 @@ name = "quickcheck" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (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.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2757,7 +2885,7 @@ name = "rand" version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2767,7 +2895,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2780,7 +2908,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2791,7 +2919,7 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2803,6 +2931,18 @@ dependencies = [ "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.0 (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.1.1" @@ -2812,6 +2952,16 @@ dependencies = [ "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rand_chacha" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rand_core" version = "0.3.1" @@ -2825,6 +2975,14 @@ name = "rand_core" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "rand_core" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rand_hc" version = "0.1.0" @@ -2833,6 +2991,14 @@ dependencies = [ "rand_core 0.3.1 (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.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rand_isaac" version = "0.1.1" @@ -2846,7 +3012,7 @@ name = "rand_jitter" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2858,7 +3024,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2892,22 +3058,23 @@ dependencies = [ [[package]] name = "rayon" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon-core 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rayon-core" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2921,7 +3088,7 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.1.54" +version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -2929,7 +3096,7 @@ name = "redox_termios" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2939,19 +3106,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "regex" -version = "1.1.7" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "aho-corasick 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "utf8-ranges 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "regex-automata" +version = "0.1.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)", +] + [[package]] name = "regex-syntax" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2959,7 +3134,7 @@ dependencies = [ [[package]] name = "remove_dir_all" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2967,14 +3142,13 @@ dependencies = [ [[package]] name = "rhododendron" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec-derive 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2984,7 +3158,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2995,7 +3169,7 @@ name = "rocksdb" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "librocksdb-sys 5.18.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3005,7 +3179,7 @@ version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3046,13 +3220,13 @@ version = "0.1.2" 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.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ryu" -version = "0.2.8" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -3128,7 +3302,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3140,12 +3314,21 @@ dependencies = [ "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "semver" +version = "0.6.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" 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)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3160,30 +3343,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.92" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde_derive 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive" -version = "1.0.92" +version = "1.0.94" 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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_json" -version = "1.0.39" +version = "1.0.40" 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 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3192,7 +3375,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3220,7 +3403,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3232,7 +3415,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3264,10 +3447,10 @@ name = "slog-json" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.7 (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.92 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "slog 2.4.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3288,7 +3471,7 @@ 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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3315,14 +3498,15 @@ dependencies = [ [[package]] name = "soketto" -version = "0.1.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "flate2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3347,7 +3531,7 @@ 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)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (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.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3358,8 +3542,8 @@ dependencies = [ "substrate-primitives 2.0.0", "substrate-state-machine 2.0.0", "substrate-test-runtime-client 2.0.0", - "syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)", - "trybuild 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", + "trybuild 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3367,16 +3551,16 @@ name = "sr-io" version = "2.0.0" dependencies = [ "environmental 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", "libsecp256k1 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (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-offchain 2.0.0", "substrate-primitives 2.0.0", "substrate-state-machine 2.0.0", "substrate-trie 2.0.0", - "tiny-keccak 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3386,10 +3570,11 @@ dependencies = [ "integer-sqrt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "primitive-types 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "paste 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "primitive-types 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-std 2.0.0", "substrate-primitives 2.0.0", @@ -3400,7 +3585,7 @@ name = "sr-sandbox" version = "2.0.0" dependencies = [ "assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (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-primitives 2.0.0", @@ -3420,8 +3605,8 @@ name = "sr-version" version = "2.0.0" dependencies = [ "impl-serde 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "sr-std 2.0.0", ] @@ -3430,8 +3615,8 @@ dependencies = [ name = "srml-assets" version = "2.0.0" dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", "sr-std 2.0.0", @@ -3445,9 +3630,9 @@ name = "srml-aura" version = "2.0.0" dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", "sr-std 2.0.0", @@ -3461,19 +3646,33 @@ dependencies = [ "substrate-primitives 2.0.0", ] +[[package]] +name = "srml-authorship" +version = "0.1.0" +dependencies = [ + "parity-codec 4.1.1 (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-babe" version = "2.0.0" dependencies = [ - "hex-literal 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "hex-literal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (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-session 2.0.0", + "srml-staking 2.0.0", "srml-support 2.0.0", "srml-system 2.0.0", "srml-timestamp 2.0.0", @@ -3486,9 +3685,9 @@ dependencies = [ name = "srml-balances" version = "2.0.0" dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.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.92 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", "sr-std 2.0.0", @@ -3498,6 +3697,23 @@ dependencies = [ "substrate-primitives 2.0.0", ] +[[package]] +name = "srml-collective" +version = "2.0.0" +dependencies = [ + "hex-literal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.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.94 (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-contracts" version = "2.0.0" @@ -3505,10 +3721,10 @@ dependencies = [ "assert_matches 1.3.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.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "parity-wasm 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)", "pwasm-utils 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", "sr-sandbox 2.0.0", @@ -3523,30 +3739,29 @@ dependencies = [ ] [[package]] -name = "srml-council" +name = "srml-democracy" version = "2.0.0" dependencies = [ - "hex-literal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.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.92 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (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-democracy 2.0.0", "srml-support 2.0.0", "srml-system 2.0.0", "substrate-primitives 2.0.0", ] [[package]] -name = "srml-democracy" +name = "srml-elections" version = "2.0.0" dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "hex-literal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.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.92 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", "sr-std 2.0.0", @@ -3560,8 +3775,8 @@ dependencies = [ name = "srml-example" version = "2.0.0" dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", "srml-balances 2.0.0", @@ -3575,8 +3790,8 @@ name = "srml-executive" version = "2.0.0" dependencies = [ "hex-literal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", "sr-std 2.0.0", @@ -3591,10 +3806,8 @@ dependencies = [ name = "srml-finality-tracker" version = "2.0.0" dependencies = [ - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", "sr-std 2.0.0", @@ -3604,12 +3817,26 @@ dependencies = [ "substrate-primitives 2.0.0", ] +[[package]] +name = "srml-generic-asset" +version = "2.0.0" +dependencies = [ + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (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-grandpa" version = "2.0.0" dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", "sr-std 2.0.0", @@ -3621,14 +3848,29 @@ dependencies = [ "substrate-primitives 2.0.0", ] +[[package]] +name = "srml-im-online" +version = "0.1.0" +dependencies = [ + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (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-session 2.0.0", + "srml-support 2.0.0", + "srml-system 2.0.0", + "substrate-primitives 2.0.0", +] + [[package]] name = "srml-indices" version = "2.0.0" dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "ref_thread_local 0.0.0 (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.92 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", "sr-std 2.0.0", @@ -3642,8 +3884,8 @@ dependencies = [ name = "srml-metadata" version = "2.0.0" dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "sr-std 2.0.0", "substrate-primitives 2.0.0", ] @@ -3653,9 +3895,9 @@ name = "srml-session" version = "2.0.0" dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.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.92 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", "sr-std 2.0.0", @@ -3663,16 +3905,17 @@ dependencies = [ "srml-system 2.0.0", "srml-timestamp 2.0.0", "substrate-primitives 2.0.0", + "substrate-trie 2.0.0", ] [[package]] name = "srml-staking" version = "2.0.0" dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (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.92 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", "sr-std 2.0.0", @@ -3689,8 +3932,8 @@ dependencies = [ name = "srml-sudo" version = "2.0.0" dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", "sr-std 2.0.0", @@ -3706,10 +3949,10 @@ version = "2.0.0" dependencies = [ "bitmask 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "once_cell 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "paste 0.1.5 (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.92 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", "sr-std 2.0.0", @@ -3717,6 +3960,7 @@ dependencies = [ "srml-support-procedural 2.0.0", "srml-system 2.0.0", "substrate-inherents 2.0.0", + "substrate-primitives 2.0.0", ] [[package]] @@ -3727,7 +3971,7 @@ dependencies = [ "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", "sr-api-macros 2.0.0", "srml-support-procedural-tools 2.0.0", - "syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3738,7 +3982,7 @@ dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", "srml-support-procedural-tools-derive 2.0.0", - "syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3747,20 +3991,20 @@ version = "2.0.0" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "srml-support-test" version = "2.0.0" dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "srml-support 2.0.0", "substrate-inherents 2.0.0", "substrate-primitives 2.0.0", - "trybuild 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "trybuild 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3768,9 +4012,9 @@ name = "srml-system" version = "2.0.0" dependencies = [ "criterion 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.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.92 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", "sr-std 2.0.0", @@ -3782,8 +4026,8 @@ dependencies = [ name = "srml-timestamp" version = "2.0.0" dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", "sr-std 2.0.0", @@ -3797,8 +4041,8 @@ dependencies = [ name = "srml-treasury" version = "2.0.0" dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", "sr-std 2.0.0", @@ -3828,12 +4072,12 @@ name = "stream-cipher" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "string" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3846,22 +4090,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "structopt" -version = "0.2.16" +version = "0.2.18" 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.16 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt-derive 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "structopt-derive" -version = "0.2.16" +version = "0.2.18" 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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3877,7 +4121,7 @@ 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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3889,12 +4133,14 @@ dependencies = [ "hex-literal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "node-primitives 2.0.0", "node-runtime 2.0.0", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "schnorrkel 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", - "substrate-bip39 0.2.1 (git+https://github.com/paritytech/substrate-bip39)", + "srml-balances 2.0.0", + "srml-system 2.0.0", + "substrate-bip39 0.2.2 (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)", ] @@ -3904,7 +4150,7 @@ name = "substrate" version = "2.0.0" dependencies = [ "ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (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)", ] @@ -3914,7 +4160,7 @@ name = "substrate-basic-authorship" version = "2.0.0" dependencies = [ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "substrate-client 2.0.0", "substrate-consensus-aura-primitives 2.0.0", @@ -3928,8 +4174,8 @@ dependencies = [ [[package]] name = "substrate-bip39" -version = "0.2.1" -source = "git+https://github.com/paritytech/substrate-bip39#44307fda4ea17fe97aeb93af317fbc8f6ed34193" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "hmac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3946,18 +4192,19 @@ dependencies = [ "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", "derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.6.2 (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.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "names 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "rpassword 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", - "structopt 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", "substrate-client 2.0.0", "substrate-keyring 2.0.0", "substrate-network 2.0.0", @@ -3968,7 +4215,7 @@ dependencies = [ "substrate-telemetry 2.0.0", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3977,13 +4224,13 @@ version = "2.0.0" dependencies = [ "derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hex-literal 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hex-literal 0.2.0 (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)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "sr-api-macros 2.0.0", "sr-primitives 2.0.0", @@ -4004,14 +4251,14 @@ dependencies = [ name = "substrate-client-db" version = "2.0.0" dependencies = [ - "env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db 0.14.0 (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)", "kvdb-rocksdb 0.1.4 (git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7987b8475878fc4d2e1f81d)", "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "substrate-client 2.0.0", @@ -4028,10 +4275,11 @@ dependencies = [ name = "substrate-consensus-aura" version = "2.0.0" dependencies = [ - "env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", @@ -4050,7 +4298,7 @@ dependencies = [ "substrate-service 2.0.0", "substrate-telemetry 2.0.0", "substrate-test-runtime-client 2.0.0", - "tokio 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "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)", ] @@ -4058,7 +4306,7 @@ dependencies = [ name = "substrate-consensus-aura-primitives" version = "2.0.0" dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "sr-std 2.0.0", "substrate-client 2.0.0", @@ -4069,12 +4317,13 @@ dependencies = [ name = "substrate-consensus-babe" version = "2.0.0" dependencies = [ - "env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "fork-tree 2.0.0", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "merlin 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec-derive 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "schnorrkel 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4095,7 +4344,7 @@ dependencies = [ "substrate-service 2.0.0", "substrate-telemetry 2.0.0", "substrate-test-runtime-client 2.0.0", - "tokio 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "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)", ] @@ -4103,7 +4352,8 @@ dependencies = [ name = "substrate-consensus-babe-primitives" version = "2.0.0" dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "schnorrkel 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "sr-std 2.0.0", "substrate-client 2.0.0", @@ -4116,10 +4366,10 @@ name = "substrate-consensus-common" version = "2.0.0" dependencies = [ "derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "sr-std 2.0.0", @@ -4127,7 +4377,7 @@ dependencies = [ "substrate-inherents 2.0.0", "substrate-primitives 2.0.0", "substrate-test-runtime-client 2.0.0", - "tokio-executor 0.1.7 (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)", ] @@ -4137,11 +4387,11 @@ version = "2.0.0" dependencies = [ "derive_more 0.14.1 (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.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rhododendron 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rhododendron 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", "sr-version 2.0.0", @@ -4152,16 +4402,16 @@ dependencies = [ "substrate-keyring 2.0.0", "substrate-primitives 2.0.0", "substrate-transaction-pool 2.0.0", - "tokio 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "substrate-consensus-slots" version = "2.0.0" dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "substrate-client 2.0.0", @@ -4178,21 +4428,24 @@ version = "2.0.0" dependencies = [ "assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "derive_more 0.14.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.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "libsecp256k1 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-version 2.0.0", + "substrate-client 2.0.0", + "substrate-offchain 2.0.0", "substrate-panic-handler 2.0.0", "substrate-primitives 2.0.0", + "substrate-runtime-test 2.0.0", "substrate-serializer 2.0.0", "substrate-state-machine 2.0.0", "substrate-trie 2.0.0", - "tiny-keccak 1.4.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.7.4 (registry+https://github.com/rust-lang/crates.io-index)", "wasmi 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -4201,15 +4454,16 @@ dependencies = [ name = "substrate-finality-grandpa" version = "2.0.0" dependencies = [ - "env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "finality-grandpa 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "finality-grandpa 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "fork-tree 2.0.0", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.40 (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", @@ -4222,15 +4476,17 @@ dependencies = [ "substrate-service 2.0.0", "substrate-telemetry 2.0.0", "substrate-test-runtime-client 2.0.0", - "tokio 0.1.21 (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)", ] [[package]] name = "substrate-finality-grandpa-primitives" version = "2.0.0" dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "sr-std 2.0.0", "substrate-client 2.0.0", @@ -4241,7 +4497,7 @@ dependencies = [ name = "substrate-inherents" version = "2.0.0" dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "sr-std 2.0.0", @@ -4265,7 +4521,7 @@ dependencies = [ "derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "substrate-primitives 2.0.0", "subtle 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4278,23 +4534,26 @@ dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "erased-serde 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "fork-tree 2.0.0", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-timer 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "linked_hash_set 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "quickcheck 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "slog 2.4.1 (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)", @@ -4308,31 +4567,31 @@ dependencies = [ "substrate-test-runtime 2.0.0", "substrate-test-runtime-client 2.0.0", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.21 (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)", - "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "unsigned-varint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "substrate-offchain" version = "2.0.0" dependencies = [ - "env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "substrate-client 2.0.0", - "substrate-consensus-common 2.0.0", + "substrate-client-db 2.0.0", + "substrate-network 2.0.0", "substrate-offchain-primitives 2.0.0", "substrate-primitives 2.0.0", "substrate-test-runtime-client 2.0.0", "substrate-transaction-pool 2.0.0", - "tokio 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -4347,7 +4606,7 @@ dependencies = [ name = "substrate-panic-handler" version = "2.0.0" dependencies = [ - "backtrace 0.3.30 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -4355,14 +4614,13 @@ dependencies = [ name = "substrate-peerset" version = "2.0.0" dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (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.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -4374,27 +4632,30 @@ dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "criterion 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "ed25519-dalek 1.0.0-pre.1 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hash256-std-hasher 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hash256-std-hasher 0.14.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.0 (registry+https://github.com/rust-lang/crates.io-index)", "impl-serde 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "primitive-types 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "primitive-types 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "schnorrkel 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "sr-std 2.0.0", - "substrate-bip39 0.2.1 (git+https://github.com/paritytech/substrate-bip39)", + "substrate-bip39 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "substrate-serializer 2.0.0", "tiny-bip39 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "twox-hash 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "twox-hash 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "wasmi 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -4403,17 +4664,18 @@ version = "2.0.0" dependencies = [ "assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core-client 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-derive 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-pubsub 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 12.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core-client 12.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-derive 12.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-pubsub 12.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.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 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", "sr-version 2.0.0", @@ -4424,28 +4686,39 @@ dependencies = [ "substrate-state-machine 2.0.0", "substrate-test-runtime-client 2.0.0", "substrate-transaction-pool 2.0.0", - "tokio 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "substrate-rpc-servers" version = "2.0.0" dependencies = [ - "jsonrpc-http-server 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-pubsub 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-ws-server 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-http-server 12.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-pubsub 12.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-ws-server 12.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "substrate-rpc 2.0.0", ] +[[package]] +name = "substrate-runtime-test" +version = "2.0.0" +dependencies = [ + "sr-io 2.0.0", + "sr-sandbox 2.0.0", + "sr-std 2.0.0", + "substrate-primitives 2.0.0", + "substrate-wasm-builder-runner 1.0.2", +] + [[package]] name = "substrate-serializer" version = "2.0.0" dependencies = [ - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -4454,16 +4727,18 @@ version = "2.0.0" dependencies = [ "derive_more 0.14.1 (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.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "node-executor 2.0.0", "node-primitives 2.0.0", "node-runtime 2.0.0", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-multiaddr 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "slog 2.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", @@ -4480,9 +4755,9 @@ dependencies = [ "substrate-telemetry 2.0.0", "substrate-test-runtime-client 2.0.0", "substrate-transaction-pool 2.0.0", - "sysinfo 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", + "sysinfo 0.9.0 (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.21 (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)", ] @@ -4490,9 +4765,9 @@ dependencies = [ name = "substrate-service-test" version = "2.0.0" dependencies = [ - "env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.6.2 (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.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "substrate-client 2.0.0", @@ -4501,16 +4776,16 @@ dependencies = [ "substrate-primitives 2.0.0", "substrate-service 2.0.0", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "substrate-state-db" version = "2.0.0" dependencies = [ - "env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "substrate-primitives 2.0.0", ] @@ -4519,17 +4794,17 @@ dependencies = [ name = "substrate-state-machine" version = "2.0.0" dependencies = [ - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", "hex-literal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "substrate-panic-handler 2.0.0", "substrate-primitives 2.0.0", "substrate-trie 2.0.0", - "trie-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-root 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", + "trie-db 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "trie-root 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -4537,18 +4812,18 @@ name = "substrate-telemetry" version = "2.0.0" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-timer 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "slog 2.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "slog-json 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "slog-scope 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -4556,9 +4831,9 @@ dependencies = [ name = "substrate-test-client" version = "2.0.0" dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "substrate-client 2.0.0", "substrate-client-db 2.0.0", @@ -4575,15 +4850,17 @@ version = "2.0.0" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "memory-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", "sr-std 2.0.0", "sr-version 2.0.0", + "srml-babe 2.0.0", "srml-executive 2.0.0", "srml-support 2.0.0", + "srml-system 2.0.0", + "srml-timestamp 2.0.0", "substrate-client 2.0.0", "substrate-consensus-aura-primitives 2.0.0", "substrate-consensus-babe-primitives 2.0.0", @@ -4594,7 +4871,8 @@ dependencies = [ "substrate-primitives 2.0.0", "substrate-test-runtime-client 2.0.0", "substrate-trie 2.0.0", - "trie-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-wasm-builder-runner 1.0.2", + "trie-db 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -4613,12 +4891,12 @@ version = "2.0.0" dependencies = [ "assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "substrate-primitives 2.0.0", "substrate-test-runtime 2.0.0", @@ -4629,9 +4907,9 @@ name = "substrate-transaction-pool" version = "2.0.0" dependencies = [ "derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "substrate-client 2.0.0", @@ -4646,19 +4924,40 @@ name = "substrate-trie" version = "2.0.0" dependencies = [ "criterion 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", "hex-literal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hasher 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "memory-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "keccak-hasher 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memory-db 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "sr-std 2.0.0", "substrate-primitives 2.0.0", - "trie-bench 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-root 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-standardmap 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "trie-bench 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "trie-db 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "trie-root 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "trie-standardmap 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "substrate-wasm-builder" +version = "1.0.4" +dependencies = [ + "build-helper 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cargo_metadata 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "fs2 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "substrate-wasm-builder-runner" +version = "1.0.2" + +[[package]] +name = "substrate-wasm-builder-runner" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "subtle" version = "1.0.0" @@ -4671,7 +4970,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "syn" -version = "0.15.35" +version = "0.15.39" 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)", @@ -4686,18 +4985,19 @@ 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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (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 = "sysinfo" -version = "0.8.5" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "doc-comment 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -4712,19 +5012,19 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tempfile" -version = "3.0.8" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", - "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -4738,12 +5038,12 @@ dependencies = [ [[package]] name = "termion" -version = "1.5.2" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -4768,8 +5068,8 @@ name = "time" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -4789,10 +5089,10 @@ dependencies = [ [[package]] name = "tiny-keccak" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -4800,8 +5100,8 @@ name = "tinytemplate" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -4809,32 +5109,31 @@ name = "tk-listen" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.21 (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)", ] [[package]] name = "tokio" -version = "0.1.21" +version = "0.1.22" 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.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-sync 0.1.6 (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.14 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-trace-core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -4846,7 +5145,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)", "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -4855,7 +5154,7 @@ version = "0.1.1" 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.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -4864,8 +5163,8 @@ name = "tokio-current-thread" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -4873,19 +5172,19 @@ name = "tokio-dns-unofficial" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-executor" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -4893,9 +5192,9 @@ name = "tokio-fs" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -4904,7 +5203,7 @@ version = "0.1.12" 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.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -4914,14 +5213,14 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (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)", ] @@ -4932,7 +5231,7 @@ version = "0.10.0-alpha.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)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (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)", @@ -4945,7 +5244,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -4954,7 +5253,7 @@ version = "0.1.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)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (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)", @@ -4963,18 +5262,18 @@ dependencies = [ [[package]] name = "tokio-threadpool" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -4983,9 +5282,9 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -4993,26 +5292,18 @@ name = "tokio-tls" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "tokio-trace-core" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "tokio-udp" version = "0.1.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)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5026,9 +5317,9 @@ version = "0.2.5" 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.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5042,7 +5333,7 @@ name = "toml" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -5055,7 +5346,7 @@ name = "transaction-factory" version = "0.0.1" dependencies = [ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "substrate-cli 2.0.0", "substrate-client 2.0.0", @@ -5066,26 +5357,26 @@ dependencies = [ [[package]] name = "trie-bench" -version = "0.12.2" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "criterion 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hasher 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "memory-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-root 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-standardmap 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "keccak-hasher 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memory-db 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "trie-db 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "trie-root 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "trie-standardmap 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "trie-db" -version = "0.12.2" +version = "0.14.0" 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.12.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", "hashmap_core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5093,19 +5384,19 @@ dependencies = [ [[package]] name = "trie-root" -version = "0.12.2" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "trie-standardmap" -version = "0.12.3" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hasher 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "keccak-hasher 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -5115,13 +5406,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "trybuild" -version = "1.0.5" +version = "1.0.6" 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.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.40 (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.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -5138,7 +5429,7 @@ dependencies = [ [[package]] name = "twox-hash" -version = "1.3.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5161,12 +5452,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "uint" -version = "0.7.1" +version = "0.8.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)", "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -5248,7 +5538,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "vcpkg" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -5262,7 +5552,7 @@ version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -5281,9 +5571,9 @@ name = "wabt" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "wabt-sys 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -5309,75 +5599,75 @@ dependencies = [ [[package]] name = "want" -version = "0.0.6" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasm-bindgen" -version = "0.2.45" +version = "0.2.47" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "wasm-bindgen-macro 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-macro 0.2.47 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.45" +version = "0.2.47" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bumpalo 2.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bumpalo 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-shared 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-shared 0.2.47 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasm-bindgen-futures" -version = "0.3.22" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.47 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.45" +version = "0.2.47" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-macro-support 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-macro-support 0.2.47 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.45" +version = "0.2.47" 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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-backend 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-shared 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-backend 0.2.47 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-shared 0.2.47 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.45" +version = "0.2.47" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "wasm-bindgen-webidl" -version = "0.2.45" +version = "0.2.47" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5385,21 +5675,22 @@ dependencies = [ "log 0.4.6 (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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-backend 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-backend 0.2.47 (registry+https://github.com/rust-lang/crates.io-index)", "weedle 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasm-timer" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", "send_wrapper 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "web-sys 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.47 (registry+https://github.com/rust-lang/crates.io-index)", + "web-sys 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -5423,15 +5714,14 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.22" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys 0.3.24 (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.45 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-webidl 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.47 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-webidl 0.2.47 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -5461,7 +5751,7 @@ dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.10.16 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5489,7 +5779,7 @@ 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)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -5545,7 +5835,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)", "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", "mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5590,7 +5880,7 @@ version = "0.2.1" 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.27 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "nohash-hasher 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5602,43 +5892,20 @@ dependencies = [ [[package]] name = "zeroize" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "zeroize" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "zeroize_derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "zeroize" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "zeroize_derive 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize_derive 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "zeroize_derive" -version = "0.1.0" -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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "zeroize_derive" -version = "0.8.0" +version = "0.9.0" 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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -5647,19 +5914,19 @@ dependencies = [ "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 aho-corasick 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e6f484ae0c99fec2e858eb6134949117399f222608d84cadb3f58c1f97c2364c" +"checksum aho-corasick 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "36b7aa1ccb7d7ea3f437cf025a2ab1c47cc6c1bc9fc84918ff449def12f5e282" "checksum aio-limited 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7f10b352bc3fc08ae24dc5d2d3ddcac153678533986122dc283d747b12071000" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e73a24bad9bd6a94d6395382a6c69fe071708ae4409f763c5475e14ee896313d" "checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" "checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" -"checksum asn1_der 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9893d63fc3b1c44231e667da6836a33f27d8b6b3bdc82f83da5dfd579d1b6528" +"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 assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7deb0a829ca7bcfaf5da70b073a8d128619259a7be8216a355e23f00763059e5" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" "checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf" -"checksum backtrace 0.3.30 (registry+https://github.com/rust-lang/crates.io-index)" = "ada4c783bb7e7443c14e0480f429ae2cc99da95065aeab7ee1b81ada0419404f" -"checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" +"checksum backtrace 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)" = "18b50f5258d1a9ad8396d2d345827875de4261b158124d4c819d9b351454fae5" +"checksum backtrace-sys 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)" = "5b3a000b9c543553af61bc01cbfc403b04b5caa9e421033866f2e98061eb3e61" "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" @@ -5667,6 +5934,7 @@ dependencies = [ "checksum bindgen 0.47.3 (registry+https://github.com/rust-lang/crates.io-index)" = "df683a55b54b41d5ea8ebfaebb5aa7e6b84e3f3006a78f010dadc9ca88469260" "checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" "checksum bitmask 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5da9b3d9f6f585199287a473f4f8dfab6566cf827d15c00c219f53c645687ead" +"checksum bitvec 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b67491e1cc6f37da6c4415cd743cb8d2e2c65388acc91ca3094a054cbf3cbd0c" "checksum blake2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "91721a6330935673395a0607df4d49a9cb90ae12d259f1b3e0a3f6e1d486872e" "checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" "checksum block-buffer 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1339a1042f5d9f295737ad4d9a6ab6bf81c84a933dba110b9200cd6d1448b814" @@ -5674,20 +5942,22 @@ dependencies = [ "checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" "checksum block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4dc3af3ee2e12f3e5d224e5e1e3d73668abbeb69e566d361f7d5563a4fdf09" "checksum bs58 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0de79cfb98e7aa9988188784d8664b4b5dad6eaaa0863b91d9a4ed871d4f7a42" -"checksum bstr 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "59604ece62a407dc9164732e5adea02467898954c3a5811fd2dc140af14ef15b" -"checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" -"checksum bumpalo 2.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "84dca3afd8e01b9526818b7963e5b4916063b3cdf9f10cf6b73ef0bd0ec37aa5" +"checksum bstr 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc0572e02f76cb335f309b19e0a0d585b4f62788f7d26de2a13a836a637385f" +"checksum build-helper 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bdce191bf3fa4995ce948c8c83b4640a1745457a149e73c6db75b4ffe36aad5f" +"checksum bumpalo 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2cd43d82f27d68911e6ee11ee791fb248f138f5d69424dc02e098d4f152b0b05" "checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" "checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" "checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" "checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" +"checksum c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101" "checksum c_linked_list 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4964518bd3b4a8190e832886cdc0da9794f12e8e6c1613a9e90ff331c4c8724b" +"checksum cargo_metadata 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "929766d993a2fde7a0ae962ee82429069cd7b68839cd9375b98efd719df65d3a" "checksum cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "926013f2860c46252efceabb19f4a6b308197505082c609025aa6706c011d427" "checksum cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "39f75544d7bbaf57560d2168f28fd649ff9c76153874db88bdbdfd839b1a7e7d" "checksum cexpr 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a7fa24eb00d5ffab90eaeaf1092ac85c04c64aaf358ea6f84505b8116d24c6af" "checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" -"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878" +"checksum chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "77d81f58b7301084de3b958691458a53c3f7e0b1d702f77e550b6a88e3a88abe" "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 clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "97276801e127ffb46b66ce23f35cc96bd454fa311294bced4bbace7baa8b1d17" @@ -5696,26 +5966,22 @@ dependencies = [ "checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e" "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 crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" "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-plot 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "76f9212ddf2f4a9eb2d401635190600656a1f88a932ef53d06e7fa4c7e02fb8e" "checksum crossbeam 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad4c7ea749d9fb09e23c5cb17e3b70650860553a0e2744e38446b1803bf7db94" "checksum crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f0ed1a4de2235cabda8558ff5840bffb97fcb64c97827f354a451307df5f72b" -"checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" "checksum crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "05e44b8cf3e1a625844d1750e1f7820da46044ff6d28f4d43e455ba3e5bb2c13" "checksum crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" -"checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150" "checksum crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "04c9e3102cc2d69cd681412141b390abd55a362afc1540965dad0ad4d34280b4" "checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" -"checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" "checksum crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f8306fcef4a7b563b76b7dd949ca48f52bc1141aa067d2ea09565f3e2652aa5c" "checksum crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" "checksum crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" "checksum crypto-mac 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "779015233ac67d65098614aec748ac1c756ab6677fa2e14cf8b37c08dfed1198" "checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" -"checksum csv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9044e25afb0924b5a5fc5511689b0918629e85d68ea591e5e87fbf1e85ea1b3b" -"checksum csv-core 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa5cdef62f37e6ffe7d1f07a381bc0db32b7a3ff1cac0de56cb0d81e71f53d65" +"checksum csv 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37519ccdfd73a75821cac9319d4fce15a81b9fcf75f951df5b9988aa3a0af87d" +"checksum csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9b5cadb6b25c77aeff80ba701712494213f4a8418fcda2ee11b6560c3ad0bf4c" "checksum ctor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3b4c17619643c1252b5f690084b82639dd7fac141c57c8e77a00e0148132092c" "checksum ctr 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "022cd691704491df67d25d006fe8eca083098253c4d43516c2206479c58c6736" "checksum ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7dfd2d8b4c82121dfdff120f818e09fc4380b0b7e17a742081a89b94853e87f" @@ -5723,14 +5989,16 @@ dependencies = [ "checksum curve25519-dalek 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5d4b820e8711c211745880150f5fac78ab07d6e3851d8ce9f5a02cedc199174c" "checksum data-encoding 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4f47ca1860a761136924ddd2422ba77b2ea54fe8cc75b9040804a0d9d32ad97" "checksum derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6d944ac6003ed268757ef1ee686753b57efc5fcf0ebe7b64c9fc81e7e32ff839" +"checksum derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a141330240c921ec6d074a3e188a7c7ef95668bb95e7d44fa0e5778ec2a7afe" "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.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c" +"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" "checksum dns-parser 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c4d33be9473d06f75f58220f71f7a9317aca647dc061dbd3c361b0bef505fbea" +"checksum doc-comment 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "923dea538cea0aa3025e8685b20d6ee21ef99c4f77e954a30febbaac5ec73a97" "checksum ed25519-dalek 1.0.0-pre.1 (registry+https://github.com/rust-lang/crates.io-index)" = "81956bcf7ef761fb4e1d88de3fa181358a0d26cbcb9755b587a08f9119824b86" "checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b" "checksum elastic-array 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "073be79b6538296faf81c631872676600616073817dd9a440c477ad09b408983" -"checksum env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b61fa891024a945da30a9581546e8cfaf5602c7b3f4c137a2805cf388f92075a" +"checksum env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3" "checksum environmental 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c7464757b80de8930c91c9afe77ddce501826bf9d134a87db2c67d9dc177e2c" "checksum erased-serde 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3beee4bc16478a1b26f2e80ad819a52d24745e292f521a63c16eea5f74b7eb60" "checksum error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3ab49e9dcb602294bc42f9a7dfc9bc6e936fca4418ea300dbfb84fe16de0b7d9" @@ -5739,49 +6007,57 @@ dependencies = [ "checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" "checksum fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1ee15a7050e5580b3712877157068ea713b245b080ff302ae2ca973cfcd9baa" -"checksum finality-grandpa 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5cdd9ef7c48777665dacc9657c272778121d4d09848100bcc2bd9c773c6cf837" -"checksum fixed-hash 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d1a683d1234507e4f3bf2736eeddf0de1dc65996dc0164d57eba0a74bcf29489" -"checksum flate2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f87e68aa82b2de08a6e037f1385455759df6e445a8df5e005b4297191dbf18aa" +"checksum finality-grandpa 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9e7cba2aaadf09932452a4fc054a77451b31eb99bc0b42bf54bd44f01a9daf4" +"checksum fixed-hash 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "516877b7b9a1cc2d0293cbce23cd6203f0edbfd4090e6ca4489fecb5aa73050e" +"checksum flate2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "550934ad4808d5d39365e5d61727309bf18b3b02c6c56b729cb92e7dd84bc3d8" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" "checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" "checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" "checksum fs-swap 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "921d332c89b3b61a826de38c61ee5b6e02c56806cade1b0e5d81bd71f57a71bb" +"checksum fs2 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" -"checksum futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)" = "a2037ec1c6c1c4f79557762eab1f7eae1f64f6cb418ace90fae88f0942b60139" +"checksum futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "45dc39533a6cae6da2b56da48edae506bb767ec07370f86f70fc062e9d435869" +"checksum futures-channel-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)" = "21c71ed547606de08e9ae744bb3c6d80f5627527ef31ecf2a7210d0e67bc8fae" +"checksum futures-core-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)" = "4b141ccf9b7601ef987f36f1c0d9522f76df3bba1cf2e63bfacccc044c4558f5" "checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" +"checksum futures-executor-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)" = "87ba260fe51080ba37f063ad5b0732c4ff1f737ea18dcb67833d282cdc2c6f14" +"checksum futures-io-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)" = "082e402605fcb8b1ae1e5ba7d7fdfd3e31ef510e2a8367dd92927bb41ae41b3a" +"checksum futures-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)" = "bf25f91c8a9a1f64c451e91b43ba269ed359b9f52d35ed4b3ce3f9c842435867" +"checksum futures-sink-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)" = "4309a25a1069a1f3c10647b227b9afe6722b67a030d3f00a9cbdc171fc038de4" +"checksum futures-timer 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb4a32e84935678650944c6ebd0d912db46405d37bf94f1a058435c5080abcb1" +"checksum futures-util-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)" = "af8198c48b222f02326940ce2b3aa9e6e91a32886eeaad7ca3b8e4c70daa3f4e" "checksum gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)" = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" -"checksum generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c0f28c2f5bfb5960175af447a2da7c18900693738343dc896ffbcabd9839592" +"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" "checksum generic-array 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fceb69994e330afed50c93524be68c42fa898c2d9fd4ee8da03bd7363acd26f2" "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.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e65cce4e5084b14874c4e7097f38cab54f47ee554f9194673456ea379dcc4c55" "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.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ef4feaabe24a0a658fd9cf4a9acf6ed284f045c77df0f49020ba3245cfb7b454" -"checksum h2 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)" = "1e42e3daed5a7e17b12a0c23b5b2fbff23a925a570938ebee4baca1a9a1a2240" -"checksum hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ba7fb417e5c470acdd61068c79767d0e65962e70836cf6c9dfd2409f06345ce0" -"checksum hash256-std-hasher 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f8b2027c19ec91eb304999abae7307d225cf93be42af53b0039f76e98ed5af86" +"checksum globset 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "925aa2cac82d8834e2b2a4415b6f6879757fb5c0928fc445ae76461a12eed8f2" +"checksum h2 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)" = "a539b63339fbbb00e081e84b6e11bd1d9634a82d91da2984a18ac74a8823f392" +"checksum hash-db 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c4a2710506bcc28e53b6d48d9686b233a31ad831597da7de91e6112a2fc8f260" +"checksum hash256-std-hasher 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff4a5dcbaf4fe8977852851d137546bcad8679c9582f170032ca35b30701138e" "checksum hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3bae29b6653b3412c2e71e9d486db9f9df5d701941d86683005efb9f2d28e3da" "checksum hashmap_core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8e04cb7a5051270ef3fa79f8c7604d581ecfa73d520e74f554e45541c4b5881a" "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" "checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" -"checksum hex-literal 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ddc2928beef125e519d69ae1baa8c37ea2e0d3848545217f6db0179c5eb1d639" "checksum hex-literal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c3da68162fdd2147e66682e78e729bd77f93b4c99656db058c5782d8c6b6225a" -"checksum hex-literal-impl 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "520870c3213943eb8d7803e80180d12a6c7ceb4ae74602544529d1643dc4ddda" "checksum hex-literal-impl 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "06095d08c7c05760f11a071b3e1d4c5b723761c01bd8d7201c30a9536668a612" "checksum hmac 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7a13f4163aa0c5ca1be584aace0e2212b2e41be5478218d4f657f5f778b2ae2a" "checksum hmac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f127a908633569f208325f86f71255d3363c79721d7f9fe31cd5569908819771" "checksum hmac-drbg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4fe727d41d2eec0a6574d887914347e5ff96a3b87177817e2a9820c5c87fecc2" "checksum http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "eed324f0f0daf6ec10c474f150505af2c143f251722bf9dbd1261bd1f2ee2c1a" "checksum http-body 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d" -"checksum httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e8734b0cfd3bc3e101ec59100e101c2eecd19282202e87808b3037b442777a83" +"checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" "checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" "checksum hyper 0.10.16 (registry+https://github.com/rust-lang/crates.io-index)" = "0a0652d9a2609a968c14be1a9ea00bf4b1d64e2e1f53a1b51b6fff3a6e829273" -"checksum hyper 0.12.29 (registry+https://github.com/rust-lang/crates.io-index)" = "e2cd6adf83b3347d36e271f030621a8cf95fd1fd0760546b9fc5a24a0f1447c7" +"checksum hyper 0.12.31 (registry+https://github.com/rust-lang/crates.io-index)" = "6481fff8269772d4463253ca83c788104a7305cb3fb9136bc651a6211e46e03f" "checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" -"checksum impl-codec 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d2050d823639fbeae26b2b5ba09aca8907793117324858070ade0673c49f793b" +"checksum impl-codec 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "62ed8ff267bc916dd848a800b96d3129aec73d5b23a5e3d018c83655d0c55371" "checksum impl-serde 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5158079de9d4158e0ce1de3ae0bd7be03904efc40b3d7dd8b8c301cbf6b52b56" "checksum impl-serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d26be4b97d738552ea423f76c4f681012ff06c3fa36fa968656b3679f60b4a1" "checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d" @@ -5791,17 +6067,17 @@ dependencies = [ "checksum ipnet 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e61c2da0d0f700c77d2d313dbf4f93e41d235fa12c6681fee06621036df4c2af" "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 js-sys 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "9987e7c13a91d9cf0efe59cca48a3a7a70e2b11695d5a4640f85ae71e28f5e73" -"checksum jsonrpc-client-transports 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0245e08f98d627a579cdee6a2138d05ab64f6093efbcdeec50805d121ee13c0c" -"checksum jsonrpc-core 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "288dca7f9713710a29e485076b9340156cb701edb46a881f5d0c31aa4f5b9143" -"checksum jsonrpc-core-client 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05e499e393aaa97cf5ff3a7444549c94a6d27e70be1c300b865187d722f1b426" -"checksum jsonrpc-derive 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b65eafb36286a4251c9a1d4cdf4e9a7cf8fa4f7bf991383e42f0cf26908767" -"checksum jsonrpc-http-server 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea8b3996f19dc6dd90d928c81d30b3ce9535840487734290da9fae3b3185db5d" -"checksum jsonrpc-pubsub 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fcdd238ecccde73faea93760b068f3fe3ca84caeb6b5414c2aabd4f008dad418" -"checksum jsonrpc-server-utils 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "036a53ffa47533dcccf1e1bb16abb0f45ef9a2dc9a63654d2d2cd199b80ad33e" -"checksum jsonrpc-ws-server 12.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "977ea40f077c027553e4112d750114b9e5cc7bcf5642512838abc2a9b322bd23" +"checksum js-sys 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "eac16f41aa9b9388230b1d6617d7ed897a1af5416b8fe1c8734dcef79c7aae10" +"checksum jsonrpc-client-transports 12.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6be24a8de4ced80f6fd8b6ace54aa610823a7642976a0e8e00e3bb2f4d8c33f0" +"checksum jsonrpc-core 12.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0216cf4c95fb373d89c63572672097b8aa74cfcdd77054accbf545d840be5bd7" +"checksum jsonrpc-core-client 12.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1603b6cc05060de7794c2962edd705e1ad2698bd2b0d2ddd4489f8c85df122b7" +"checksum jsonrpc-derive 12.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8afff172177878850d133ccdcd93cad794e85d7779ab334998d669ef80e13180" +"checksum jsonrpc-http-server 12.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a24e140242e0d2e9a694cf8db513a2bd739d24c392e0ad15e0d6d7ee8851e3a2" +"checksum jsonrpc-pubsub 12.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e3c45f7cdb1bb28a3bfb3a0a5184bf99669c9ffe8cf8d7b8a582f2a52bf9944a" +"checksum jsonrpc-server-utils 12.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d7aac8e0029d19582b68c9fd498d18bdcf0846612c968acc93b6e5ae67eea4e0" +"checksum jsonrpc-ws-server 12.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "698fee4fcaf09a5927b7e39dd8a8136a102b343cebacaa351fc4def01a050a5b" "checksum keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" -"checksum keccak-hasher 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "af672553b2abac1c86c29fd62c79880638b6abc91d96db4aa42a5baab2bc1ca9" +"checksum keccak-hasher 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3767172fe16797c41f975f12f38247964ace8e5e1a2539b82d5e19f9106b1cb9" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum kvdb 0.1.0 (git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7987b8475878fc4d2e1f81d)" = "" "checksum kvdb-memorydb 0.1.0 (git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7987b8475878fc4d2e1f81d)" = "" @@ -5809,30 +6085,31 @@ dependencies = [ "checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" "checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" -"checksum libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "6281b86796ba5e4366000be6e9e18bf35580adf9e63fbe2294aadb587613a319" -"checksum libloading 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a5692f82b51823e27c4118b3e5c0d98aee9be90633ebc71ad12afef380b50219" -"checksum libp2p 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6abde4e6fc777dc06ae2a15202ddedb1a38d7c71ed16bc10fa704b03f73aec37" -"checksum libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b4ceb4791289534d4c1ad8e4bd3c6f06d3670efa55ce71482951a287df93ddd1" -"checksum libp2p-core-derive 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "851a59dcaab66c96777ae0cace96de88a700243c3b8360ab51c7e093f3727066" -"checksum libp2p-deflate 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "902b44e92e1f8b7e697b3a186d15c841e0e38037f14286513207a5407650a635" -"checksum libp2p-dns 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71a6630a84552b39e5f752e1f6a951d31f3211079465d2e7af73491b6f48fc3f" -"checksum libp2p-floodsub 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9fced4da0c31e0dc8a759472c65fab41db40c01de2d93bc45e1431c13f0564f0" -"checksum libp2p-identify 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1ba5e882d72c71cdf77f45ab68dd715451d3b78a23085f8d385c7a31ec1b4272" -"checksum libp2p-kad 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d71966dbbb4cedcfcdb1d4c87d5dbb6f3f07b465d1ca74f2624256669997d1f2" -"checksum libp2p-mdns 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cdbdaea6f0049cc09ba5db00308f5b93105a8a33b65ba2e36bd35da707850ea2" -"checksum libp2p-mplex 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b351bfd67e97154e7b60f62402237671486c8a89f83eabdb6838f37d4d5f006" -"checksum libp2p-noise 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44324032b2f9260d2b862c741d79d250dc02298dbba56354a992528a826ee2d5" -"checksum libp2p-ping 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1ac43ffd01de4210cf1b969bbb55a008c77f9ec22b74df26a6590bb6bd4c93f" -"checksum libp2p-plaintext 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0506e10770bcbcb59f2a6154ce93c8fd5cb9730b6ceb5aa1463164af1fd0b9c6" -"checksum libp2p-ratelimit 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3886b79a35c0348497bab763517a9a2b4965173f4b4c7438d59f1e4dcf5122ff" -"checksum libp2p-secio 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b811272e5cd86d39bd71fb94687025d9802b13daf0998ebe0d3f2885c636c51a" -"checksum libp2p-tcp 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b2c54cb75f17557de6ce0149aa03e729455e2d240f84d854272bc4b11012a324" -"checksum libp2p-uds 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fbedf4a1e72a5f67523915414e9e12d71d128731873f0f24d8b878398fb47aa4" -"checksum libp2p-wasm-ext 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c1f615b56aa2a6f4ec07bf9667be9fff8877b9c5bd5335601af47490eda341" -"checksum libp2p-websocket 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a0d1bfe60577558f48a9fdf9f35c0ee2dc5baa01f685ff847d3b5cf4f12ee135" -"checksum libp2p-yamux 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bf4bfc7ff127cd622502dbe56f10513dd6776b970e33d8ebb6e367f0752324f6" +"checksum libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)" = "3262021842bf00fe07dbd6cf34ff25c99d7a7ebef8deea84db72be3ea3bb0aff" +"checksum libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" +"checksum libp2p 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29f6b3be5b0cb89f7a072352e2a3bf86991dce0909624181e9e343db0b558568" +"checksum libp2p-core 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c71c33e59899d57ed0a14272984705561abd71788a2b303598ec57dac32130e8" +"checksum libp2p-core-derive 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1e6df0fa6933f4be908cfd8c6d627776aa8c909066ba7ce13b017bfe18b9c92b" +"checksum libp2p-deflate 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "448fe9d2231bc21bb308f394346780666a376274ceaf3380e5c7adf3cdbf5a9c" +"checksum libp2p-dns 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67ec2cea26aaccd4bdf264075d6a499bc635b90cb23419bcc3b1f2f0d135c451" +"checksum libp2p-floodsub 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4c39c17f2b7c994106e00ccd71a9941d8574c01bef5f97e36d9a106cbde14fab" +"checksum libp2p-identify 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9455cc0752fd3e3f35e9464598576c54476772eaa927b773f7fdf5686ae51f" +"checksum libp2p-kad 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a1f595983a76012779d6941a9d51fc0b9b95d720315787bf8d73f6672351f6d8" +"checksum libp2p-mdns 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0e7847e6e13a793d70ee5a5d833ddb13ff277c4c0d4fc65b5bc8543ef37df8cf" +"checksum libp2p-mplex 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29bd0885dd9154d93a1fa83e06a10aba2f0e3a0bf9eb63233c095141fbfaf525" +"checksum libp2p-noise 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8f8e0852efc26bfcba11fcc7c4fb593ed00446c19b6d90db39794a3a7ac48e13" +"checksum libp2p-ping 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3dfda9d329eacf6a8e875c18b5e5317a47b326cb58372f506fff8b6259c8951a" +"checksum libp2p-plaintext 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "86759777e5441725b60c6e78b23933b03a531b45d1f3e7d1fb430df49e0b151c" +"checksum libp2p-ratelimit 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f965ff88fda7b1fff062b18b25d781b86c17ea335a237958220895f3e3ddfdd8" +"checksum libp2p-secio 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df1d980a61a1423518205f6710e692102c94efb8132b5dcc54ffe5dbac621360" +"checksum libp2p-tcp 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24faf4ebb10b805f2e2221540097f764075edd18ca735cab0430a118382888df" +"checksum libp2p-uds 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f57a4942babd03f582a838238093b08f94521f63c8b12889a914be5c3cc170c2" +"checksum libp2p-wasm-ext 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5427b52a8a51460961fadd72bd9fdcd957a2a7706588559423ccb86b58a52a7d" +"checksum libp2p-websocket 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "152dce704e235f47b9460004d7ac09663d43f4ca3cb99ddb8d4e0be54240673e" +"checksum libp2p-yamux 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "907bf1f31d572aa8537595a784f59c86b94162eb03dc51839c32ab4a05a5faad" "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 libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "2eb5e43362e38e2bca2fd5f5134c4d4564a23a5c28e9b95411652021a8675ebe" "checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" "checksum linked_hash_set 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3c7c91c4c7bbeb4f2f7c4e5be11e6a05bd6830bc37249c47ce1ad86ad453ff9c" "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" @@ -5840,16 +6117,17 @@ dependencies = [ "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" "checksum lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +"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.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39" +"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" -"checksum memory-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7623b01a4f1b7acb7cf8e3f678f05e15e6ae26cb0b738dfeb5cc186fd6b82ef4" +"checksum memory-db 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "896b24d1a9850e7a25b070d552f311cbb8735214456efa222dcc4c431073c215" "checksum memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" "checksum merlin 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8c39467de91b004f5b9c06fac5bbc8e7d28309a205ee66905166b70804a71fea" "checksum mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0" "checksum miniz-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9e3ae51cea1576ceba0dde3d484d30e6e5b86dee0b2d412fe3a16a15c98202" -"checksum miniz_oxide 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c468f2369f07d651a5d0bb2c9079f8488a66d5466efe42d0c5c6466edcb7f71e" -"checksum miniz_oxide_c_api 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b7fe927a42e3807ef71defb191dc87d4e24479b221e67015fe38ae2b7b447bab" +"checksum miniz_oxide 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b6c3756d66cf286314d5f7ebe74886188a9a92f5eee68b06f31ac2b4f314c99d" +"checksum miniz_oxide_c_api 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5b78ca5446dd9fe0dab00e058731b6b08a8c1d2b9cdb8efb10876e24e9ae2494" "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" @@ -5876,11 +6154,12 @@ dependencies = [ "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" "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)" = "" -"checksum parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dcb43c05fb71c03b4ea7327bf15694da1e0f23f19d5b1e95bab6c6d74097e336" +"checksum parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7902deb39d3b431897f211c1918789938251e67a740f55effd53201e79c0906c" "checksum parity-codec-derive 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "00a486fd383382ddcb2de928364b1f82571c1e48274fc43b7667a4738ee4056c" "checksum parity-multiaddr 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "045b3c7af871285146300da35b1932bb6e4639b66c7c98e85d06a32cbc4e8fa7" "checksum parity-multihash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eb83358a0c05e52c44d658981fec2d146d3516d1adffd9e553684f8c8e9e8fa5" "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.31.3 (registry+https://github.com/rust-lang/crates.io-index)" = "511379a8194230c2395d2f5fa627a5a7e108a9f976656ce723ae68fca4097bfc" "checksum parking_lot 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d4d05f1349491390b1730afba60bb20d55761bef489a954546b58b4b34e1e2ac" "checksum parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0802bff09003b291ba756dc7e79313e51cc31667e94afbe847def490424cde5" @@ -5895,16 +6174,16 @@ dependencies = [ "checksum pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" "checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" +"checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" "checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c" +"checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b" "checksum pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a029430f0d744bc3d15dd474d591bed2402b645d024583082b9f63bb936dac6" "checksum pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427" -"checksum primitive-types 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6e8612a8dc70f26276fed6131c153ca277cf275ee0a5e2a50cd8a69c697beb8f" +"checksum primitive-types 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "366ef730e56c11fd21ab3e518866cf7feb0fdf7f7c16ddc68485579e9d802787" "checksum proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e10d4b51f154c8a7fb96fd6dad097cb74b863943ec010ac94b9fd1be8861fe1e" -"checksum proc-macro-hack 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2c725b36c99df7af7bf9324e9c999b9e37d92c8f8caf106d82e1d7953218d2d8" -"checksum proc-macro-hack 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0c1dd4172a1e1f96f709341418f49b11ea6c2d95d53dca08c0f74cbd332d9cf3" -"checksum proc-macro-hack-impl 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2b753ad9ed99dd8efeaa7d2fb8453c8f6bc3e54b97966d35f1bc77ca6865254a" +"checksum proc-macro-hack 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "982a35d1194084ba319d65c4a68d24ca28f5fdb5b8bc20899e4eef8641ea5178" "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -"checksum protobuf 2.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e9076cae823584ab4d8fab3a111658d1232faf106611dc8378161b7d062b628" +"checksum protobuf 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5f00e4a3cb64ecfeac2c0a73c74c68ae3439d7a6bead3870be56ad5dd2620a6f" "checksum pwasm-utils 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "efb0dcbddbb600f47a7098d33762a00552c671992171637f5bb310b37fe1f0e4" "checksum quick-error 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5fb6ccf8db7bbcb9c2eae558db5ab4f3da1c2a87e4e597ed394726bc8ea6ca1d" "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" @@ -5914,26 +6193,31 @@ dependencies = [ "checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" "checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" +"checksum rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d47eab0e83d9693d40f825f86948aa16eff6750ead4bdffc4ab95b8b3a7f052c" "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" +"checksum rand_chacha 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e193067942ef6f485a349a113329140d0ab9e2168ce92274499bb0e9a4190d9d" "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" "checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0" +"checksum rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "615e683324e75af5d43d8f7a39ffe3ee4a9dc42c5c701167a71dc59c3a493aca" "checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" "checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" "checksum rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" "checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" "checksum rand_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 rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "373814f27745b2686b350dd261bfd24576a6fb0e2c5919b3a2b6005f820b0473" -"checksum rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b055d1e92aba6877574d8fe604a63c8b5df60f60e5982bf7ccbb1338ea527356" +"checksum rayon 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a4b0186e22767d5b9738a05eab7c6ac90b15db17e5b5f9bd87976dd7d89a10a4" +"checksum rayon-core 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbe0df8435ac0c397d467b6cad6d25543d06e8a019ef3f6af3c384597515bd2" "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -"checksum redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)" = "12229c14a0f65c4f1cb046a3b52047cdd9da1f4b30f8a39c5063c8bae515e252" +"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum ref_thread_local 0.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d813022b2e00774a48eaf43caaa3c20b45f040ba8cbf398e2e8911a06668dbe6" -"checksum regex 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0b2f0808e7d7e4fb1cb07feb6ff2f4bc827938f24f8c2e6a3beb7370af544bdd" -"checksum regex-syntax 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d76410686f9e3a17f06128962e0ecc5755870bb890c34820c7af7f1db2e1d48" -"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" -"checksum rhododendron 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ae9381ed76c1ec4e8994f1f7d2c6d7e33eed3ff7176e16fece09c2e993fc4a5a" +"checksum regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "d9d8297cc20bbb6184f8b45ff61c8ee6a9ac56c156cec8e38c3e5084773c44ad" +"checksum regex-automata 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "3ed09217220c272b29ef237a974ad58515bde75f194e3ffa7e6d0bf0f3b01f86" +"checksum regex-syntax 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "9b01330cce219c1c6b2e209e5ed64ccd587ae5c67bed91c0b49eecf02ae40e21" +"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" +"checksum rhododendron 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "057fecd57cc69e24d9d215c9f283a42133c3f48952e4fc06b088ecf3ce3d90bb" "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 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c34fa7bcae7fca3c8471e8417088bbc3ad9af8066b0ecf4f3c0d98a0d772716e" @@ -5942,7 +6226,7 @@ dependencies = [ "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 rw-stream-sink 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9cbe61c20455d3015b2bb7be39e1872310283b8e5a52f5b242b0ac7581fe78" -"checksum ryu 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "b96a9549dc8d48f2c283938303c4b5a77aa29bfbc5b54b084fb1630408899a8f" +"checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" "checksum safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7f7bf422d23a88c16d5090d455f182bc99c60af4df6a345c63428acf5129e347" "checksum safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dca453248a96cb0749e36ccdfe2b0b4e54a61bfef89fb97ec621eb8e0a93dd9" "checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267" @@ -5953,12 +6237,13 @@ dependencies = [ "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" +"checksum semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" "checksum send_wrapper 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a0eddf2e8f50ced781f288c19f18621fa72a3779e3cb58dbf23b07469b0abeb4" -"checksum serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)" = "32746bf0f26eab52f06af0d0aa1984f641341d06d8d673c693871da2d188c9be" -"checksum serde_derive 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)" = "46a3223d0c9ba936b61c0d2e3e559e3217dbfb8d65d06d26e8b3c25de38bae3e" -"checksum serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "5a23aa71d4a4d43fdbfaac00eff68ba8a06a51759a89ac3304323e800c4dd40d" +"checksum serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)" = "076a696fdea89c19d3baed462576b8f6d663064414b5c793642da8dfeb99475b" +"checksum serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)" = "ef45eb79d6463b22f5f9e16d283798b7c0175ba6050bc25c1a946c122727fe7b" +"checksum serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)" = "051c49229f282f7c6f3813f8286cc1e3323e8051823fce42c7ea80fe13521704" "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" @@ -5972,69 +6257,69 @@ dependencies = [ "checksum slog_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9eff3b513cf2e0d1a60e1aba152dc72bedc5b05585722bb3cebd7bcb1e31b98f" "checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" "checksum snow 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5a64f02fd208ef15bd2d1a65861df4707e416151e1272d02c8faafad1c138100" -"checksum soketto 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8cf3ae22c0bce5437c7dce6a2b00e492c19da1feb21ad64a7b6fd7058438c3f2" +"checksum soketto 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "db2383e992ba8ba8205cd1169cac2efdf325d3a0da465dc35f63a2074598347e" "checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3" "checksum spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44363f6f51401c34e7be73db0db371c04705d35efbe9f7d6082e03a921a32c55" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum static_assertions 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c19be23126415861cb3a23e501d34a708f7f9b2183c5252d690941c2e69199d5" "checksum static_slice 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "92a7e0c5e3dfb52e8fbe0e63a1b947bbb17b4036408b151353c4491374931362" "checksum stream-cipher 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8861bc80f649f5b4c9bd38b696ae9af74499d479dbfb327f0607de6b326a36bc" -"checksum string 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0bbfb8937e38e34c3444ff00afb28b0811d9554f15c5ad64d12b0308d1d1995" +"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.16 (registry+https://github.com/rust-lang/crates.io-index)" = "fa19a5a708e22bb5be31c1b6108a2a902f909c4b9ba85cba44c06632386bc0ff" -"checksum structopt-derive 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)" = "c6d59d0ae8ef8de16e49e3ca7afa16024a3e0dfd974a75ef93fdc5464e34523f" +"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 strum 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1810e25f576e7ffce1ff5243b37066da5ded0310b3274c20baaeccb1145b2806" "checksum strum_macros 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "572a2f4e53dd4c3483fd79e5cc10ddd773a3acb1169bbfe8762365e107110579" -"checksum substrate-bip39 0.2.1 (git+https://github.com/paritytech/substrate-bip39)" = "" +"checksum substrate-bip39 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d69ace596e9ca97837cc41f8edcfc4e0a997f227d5fc153d1010b60a0fe9acda" +"checksum substrate-wasm-builder-runner 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f52ecbff6cc3d6e5c6401828e15937b680f459d6803ce238f01fe615bc40d071" "checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" "checksum subtle 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "01dca13cf6c3b179864ab3292bd794e757618d35a7766b7c46050c614ba00829" -"checksum syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)" = "641e117d55514d6d918490e47102f7e08d096fdde360247e4a10f7a91a8478d3" +"checksum syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d960b829a55e56db167e861ddb43602c003c7be0bee1d345021703fac2fb7c" "checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" -"checksum sysinfo 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1cf62641ed7e88e20242b948d17b9fcc37e80b5599cf09cde190d6d4bb4bf289" +"checksum sysinfo 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c3e2cab189e59f72710e3dd5e1e0d5be0f6c5c999c326f2fdcdf3bf4483ec9fd" "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.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7dc4738f2e68ed2855de5ac9cdbe05c9216773ecde4739b2f095002ab03a13ef" +"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 termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dde0593aeb8d47accea5392b39350015b5eccb12c0d98044d856983d89548dea" +"checksum termion 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a8fb22f7cde82c8220e5aeacb3258ed7ce996142c77cba193f203515e26c330" "checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "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.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "dbbdebb0b801c7fa4260b6b9ac5a15980276d7d7bcc2dc2959a7c4dc8b426a1a" +"checksum tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d8a021c69bb74a44ccedb824a046447e2c84a01df9e5c20779750acb38e11b2" "checksum tinytemplate 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4574b75faccaacddb9b284faecdf0b544b80b6b294f3d062d325c5726a209c20" "checksum tk-listen 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5462b0f968c0457efe38fcd2df7e487096b992419e4f5337b06775a614bbda4b" -"checksum tokio 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "ec2ffcf4bcfc641413fa0f1427bf8f91dfc78f56a6559cbf50e04837ae442a87" +"checksum tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)" = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" "checksum tokio-buf 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fb220f46c53859a4b7ec083e41dec9778ff0b1851c0942b211edb89e0ccdc46" "checksum tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c501eceaf96f0e1793cf26beb63da3d11c738c4a943fdf3746d81d64684c39f" "checksum tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d16217cad7f1b840c5a97dfb3c43b0c871fef423a6e8d2118c604e843662a443" "checksum tokio-dns-unofficial 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "82c65483db54eb91b4ef3a9389a3364558590faf30ce473141707c0e16fda975" -"checksum tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "83ea44c6c0773cc034771693711c35c677b4b5a4b21b9e7071704c54de7d555e" +"checksum tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f27ee0e6db01c5f0b2973824547ce7e637b2ed79b891a9677b0de9bd532b6ac" "checksum tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe6dc22b08d6993916647d108a1a7d15b9cd29c4f4496c62b92c45b5041b7af" "checksum tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5090db468dad16e1a7a54c8c67280c5e4b544f3d3e018f0b913b400261f85926" "checksum tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6af16bfac7e112bea8b0442542161bfc41cbfa4466b580bdda7d18cb88b911ce" "checksum tokio-rustls 0.10.0-alpha.3 (registry+https://github.com/rust-lang/crates.io-index)" = "316fdbc899efec48b3b492bd0f339e6d81c4ee96a409257572147ec341943452" "checksum tokio-sync 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2162248ff317e2bc713b261f242b69dbb838b85248ed20bb21df56d60ea4cae7" "checksum tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1d14b10654be682ac43efee27401d792507e30fd8d26389e1da3b185de2e4119" -"checksum tokio-threadpool 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72558af20be886ea124595ea0f806dd5703b8958e4705429dd58b3d8231f72f2" +"checksum tokio-threadpool 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "90ca01319dea1e376a001e8dc192d42ebde6dd532532a5bad988ac37db365b19" "checksum tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "f2106812d500ed25a4f38235b9cae8f78a09edf43203e16e59c3b769a342a60e" "checksum tokio-tls 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "354b8cd83825b3c20217a9dc174d6a0c67441a2fae5c41bcb1ea6679f6ae0f7c" -"checksum tokio-trace-core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9c8a256d6956f7cb5e2bdfe8b1e8022f1a09206c6c2b1ba00f3b746b260c613" "checksum tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "66268575b80f4a4a710ef83d087fdfeeabdce9b74c797535fbac18a2cb906e92" "checksum tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "037ffc3ba0e12a0ab4aca92e5234e0dedeb48fddf6ccd260f1f150a36a9f2445" "checksum toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b8c96d7873fa7ef8bdeb3a9cda3ac48389b4154f32b9803b4bc26220b677b039" "checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" -"checksum trie-bench 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ba20f7d9865497ea46511860b43e05a44f4ac9a76ee089d34cd80a839a690264" -"checksum trie-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1ba73747fd3a64ab531274c04cb588dfa9d30d972d62990831e63fbce2cfec59" -"checksum trie-root 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cfa2e20c4f1418ac2e71ddc418e35e1b56e34022e2146209ffdbf1b2de8b1bd9" -"checksum trie-standardmap 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ebaa4b340046196efad8872b2dffe585b5ea330230dc44ee14e399f77da29f51" +"checksum trie-bench 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "401abff5ad06075d2c65d1eedeaaa70616d0df268f3186a82cf1aa2d798977d7" +"checksum trie-db 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1803d8ff63ec3743bee883aacf3df74c524ffab188d9abebe18ded4da0dcd5d4" +"checksum trie-root 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "226f4b2e7bc6a71172ffe7f137385cf63833de7c684059dde7520ddbf1fb04f4" +"checksum trie-standardmap 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b65b79aee5dcdcc7247fdd811f7e26b47e65ecc17f776ecf5db8e8fd46db3b54" "checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" -"checksum trybuild 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d1506db833ec4a139b8e3d2e88125d8999270cc944046ca1fb138f6bbfbc2e43" +"checksum trybuild 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b7592bfd3449da952920cb55618d55f34779293127f76d946c4a54f258ca87b8" "checksum twofish 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712d261e83e727c8e2dbb75dacac67c36e35db36a958ee504f2164fc052434e1" -"checksum twox-hash 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6c7bcecad121018bdcd6b709fa2325b004878fcb3d3067934ce90749f0faff9a" +"checksum twox-hash 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7834480552ffc48e1930ceddd701f47d2234319d80b7bcbbe2fe7202933c101" "checksum typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" -"checksum uint 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2143cded94692b156c356508d92888acc824db5bffc0b4089732264c6fcf86d4" +"checksum uint 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f5375d2c574f89adad4108ad525c93e39669853a602560bf5ed4ca9943b10799" "checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" "checksum unicase 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a84e5511b2a947f3ae965dcb29b13b7b1691b6e7332cf5dbc1744138d5acb7f6" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" @@ -6046,7 +6331,7 @@ dependencies = [ "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 utf8-ranges 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9d50aa7650df78abf942826607c62468ce18d9019673d4a2ebe1865dbb96ffde" -"checksum vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "def296d3eb3b12371b2c7d0e83bfe1403e4db2d7a0bba324a12b21c4ee13143d" +"checksum vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "33dd455d0f96e90a75803cfeb7f948768c08d70a6de9a8d2362461935698bf95" "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" @@ -6054,18 +6339,18 @@ dependencies = [ "checksum wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "74e463a508e390cc7447e70f640fbf44ad52e1bd095314ace1fdf99516d32add" "checksum wabt-sys 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a6265b25719e82598d104b3717375e37661d41753e2c84cde3f51050c7ed7e3c" "checksum walkdir 2.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c7904a7e2bb3cdf0cf5e783f44204a85a37a93151738fa349f06680f59a98b45" -"checksum want 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "797464475f30ddb8830cc529aaaae648d581f99e2036a928877dfde027ddf6b3" -"checksum wasm-bindgen 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "b7ccc7b93cfd13e26700a9e2e41e6305f1951b87e166599069f77d10358100e6" -"checksum wasm-bindgen-backend 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "1953f91b1608eb1522513623c7739f047bb0fed4128ce51a93f08e12cc314645" -"checksum wasm-bindgen-futures 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "fa1af11c73eca3dc8c51c76ea475a4416e912da6402064a49fc6c0214701866d" -"checksum wasm-bindgen-macro 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "0f69da5696545d7ca6607a2e4b1a0edf5a6b36b2c49dbb0f1df6ad1d92884047" -"checksum wasm-bindgen-macro-support 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d4246f3bc73223bbb846f4f2430a60725826a96c9389adf715ed1d5af46dec6" -"checksum wasm-bindgen-shared 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "c08381e07e7a79e5e229ad7c60d15833d19033542cc5dd91d085df59d235f4a6" -"checksum wasm-bindgen-webidl 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "1f42ff7adb8102bf5ad8adbc45b1635c520c8175f9fdf6eb2c54479d485d435a" -"checksum wasm-timer 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad9ac33c834103916e373d648adf65f58c83fb3d8a0f3e6b9a64bca7253a4dca" +"checksum want 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b6395efa4784b027708f7451087e647ec73cc74f5d9bc2e418404248d679a230" +"checksum wasm-bindgen 0.2.47 (registry+https://github.com/rust-lang/crates.io-index)" = "22029998cc650473cb05f10f19c06a1536b9e1f1572e4f5dacd45ab4d3f85877" +"checksum wasm-bindgen-backend 0.2.47 (registry+https://github.com/rust-lang/crates.io-index)" = "6f858ff3cb4196c702e8c24b75fba1d3ab46958de4f7c253627f0507aae1507c" +"checksum wasm-bindgen-futures 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "cc16facd42fc3d0fa0cae78b39516bac04496cf80518fd09bbfa33e9b0e9c92d" +"checksum wasm-bindgen-macro 0.2.47 (registry+https://github.com/rust-lang/crates.io-index)" = "15c29f04eb117312931e7b02878453ee63d67a6f291797651890128bf5ee71db" +"checksum wasm-bindgen-macro-support 0.2.47 (registry+https://github.com/rust-lang/crates.io-index)" = "92b1356b623816248dfe0e2c4b7e113618d647808907ff6a3d9838ebee8e82ee" +"checksum wasm-bindgen-shared 0.2.47 (registry+https://github.com/rust-lang/crates.io-index)" = "15de16ddb30cfd424a87598b30021491bae1607d32e52056979865c98b7913b4" +"checksum wasm-bindgen-webidl 0.2.47 (registry+https://github.com/rust-lang/crates.io-index)" = "21724123084234fff2f986018b790afc5d6f45c9a3903025e6c55d0068cb7d15" +"checksum wasm-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3d6101df9a5987df809216bdda7289f52b58128e6b6a6546e9ee3e6b632b4921" "checksum wasmi 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "aebbaef470840d157a5c47c8c49f024da7b1b80e90ff729ca982b2b80447e78b" "checksum wasmi-validation 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab380192444b3e8522ae79c0a1976e42a82920916ccdfbce3def89f456ea33f3" -"checksum web-sys 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "540b8259eb242ff3a566fa0140bda03a4ece4e5c226e1284b5c95dddcd4341f6" +"checksum web-sys 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "22306ce642c58266cb5c5938150194911322bc179aa895146076217410ddbc82" "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" "checksum websocket 0.22.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0adcd2a64c5746c9702b354a1b992802b0c363df1dfa324a74cb7aebe10e0cbf" @@ -6084,8 +6369,5 @@ dependencies = [ "checksum xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" "checksum yaml-rust 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e66366e18dc58b46801afbf2ca7661a9f59cc8c5962c29892b6039b4f86fa992" "checksum yamux 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "01bd67889938c48f0049fc60a77341039e6c3eaf16cb7693e6ead7c0ba701295" -"checksum zeroize 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8ddfeb6eee2fb3b262ef6e0898a52b7563bb8e0d5955a313b3cf2f808246ea14" -"checksum zeroize 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e68403b858b6af538b11614e62dfe9ab2facba9f13a0cafb974855cfb495ec95" -"checksum zeroize 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b60a6c572b91d8ecb0a460950d84fe5b40699edd07d65f73789b31237afc8f66" -"checksum zeroize_derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3f07490820219949839d0027b965ffdd659d75be9220c00798762e36c6cd281" -"checksum zeroize_derive 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9dac4b660d969bff9c3fe1847a891cacaa8b21dd5f2aae6e0a3e0975aea96431" +"checksum zeroize 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4177936c03b5a349c1b8e4509c46add268e66bc66fe92663729fa0570fe4f213" +"checksum zeroize_derive 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "afd1469e4bbca3b96606d26ba6e9bd6d3aed3b1299c82b92ec94377d22d78dbc" diff --git a/Cargo.toml b/Cargo.toml index f45353204f05ba3ec1c0a339eeb75cc7dafcb2c0..a6a7b8d17ba35048f495e1344edca4a6d1cc21c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ members = [ "core/consensus/rhd", "core/consensus/slots", "core/executor", + "core/executor/runtime-test", "core/finality-grandpa", "core/finality-grandpa/primitives", "core/inherents", @@ -55,22 +56,28 @@ members = [ "core/transaction-pool", "core/transaction-pool/graph", "core/trie", - "core/util/fork-tree", + "core/utils/fork-tree", + "core/utils/wasm-builder", + "core/utils/wasm-builder-runner", "srml/support", "srml/support/procedural", "srml/support/procedural/tools", "srml/support/procedural/tools/derive", "srml/support/test", + "srml/authorship", "srml/assets", "srml/aura", "srml/balances", "srml/contracts", - "srml/council", + "srml/collective", "srml/democracy", + "srml/elections", "srml/example", "srml/executive", "srml/finality-tracker", + "srml/generic-asset", "srml/grandpa", + "srml/im-online", "srml/indices", "srml/metadata", "srml/session", @@ -88,11 +95,6 @@ members = [ "subkey", "test-utils/chain-spec-builder", ] -exclude = [ - "node/runtime/wasm", - "core/executor/wasm", - "core/test-runtime/wasm", -] [badges] travis-ci = { repository = "paritytech/substrate", branch = "master" } diff --git a/Dockerfile b/Dockerfile index c5944dbab6de9231c9fa61af735fcf48d2b5be6b..0271db8d1461d7a8c58fb52f9f6306e5ca833e2e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ WORKDIR /substrate COPY . /substrate RUN apt-get update && \ - apt-get upgrade -y && \ + apt-get dist-upgrade -y && \ apt-get install -y cmake pkg-config libssl-dev git clang RUN curl https://sh.rustup.rs -sSf | sh -s -- -y && \ @@ -20,9 +20,8 @@ RUN curl https://sh.rustup.rs -sSf | sh -s -- -y && \ rustup target add wasm32-unknown-unknown --toolchain nightly && \ cargo install --git https://github.com/alexcrichton/wasm-gc && \ rustup default nightly && \ - ./scripts/build.sh && \ rustup default stable && \ - cargo build --$PROFILE + cargo build "--$PROFILE" # ===== SECOND STAGE ====== diff --git a/README.adoc b/README.adoc index 826370ee6f5a0a7f03f178f80ca60cf5a37a6d79..89c042bb325b7e0dc61102ed63e67351a781571f 100644 --- a/README.adoc +++ b/README.adoc @@ -138,7 +138,7 @@ Now, edit `~/chainspec.json` in your editor. There are a lot of individual field [source, json] ---- "timestamp": { - "period": 10 + "minimumPeriod": 10 }, ---- @@ -264,7 +264,6 @@ Then build the code: [source, shell] ---- -./scripts/build.sh # Builds the WebAssembly binaries cargo build # Builds all native code ---- @@ -309,6 +308,24 @@ cargo run --release \-- \ Additional Substrate CLI usage options are available and may be shown by running `cargo run \-- --help`. +=== WASM binaries + +The WASM binaries are built during the normal `cargo build` process. To control the WASM binary building, +we support multiple environment variables: + +* `SKIP_WASM_BUILD` - Skips building any WASM binary. This is useful when only native should be recompiled. +* `BUILD_DUMMY_WASM_BINARY` - Builds dummy WASM binaries. These dummy binaries are empty and useful + for `cargo check` runs. +* `WASM_BUILD_TYPE` - Sets the build type for building WASM binaries. Supported values are `release` or `debug`. + By default the build type is equal to the build type used by the main build. +* `TRIGGER_WASM_BUILD` - Can be set to trigger a WASM build. On subsequent calls the value of the variable + needs to change. As WASM builder instructs `cargo` to watch for file changes + this environment variable should only be required in certain circumstances. + +Each project can be skipped individually by using the environment variable `SKIP_PROJECT_NAME_WASM_BUILD`. +Where `PROJECT_NAME` needs to be replaced by the name of the cargo project, e.g. `node-runtime` will +be `NODE_RUNTIME`. + [[flaming-fir]] === Joining the Flaming Fir Testnet diff --git a/core/basic-authorship/Cargo.toml b/core/basic-authorship/Cargo.toml index 547fca9030ee651ec0680d1caf01b39035bf4623..fa409f1b747d13472a38f28cbf7e701ab1ec6ae2 100644 --- a/core/basic-authorship/Cargo.toml +++ b/core/basic-authorship/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" [dependencies] log = "0.4" -codec = { package = "parity-codec", version = "3.2" } +codec = { package = "parity-codec", version = "4.1.1" } runtime_primitives = { package = "sr-primitives", path = "../../core/sr-primitives" } client = { package = "substrate-client", path = "../../core/client" } aura_primitives = { package = "substrate-consensus-aura-primitives", path = "../../core/consensus/aura/primitives" } diff --git a/core/cli/Cargo.toml b/core/cli/Cargo.toml index 6ca50ba5f8404277bb9e4377e199cd6c5a3ab813..bf583a3b7dc2e3b2fefb117f3a03d87c8791038f 100644 --- a/core/cli/Cargo.toml +++ b/core/cli/Cargo.toml @@ -18,6 +18,7 @@ lazy_static = "1.3" app_dirs = "1.2" tokio = "0.1.7" futures = "0.1.17" +futures03 = { package = "futures-preview", version = "0.3.0-alpha.17", features = ["compat"] } fdlimit = "0.1" exit-future = "0.1" serde_json = "1.0" diff --git a/core/cli/src/informant.rs b/core/cli/src/informant.rs index 3eb5a0d992249c3a8c37f60e99cba01f6ed12fb8..d6bbf4831d8dcf99567601387d49bab384e6b617 100644 --- a/core/cli/src/informant.rs +++ b/core/cli/src/informant.rs @@ -20,6 +20,7 @@ use ansi_term::Colour; use std::fmt; use std::time; use futures::{Future, Stream}; +use futures03::{StreamExt as _, TryStreamExt as _}; use service::{Service, Components}; use tokio::runtime::TaskExecutor; use network::SyncState; @@ -44,7 +45,7 @@ where C: Components { let mut last_number = None; let mut last_update = time::Instant::now(); - let display_notifications = service.network_status().for_each(move |net_status| { + let display_notifications = service.network_status().for_each(move |(net_status, _)| { let info = client.info(); let best_number = info.chain.best_number.saturated_into::(); @@ -81,7 +82,7 @@ where C: Components { Some((info.chain.best_number, info.chain.best_hash)) }; - let display_block_import = client.import_notification_stream().for_each(move |n| { + let display_block_import = client.import_notification_stream().map(|v| Ok::<_, ()>(v)).compat().for_each(move |n| { // detect and log reorganizations. if let Some((ref last_num, ref last_hash)) = last { if n.header.parent_hash() != last_hash { diff --git a/core/cli/src/lib.rs b/core/cli/src/lib.rs index c14c9625917e51d9a6b487668b634151d38429c8..cc31af184ed9ce1288708243f82687de34f94b65 100644 --- a/core/cli/src/lib.rs +++ b/core/cli/src/lib.rs @@ -32,8 +32,7 @@ use service::{ }; use network::{ self, multiaddr::Protocol, - config::{NetworkConfiguration, NonReservedPeerMode, NodeKeyConfig}, - build_multiaddr, + config::{NetworkConfiguration, TransportConfig, NonReservedPeerMode, NodeKeyConfig, build_multiaddr}, }; use primitives::H256; @@ -250,16 +249,16 @@ where 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::Secret::File) - .unwrap_or(network::Secret::New))) + .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 .or_else(|| net_config_file(net_config_dir, NODE_KEY_ED25519_FILE)) - .map(network::Secret::File) - .unwrap_or(network::Secret::New))) + .map(network::config::Secret::File) + .unwrap_or(network::config::Secret::New))) .map(NodeKeyConfig::Ed25519) } } @@ -277,18 +276,18 @@ fn invalid_node_key(e: impl std::fmt::Display) -> error::Error { } /// Parse a Secp256k1 secret key from a hex string into a `network::Secret`. -fn parse_secp256k1_secret(hex: &String) -> error::Result { +fn parse_secp256k1_secret(hex: &String) -> error::Result { H256::from_str(hex).map_err(invalid_node_key).and_then(|bytes| - network::identity::secp256k1::SecretKey::from_bytes(bytes) - .map(network::Secret::Input) + 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 { +fn parse_ed25519_secret(hex: &String) -> error::Result { H256::from_str(&hex).map_err(invalid_node_key).and_then(|bytes| - network::identity::ed25519::SecretKey::from_bytes(bytes) - .map(network::Secret::Input) + network::config::identity::ed25519::SecretKey::from_bytes(bytes) + .map(network::config::Secret::Input) .map_err(invalid_node_key)) } @@ -354,7 +353,10 @@ fn fill_network_configuration( config.in_peers = cli.in_peers; config.out_peers = cli.out_peers; - config.enable_mdns = !is_dev && !cli.no_mdns; + config.transport = TransportConfig::Normal { + enable_mdns: !is_dev && !cli.no_mdns, + wasm_external_transport: None, + }; Ok(()) } @@ -374,7 +376,7 @@ where let spec = load_spec(&cli.shared_params, spec_factory)?; let mut config = service::Configuration::default_with_spec(spec.clone()); if cli.interactive_password { - config.password = input_keystore_password()? + config.password = input_keystore_password()?.into() } config.impl_name = impl_name; @@ -399,13 +401,9 @@ 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())) - .to_string_lossy() - .into(); + config.keystore_path = cli.keystore_path.or_else(|| Some(keystore_path(&base_path, config.chain_spec.id()))); - config.database_path = - db_path(&base_path, config.chain_spec.id()).to_string_lossy().into(); + 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 { @@ -426,12 +424,13 @@ where }; let exec = cli.execution_strategies; + let exec_all_or = |strat: params::ExecutionStrategy| exec.execution.unwrap_or(strat).into(); config.execution_strategies = ExecutionStrategies { - syncing: exec.syncing_execution.into(), - importing: exec.importing_execution.into(), - block_construction: exec.block_construction_execution.into(), - offchain_worker: exec.offchain_worker_execution.into(), - other: exec.other_execution.into(), + syncing: exec_all_or(exec.execution_syncing), + importing: exec_all_or(exec.execution_import_block), + block_construction: exec_all_or(exec.execution_block_construction), + offchain_worker: exec_all_or(exec.execution_offchain_worker), + other: exec_all_or(exec.execution_other), }; config.offchain_worker = match (cli.offchain_worker, role) { @@ -443,6 +442,8 @@ where config.roles = role; config.disable_grandpa = cli.no_grandpa; + config.grandpa_voter = cli.grandpa_voter; + let is_dev = cli.shared_params.dev; @@ -594,7 +595,7 @@ where 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()).to_string_lossy().into(); + config.database_path = db_path(&base_path, spec.id()); Ok(config) } @@ -612,7 +613,7 @@ where { let config = create_config_with_db_path::(spec_factory, &cli.shared_params, version)?; - info!("DB path: {}", config.database_path); + info!("DB path: {}", config.database_path.display()); let from = cli.from.unwrap_or(1); let to = cli.to; let json = cli.json; @@ -645,7 +646,9 @@ where None => Box::new(stdin()), }; - service::chain_ops::import_blocks::(config, exit.into_exit(), file).map_err(Into::into) + let fut = service::chain_ops::import_blocks::(config, exit.into_exit(), file)?; + tokio::run(fut); + Ok(()) } fn revert_chain( @@ -809,7 +812,7 @@ fn kill_color(s: &str) -> String { mod tests { use super::*; use tempdir::TempDir; - use network::identity::{secp256k1, ed25519}; + use network::config::identity::{secp256k1, ed25519}; #[test] fn tests_node_name_good() { @@ -841,10 +844,10 @@ mod tests { node_key_file: None }; node_key_config(params, &net_config_dir).and_then(|c| match c { - NodeKeyConfig::Secp256k1(network::Secret::Input(ref ski)) + NodeKeyConfig::Secp256k1(network::config::Secret::Input(ref ski)) if node_key_type == NodeKeyType::Secp256k1 && &sk[..] == ski.to_bytes() => Ok(()), - NodeKeyConfig::Ed25519(network::Secret::Input(ref ski)) + NodeKeyConfig::Ed25519(network::config::Secret::Input(ref ski)) if node_key_type == NodeKeyType::Ed25519 && &sk[..] == ski.as_ref() => Ok(()), _ => Err(error::Error::Input("Unexpected node key config".into())) @@ -869,9 +872,9 @@ mod tests { node_key_file: Some(file.clone()) }; node_key_config(params, &net_config_dir).and_then(|c| match c { - NodeKeyConfig::Secp256k1(network::Secret::File(ref f)) + NodeKeyConfig::Secp256k1(network::config::Secret::File(ref f)) if node_key_type == NodeKeyType::Secp256k1 && f == &file => Ok(()), - NodeKeyConfig::Ed25519(network::Secret::File(ref f)) + 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())) }) @@ -903,9 +906,9 @@ mod tests { let typ = params.node_key_type; node_key_config::(params, &None) .and_then(|c| match c { - NodeKeyConfig::Secp256k1(network::Secret::New) + NodeKeyConfig::Secp256k1(network::config::Secret::New) if typ == NodeKeyType::Secp256k1 => Ok(()), - NodeKeyConfig::Ed25519(network::Secret::New) + NodeKeyConfig::Ed25519(network::config::Secret::New) if typ == NodeKeyType::Ed25519 => Ok(()), _ => Err(error::Error::Input("Unexpected node key config".into())) }) @@ -918,10 +921,10 @@ 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::Secret::File(ref f)) + NodeKeyConfig::Secp256k1(network::config::Secret::File(ref f)) if typ == NodeKeyType::Secp256k1 && f == &dir.join(NODE_KEY_SECP256K1_FILE) => Ok(()), - NodeKeyConfig::Ed25519(network::Secret::File(ref f)) + NodeKeyConfig::Ed25519(network::config::Secret::File(ref f)) if typ == NodeKeyType::Ed25519 && f == &dir.join(NODE_KEY_ED25519_FILE) => Ok(()), _ => Err(error::Error::Input("Unexpected node key config".into())) diff --git a/core/cli/src/params.rs b/core/cli/src/params.rs index 0cdc633188d7dc3ef8ee80b6bece823e1730b74f..78899ccd4cb010b6dc293fa0844738ecd48d23a1 100644 --- a/core/cli/src/params.rs +++ b/core/cli/src/params.rs @@ -33,13 +33,12 @@ macro_rules! impl_get_log_filter { arg_enum! { /// How to execute blocks - #[derive(Debug, Clone)] + #[derive(Debug, Clone, Copy)] pub enum ExecutionStrategy { Native, Wasm, Both, NativeElseWasm, - NativeWhenPossible, } } @@ -50,7 +49,6 @@ impl Into for ExecutionStrategy { ExecutionStrategy::Wasm => client::ExecutionStrategy::AlwaysWasm, ExecutionStrategy::Both => client::ExecutionStrategy::Both, ExecutionStrategy::NativeElseWasm => client::ExecutionStrategy::NativeElseWasm, - ExecutionStrategy::NativeWhenPossible => client::ExecutionStrategy::NativeWhenPossible, } } } @@ -227,7 +225,7 @@ pub struct TransactionPoolParams { pub struct ExecutionStrategies { /// The means of execution used when calling into the runtime while syncing blocks. #[structopt( - long = "syncing-execution", + long = "execution-syncing", value_name = "STRATEGY", raw( possible_values = "&ExecutionStrategy::variants()", @@ -235,11 +233,11 @@ pub struct ExecutionStrategies { default_value = r#""NativeElseWasm""# ) )] - pub syncing_execution: ExecutionStrategy, + pub execution_syncing: ExecutionStrategy, /// The means of execution used when calling into the runtime while importing blocks. #[structopt( - long = "importing-execution", + long = "execution-import-block", value_name = "STRATEGY", raw( possible_values = "&ExecutionStrategy::variants()", @@ -247,11 +245,11 @@ pub struct ExecutionStrategies { default_value = r#""NativeElseWasm""# ) )] - pub importing_execution: ExecutionStrategy, + pub execution_import_block: ExecutionStrategy, /// The means of execution used when calling into the runtime while constructing blocks. #[structopt( - long = "block-construction-execution", + long = "execution-block-construction", value_name = "STRATEGY", raw( possible_values = "&ExecutionStrategy::variants()", @@ -259,31 +257,49 @@ pub struct ExecutionStrategies { default_value = r#""Wasm""# ) )] - pub block_construction_execution: ExecutionStrategy, + pub execution_block_construction: ExecutionStrategy, /// The means of execution used when calling into the runtime while using an off-chain worker. #[structopt( - long = "offchain-worker-execution", + long = "execution-offchain-worker", value_name = "STRATEGY", raw( possible_values = "&ExecutionStrategy::variants()", case_insensitive = "true", - default_value = r#""NativeWhenPossible""# + default_value = r#""Native""# ) )] - pub offchain_worker_execution: ExecutionStrategy, + pub execution_offchain_worker: ExecutionStrategy, /// The means of execution used when calling into the runtime while not syncing, importing or constructing blocks. #[structopt( - long = "other-execution", + long = "execution-other", value_name = "STRATEGY", raw( possible_values = "&ExecutionStrategy::variants()", case_insensitive = "true", - default_value = r#""Wasm""# + default_value = r#""Native""# ) )] - pub other_execution: ExecutionStrategy, + pub execution_other: ExecutionStrategy, + + /// The execution strategy that should be used by all execution contexts. + #[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\", + ]" + ) + )] + pub execution: Option, } /// The `run` command used to run a node. @@ -305,6 +321,11 @@ pub struct RunCmd { #[structopt(long = "no-grandpa")] pub no_grandpa: bool, + /// Run GRANDPA voter even when no additional key seed via `--key` is specified. This can for example be of interest + /// when running a sentry node in front of a validator, thus needing to forward GRANDPA gossip messages. + #[structopt(long = "grandpa-voter")] + pub grandpa_voter: bool, + /// Experimental: Run in light client mode #[structopt(long = "light")] pub light: bool, @@ -421,7 +442,7 @@ lazy_static::lazy_static! { let conflicts_with = keyring::AuthorityKeyring::iter() .filter(|b| a != *b) .map(|b| b.to_string().to_lowercase()) - .chain(["name", "key"].iter().map(|s| s.to_string())) + .chain(["name", "key"].iter().map(ToString::to_string)) .collect::>(); let name = a.to_string().to_lowercase(); diff --git a/core/client/Cargo.toml b/core/client/Cargo.toml index 510ec50b8b5c1bb527460cadfa0059d70b2e6cc7..f140b2f343f740721f2c427986ad4e6c24713e08 100644 --- a/core/client/Cargo.toml +++ b/core/client/Cargo.toml @@ -9,17 +9,17 @@ derive_more = { version = "0.14.0", optional = true } fnv = { version = "1.0", optional = true } log = { version = "0.4", optional = true } parking_lot = { version = "0.8.0", optional = true } -hex = { package = "hex-literal", version = "0.1", optional = true } -futures = { version = "0.1.17", optional = true } +hex = { package = "hex-literal", version = "0.2", optional = true } +futures-preview = { version = "0.3.0-alpha.17", optional = true } consensus = { package = "substrate-consensus-common", path = "../consensus/common", optional = true } executor = { package = "substrate-executor", path = "../executor", optional = true } state-machine = { package = "substrate-state-machine", path = "../state-machine", optional = true } keyring = { package = "substrate-keyring", path = "../keyring", optional = true } trie = { package = "substrate-trie", path = "../trie", optional = true } substrate-telemetry = { path = "../telemetry", optional = true } -hash-db = { version = "0.12.2", default-features = false } +hash-db = { version = "0.14.0", default-features = false } kvdb = { git = "https://github.com/paritytech/parity-common", optional = true, rev="b0317f649ab2c665b7987b8475878fc4d2e1f81d" } -parity-codec = { version = "3.3", default-features = false, features = ["derive"] } +parity-codec = { version = "4.1.1", default-features = false, features = ["derive"] } primitives = { package = "substrate-primitives", path = "../primitives", default-features = false } runtime-primitives = { package = "sr-primitives", path = "../sr-primitives", default-features = false } runtime-version = { package = "sr-version", path = "../sr-version", default-features = false } @@ -47,7 +47,7 @@ std = [ "fnv", "log", "hex", - "futures", + "futures-preview", "executor", "state-machine", "keyring", diff --git a/core/client/db/Cargo.toml b/core/client/db/Cargo.toml index 1ae42d6a8975c55cfcfd7fdddab1cc3a64f7aa78..6afe77a15655efe3feedbe25106bddc111603a06 100644 --- a/core/client/db/Cargo.toml +++ b/core/client/db/Cargo.toml @@ -12,12 +12,12 @@ kvdb = { git = "https://github.com/paritytech/parity-common", rev="b0317f649ab2c kvdb-rocksdb = { git = "https://github.com/paritytech/parity-common", rev="b0317f649ab2c665b7987b8475878fc4d2e1f81d", optional = true } kvdb-memorydb = { git = "https://github.com/paritytech/parity-common", rev="b0317f649ab2c665b7987b8475878fc4d2e1f81d" } linked-hash-map = "0.5" -hash-db = { version = "0.12" } +hash-db = { version = "0.14.0" } primitives = { package = "substrate-primitives", path = "../../primitives" } runtime_primitives = { package = "sr-primitives", path = "../../sr-primitives" } client = { package = "substrate-client", path = "../../client" } state-machine = { package = "substrate-state-machine", path = "../../state-machine" } -parity-codec = { version = "3.3", features = ["derive"] } +parity-codec = { version = "4.1.1", features = ["derive"] } executor = { package = "substrate-executor", path = "../../executor" } state_db = { package = "substrate-state-db", path = "../../state-db" } trie = { package = "substrate-trie", path = "../../trie" } diff --git a/core/client/db/src/lib.rs b/core/client/db/src/lib.rs index 4fbe22ac4b097ddcddb427dd39a82db4ba9d8af7..b376be116cab5119b65a2d675b1dc1c4a7f33667 100644 --- a/core/client/db/src/lib.rs +++ b/core/client/db/src/lib.rs @@ -24,7 +24,10 @@ //! //! Finality implies canonicality but not vice-versa. +#![warn(missing_docs)] + pub mod light; +pub mod offchain; mod cache; mod changes_tries_storage; @@ -78,6 +81,10 @@ 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>; +/// A reference tracking state. +/// +/// It makes sure that the hash we are using stays pinned in storage +/// until this structure is dropped. pub struct RefTrackingState { state: DbState, storage: Arc>, @@ -203,7 +210,7 @@ pub fn new_client( Ok(client::Client::new(backend, executor, genesis_storage, execution_strategies)?) } -mod columns { +pub(crate) mod columns { pub const META: Option = crate::utils::COLUMN_META; pub const STATE: Option = Some(1); pub const STATE_META: Option = Some(2); @@ -214,7 +221,9 @@ mod columns { pub const JUSTIFICATION: Option = Some(6); pub const CHANGES_TRIE: Option = Some(7); pub const AUX: Option = Some(8); - pub const CACHE: Option = Some(9); + /// Offchain workers local storage + pub const OFFCHAIN: Option = Some(9); + pub const CACHE: Option = Some(10); } struct PendingBlock { @@ -549,6 +558,7 @@ impl state_machine::Storage for DbGenesisStorage { /// Otherwise, trie nodes are kept only from some recent blocks. pub struct Backend { storage: Arc>, + offchain_storage: offchain::LocalStorage, changes_tries_storage: DbChangesTrieStorage, blockchain: BlockchainDb, canonicalization_delay: u64, @@ -560,31 +570,29 @@ impl> Backend { /// Create a new instance of database backend. /// /// The pruning window is how old a block must be before the state is pruned. - pub fn new(config: DatabaseSettings, canonicalization_delay: u64) -> Result { + pub fn new(config: DatabaseSettings, canonicalization_delay: u64) -> client::error::Result { Self::new_inner(config, canonicalization_delay) } - #[cfg(feature = "kvdb-rocksdb")] fn new_inner(config: DatabaseSettings, canonicalization_delay: u64) -> Result { + #[cfg(feature = "kvdb-rocksdb")] let db = crate::utils::open_database(&config, columns::META, "full")?; - Backend::from_kvdb(db as Arc<_>, canonicalization_delay, &config) - } - - #[cfg(not(feature = "kvdb-rocksdb"))] - fn new_inner(config: DatabaseSettings, canonicalization_delay: u64) -> Result { - log::warn!("Running without the RocksDB feature. The database will NOT be saved."); - let db = Arc::new(kvdb_memorydb::create(crate::utils::NUM_COLUMNS)); - Backend::from_kvdb(db as Arc<_>, canonicalization_delay, &config) + #[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) } + /// Create new memory-backed client backend for tests. #[cfg(any(test, feature = "test-helpers"))] pub fn new_test(keep_blocks: u32, canonicalization_delay: u64) -> Self { - use utils::NUM_COLUMNS; - - let db = Arc::new(::kvdb_memorydb::create(NUM_COLUMNS)); + 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 { @@ -595,7 +603,7 @@ impl> Backend { path: Default::default(), pruning: PruningMode::keep_blocks(keep_blocks), }; - Backend::from_kvdb( + Self::from_kvdb( db, canonicalization_delay, &db_setting, @@ -616,6 +624,7 @@ impl> Backend { db: db.clone(), state_db, }; + let offchain_storage = offchain::LocalStorage::new(db.clone()); let changes_tries_storage = DbChangesTrieStorage::new( db, columns::CHANGES_TRIE, @@ -628,6 +637,7 @@ impl> Backend { Ok(Backend { storage: Arc::new(storage_db), + offchain_storage, changes_tries_storage, blockchain, canonicalization_delay, @@ -725,7 +735,7 @@ impl> Backend { transaction, columns::KEY_LOOKUP, r.number - ); + )?; } // canonicalize: set the number lookup to map to this block's hash. @@ -736,18 +746,18 @@ impl> Backend { columns::KEY_LOOKUP, e.number, e.hash - ); + )?; } } - let lookup_key = utils::number_and_hash_to_lookup_key(best_to.0, &best_to.1); + let lookup_key = utils::number_and_hash_to_lookup_key(best_to.0, &best_to.1)?; transaction.put(columns::META, meta_keys::BEST_BLOCK, &lookup_key); utils::insert_number_to_key_mapping( transaction, columns::KEY_LOOKUP, best_to.0, best_to.1, - ); + )?; Ok((enacted, retracted)) } @@ -788,7 +798,7 @@ impl> Backend { if let Some(justification) = justification { transaction.put( columns::JUSTIFICATION, - &utils::number_and_hash_to_lookup_key(number, hash), + &utils::number_and_hash_to_lookup_key(number, hash)?, &justification.encode(), ); } @@ -863,7 +873,7 @@ impl> Backend { let number = pending_block.header.number().clone(); // blocks are keyed by number + hash. - let lookup_key = utils::number_and_hash_to_lookup_key(number, hash); + let lookup_key = utils::number_and_hash_to_lookup_key(number, hash)?; let (enacted, retracted) = if pending_block.leaf_state.is_best() { self.set_head_with_transaction(&mut transaction, parent_hash, (number, hash))? @@ -876,7 +886,7 @@ impl> Backend { columns::KEY_LOOKUP, number, hash, - ); + )?; transaction.put(columns::HEADER, &lookup_key, &pending_block.header.encode()); if let Some(body) = pending_block.body { @@ -971,6 +981,7 @@ impl> Backend { hash.clone(), (number.clone(), hash.clone()) )?; + meta_updates.push((hash, *number, true, false)); } else { return Err(client::error::Error::UnknownBlock(format!("Cannot set head {:?}", set_head))) } @@ -1031,7 +1042,7 @@ impl> Backend { if self.storage.state_db.best_canonical().map(|c| f_num.saturated_into::() > c).unwrap_or(true) { let parent_hash = f_header.parent_hash().clone(); - let lookup_key = utils::number_and_hash_to_lookup_key(f_num, f_hash.clone()); + let lookup_key = utils::number_and_hash_to_lookup_key(f_num, f_hash.clone())?; transaction.put(columns::META, meta_keys::FINALIZED_BLOCK, &lookup_key); let commit = self.storage.state_db.canonicalize_block(&f_hash) @@ -1096,6 +1107,7 @@ impl client::backend::Backend for Backend whe type BlockImportOperation = BlockImportOperation; type Blockchain = BlockchainDb; type State = CachingState, Block>; + type OffchainStorage = offchain::LocalStorage; fn begin_operation(&self) -> Result { let old_state = self.state_at(BlockId::Hash(Default::default()))?; @@ -1170,6 +1182,10 @@ impl client::backend::Backend for Backend whe Some(&self.changes_tries_storage) } + fn offchain_storage(&self) -> Option { + Some(self.offchain_storage.clone()) + } + fn revert(&self, n: NumberFor) -> Result, client::error::Error> { let mut best = self.blockchain.info().best_number; let finalized = self.blockchain.info().finalized_number; @@ -1192,7 +1208,7 @@ impl client::backend::Backend for Backend whe let hash = self.blockchain.hash(best)?.ok_or_else( || client::error::Error::UnknownBlock( format!("Error reverting to {}. Block hash not found.", best)))?; - let key = utils::number_and_hash_to_lookup_key(best.clone(), &hash); + let key = utils::number_and_hash_to_lookup_key(best.clone(), &hash)?; transaction.put(columns::META, meta_keys::BEST_BLOCK, &key); transaction.delete(columns::KEY_LOOKUP, removed.hash().as_ref()); children::remove_children(&mut transaction, columns::META, meta_keys::CHILDREN_PREFIX, hash); diff --git a/core/client/db/src/light.rs b/core/client/db/src/light.rs index 5fc2f5aadd4b644e13b5a1d7958ed7a2d11b08ea..67569b0cbbbc8b4c5a127b052b88068f1baa1c2f 100644 --- a/core/client/db/src/light.rs +++ b/core/client/db/src/light.rs @@ -83,6 +83,7 @@ impl LightStorage Self::from_kvdb(db as Arc<_>) } + /// Create new memory-backed `LightStorage` for tests. #[cfg(any(test, feature = "test-helpers"))] pub fn new_test() -> Self { use utils::NUM_COLUMNS; @@ -209,7 +210,7 @@ impl LightStorage { /// should be the parent of `best_to`. In the case where we set an existing block /// to be best, `route_to` should equal to `best_to`. fn set_head_with_transaction(&self, transaction: &mut DBTransaction, route_to: Block::Hash, best_to: (NumberFor, Block::Hash)) -> Result<(), client::error::Error> { - let lookup_key = utils::number_and_hash_to_lookup_key(best_to.0, &best_to.1); + let lookup_key = utils::number_and_hash_to_lookup_key(best_to.0, &best_to.1)?; // handle reorg. let meta = self.meta.read(); @@ -232,7 +233,7 @@ impl LightStorage { transaction, columns::KEY_LOOKUP, retracted.number - ); + )?; } for enacted in tree_route.enacted() { @@ -241,7 +242,7 @@ impl LightStorage { columns::KEY_LOOKUP, enacted.number, enacted.hash - ); + )?; } } @@ -251,7 +252,7 @@ impl LightStorage { columns::KEY_LOOKUP, best_to.0, best_to.1, - ); + )?; Ok(()) } @@ -272,7 +273,7 @@ impl LightStorage { ).into()) } - let lookup_key = utils::number_and_hash_to_lookup_key(header.number().clone(), hash); + let lookup_key = utils::number_and_hash_to_lookup_key(header.number().clone(), hash)?; transaction.put(columns::META, meta_keys::FINALIZED_BLOCK, &lookup_key); // build new CHT(s) if required @@ -291,7 +292,7 @@ impl LightStorage { )?; transaction.put( columns::CHT, - &cht_key(HEADER_CHT_PREFIX, new_cht_start), + &cht_key(HEADER_CHT_PREFIX, new_cht_start)?, new_header_cht_root.as_ref() ); @@ -309,7 +310,7 @@ impl LightStorage { )?; transaction.put( columns::CHT, - &cht_key(CHANGES_TRIE_CHT_PREFIX, new_cht_start), + &cht_key(CHANGES_TRIE_CHT_PREFIX, new_cht_start)?, new_changes_trie_cht_root.as_ref() ); } @@ -329,7 +330,7 @@ impl LightStorage { columns::KEY_LOOKUP, prune_block, hash - ); + )?; transaction.delete(columns::HEADER, &lookup_key); } prune_block += One::one(); @@ -356,7 +357,7 @@ impl LightStorage { let cht_number = cht::block_to_cht_number(cht_size, block).ok_or_else(no_cht_for_block)?; let cht_start = cht::start_number(cht_size, cht_number); - self.db.get(columns::CHT, &cht_key(cht_type, cht_start)).map_err(db_err)? + self.db.get(columns::CHT, &cht_key(cht_type, cht_start)?).map_err(db_err)? .ok_or_else(no_cht_for_block) .and_then(|hash| Block::Hash::decode(&mut &*hash).ok_or_else(no_cht_for_block)) } @@ -412,7 +413,7 @@ impl LightBlockchainStorage for LightStorage } // blocks are keyed by number + hash. - let lookup_key = utils::number_and_hash_to_lookup_key(number, &hash); + let lookup_key = utils::number_and_hash_to_lookup_key(number, &hash)?; if leaf_state.is_best() { self.set_head_with_transaction(&mut transaction, parent_hash, (number, hash))?; @@ -423,7 +424,7 @@ impl LightBlockchainStorage for LightStorage columns::KEY_LOOKUP, number, hash, - ); + )?; transaction.put(columns::HEADER, &lookup_key, &header.encode()); let is_genesis = number.is_zero(); @@ -495,6 +496,7 @@ impl LightBlockchainStorage for LightStorage let mut transaction = DBTransaction::new(); self.set_head_with_transaction(&mut transaction, hash.clone(), (number.clone(), hash.clone()))?; self.db.write(transaction).map_err(db_err)?; + self.update_meta(hash, header.number().clone(), true, false); Ok(()) } else { Err(ClientError::UnknownBlock(format!("Cannot set head {:?}", id))) @@ -559,10 +561,10 @@ impl LightBlockchainStorage for LightStorage } /// Build the key for inserting header-CHT at given block. -fn cht_key>(cht_type: u8, block: N) -> [u8; 5] { +fn cht_key>(cht_type: u8, block: N) -> ClientResult<[u8; 5]> { let mut key = [cht_type; 5]; - key[1..].copy_from_slice(&utils::number_index_key(block)); - key + key[1..].copy_from_slice(&utils::number_index_key(block)?); + Ok(key) } #[cfg(test)] diff --git a/core/client/db/src/offchain.rs b/core/client/db/src/offchain.rs new file mode 100644 index 0000000000000000000000000000000000000000..0640fb6c29bd686711e8da3a563a3b18b754de5a --- /dev/null +++ b/core/client/db/src/offchain.rs @@ -0,0 +1,148 @@ +// 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 . + +//! RocksDB-based offchain workers local storage. + +use std::{ + collections::HashMap, + sync::Arc, +}; + +use crate::columns; +use kvdb::KeyValueDB; +use parking_lot::Mutex; + +/// Offchain local storage +#[derive(Clone)] +pub struct LocalStorage { + db: Arc, + locks: Arc, Arc>>>>, +} + +impl std::fmt::Debug for LocalStorage { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.debug_struct("LocalStorage") + .finish() + } +} + +impl LocalStorage { + /// Create new offchain storage for tests (backed by memorydb) + #[cfg(any(test, feature = "test-helpers"))] + pub fn new_test() -> Self { + let db = Arc::new(::kvdb_memorydb::create(crate::utils::NUM_COLUMNS)); + Self::new(db as _) + } + + /// Create offchain local storage with given `KeyValueDB` backend. + pub fn new(db: Arc) -> Self { + Self { + db, + locks: Default::default(), + } + } +} + +impl client::backend::OffchainStorage for LocalStorage { + fn set(&mut self, prefix: &[u8], key: &[u8], value: &[u8]) { + let key: Vec = prefix.iter().chain(key).cloned().collect(); + let mut tx = self.db.transaction(); + tx.put(columns::OFFCHAIN, &key, value); + + if let Err(e) = self.db.write(tx) { + log::warn!("Error writing to the offchain DB: {:?}", e); + } + } + + fn get(&self, prefix: &[u8], key: &[u8]) -> Option> { + let key: Vec = prefix.iter().chain(key).cloned().collect(); + self.db.get(columns::OFFCHAIN, &key) + .ok() + .and_then(|x| x) + .map(|v| v.to_vec()) + } + + fn compare_and_set( + &mut self, + prefix: &[u8], + item_key: &[u8], + old_value: Option<&[u8]>, + new_value: &[u8], + ) -> bool { + let key: Vec = prefix.iter().chain(item_key).cloned().collect(); + let key_lock = { + let mut locks = self.locks.lock(); + locks.entry(key.clone()).or_default().clone() + }; + + let is_set; + { + let _key_guard = key_lock.lock(); + let val = self.db.get(columns::OFFCHAIN, &key) + .ok() + .and_then(|x| x); + is_set = val.as_ref().map(|x| &**x) == old_value; + + if is_set { + self.set(prefix, item_key, new_value) + } + } + + // clean the lock map if we're the only entry + let mut locks = self.locks.lock(); + { + drop(key_lock); + let key_lock = locks.get_mut(&key); + if let Some(_) = key_lock.and_then(Arc::get_mut) { + locks.remove(&key); + } + } + is_set + } +} + +#[cfg(test)] +mod tests { + use super::*; + use client::backend::OffchainStorage; + + #[test] + fn should_compare_and_set_and_clear_the_locks_map() { + let mut storage = LocalStorage::new_test(); + let prefix = b"prefix"; + let key = b"key"; + let value = b"value"; + + storage.set(prefix, key, value); + assert_eq!(storage.get(prefix, key), Some(value.to_vec())); + + assert_eq!(storage.compare_and_set(prefix, key, Some(value), b"asd"), true); + assert_eq!(storage.get(prefix, key), Some(b"asd".to_vec())); + assert!(storage.locks.lock().is_empty(), "Locks map should be empty!"); + } + + #[test] + fn should_compare_and_set_on_empty_field() { + let mut storage = LocalStorage::new_test(); + let prefix = b"prefix"; + let key = b"key"; + + assert_eq!(storage.compare_and_set(prefix, key, None, b"asd"), true); + assert_eq!(storage.get(prefix, key), Some(b"asd".to_vec())); + assert!(storage.locks.lock().is_empty(), "Locks map should be empty!"); + } + +} diff --git a/core/client/db/src/storage_cache.rs b/core/client/db/src/storage_cache.rs index 53e4594856f2091eab3253e9b4310a39a653be02..7df1472ece0387e0cbf22c079a05f9bb1afc10ae 100644 --- a/core/client/db/src/storage_cache.rs +++ b/core/client/db/src/storage_cache.rs @@ -21,7 +21,7 @@ use std::sync::Arc; use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard}; use linked_hash_map::{LinkedHashMap, Entry}; use hash_db::Hasher; -use runtime_primitives::traits::{Block, Header}; +use runtime_primitives::traits::{Block as BlockT, Header}; use state_machine::{backend::Backend as StateBackend, TrieBackend}; use log::trace; use super::{StorageCollection, ChildStorageCollection}; @@ -33,7 +33,7 @@ type ChildStorageKey = (Vec, Vec); type StorageValue = Vec; /// Shared canonical state cache. -pub struct Cache { +pub struct Cache { /// Storage cache. `None` indicates that key is known to be missing. lru_storage: LRUMap>, /// Storage hashes cache. `None` indicates that key is known to be missing. @@ -144,7 +144,7 @@ impl LRUMap { } -impl Cache { +impl Cache { /// Returns the used memory size of the storage cache in bytes. pub fn used_storage_cache_size(&self) -> usize { self.lru_storage.used_size() @@ -159,7 +159,7 @@ pub type SharedCache = Arc>>; const FIX_LRU_HASH_SIZE: usize = 65_536; /// Create a new shared cache instance with given max memory usage. -pub fn new_shared_cache( +pub fn new_shared_cache( shared_cache_size: usize, child_ratio: (usize, usize), ) -> SharedCache { @@ -202,7 +202,7 @@ struct LocalCache { } /// Cache changes. -pub struct CacheChanges { +pub struct CacheChanges { /// Shared canonical state cache. shared_cache: SharedCache, /// Local cache of values for this state. @@ -219,14 +219,14 @@ pub struct CacheChanges { /// For canonical instances local cache is accumulated and applied /// in `sync_cache` along with the change overlay. /// For non-canonical clones local cache and changes are dropped. -pub struct CachingState, B: Block> { +pub struct CachingState, B: BlockT> { /// Backing state. state: S, /// Cache data. pub cache: CacheChanges } -impl CacheChanges { +impl CacheChanges { /// Propagate local cache into the shared cache and synchronize /// the shared cache with the best block state. /// This function updates the shared cache by removing entries @@ -374,7 +374,7 @@ impl CacheChanges { } -impl, B: Block> CachingState { +impl, B: BlockT> CachingState { /// Create a new instance wrapping generic State and shared cache. pub fn new(state: S, shared_cache: SharedCache, parent_hash: Option) -> CachingState { CachingState { @@ -447,7 +447,7 @@ impl, B: Block> CachingState { } } -impl, B:Block> StateBackend for CachingState { +impl, B: BlockT> StateBackend for CachingState { type Error = S::Error; type Transaction = S::Transaction; type TrieBackendStorage = S::TrieBackendStorage; diff --git a/core/client/db/src/utils.rs b/core/client/db/src/utils.rs index 0179165a11851cfea71a220750c77ee8a1cc7897..13ec9bfb3b8b6b0a29102304e5ce5897ade66eba 100644 --- a/core/client/db/src/utils.rs +++ b/core/client/db/src/utils.rs @@ -17,7 +17,9 @@ //! Db-based backend utility structures and functions, used by both //! full and light storages. -use std::{io, convert::TryInto, sync::Arc}; +#[cfg(feature = "kvdb-rocksdb")] +use std::sync::Arc; +use std::{io, convert::TryInto}; use kvdb::{KeyValueDB, DBTransaction}; #[cfg(feature = "kvdb-rocksdb")] @@ -29,14 +31,15 @@ use parity_codec::Decode; use trie::DBValue; use runtime_primitives::generic::BlockId; use runtime_primitives::traits::{ - Block as BlockT, Header as HeaderT, Zero, UniqueSaturatedFrom, - UniqueSaturatedInto, CheckedConversion + Block as BlockT, Header as HeaderT, Zero, + UniqueSaturatedFrom, UniqueSaturatedInto, }; +#[cfg(feature = "kvdb-rocksdb")] use crate::DatabaseSettings; /// 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. -pub const NUM_COLUMNS: u32 = 10; +pub const NUM_COLUMNS: u32 = 11; /// Meta column. The set of keys in the column is shared by full && light storages. pub const COLUMN_META: Option = Some(0); @@ -81,25 +84,31 @@ pub type NumberIndexKey = [u8; 4]; /// /// In the current database schema, this kind of key is only used for /// lookups into an index, NOT for storing header data or others. -pub fn number_index_key>(n: N) -> NumberIndexKey { - let n = n.checked_into::().unwrap(); - [ +pub fn number_index_key>(n: N) -> client::error::Result { + let n = n.try_into().map_err(|_| + client::error::Error::Backend("Block number cannot be converted to u32".into()) + )?; + + Ok([ (n >> 24) as u8, ((n >> 16) & 0xff) as u8, ((n >> 8) & 0xff) as u8, (n & 0xff) as u8 - ] + ]) } /// Convert number and hash into long lookup key for blocks that are /// not in the canonical chain. -pub fn number_and_hash_to_lookup_key(number: N, hash: H) -> Vec where +pub fn number_and_hash_to_lookup_key( + number: N, + hash: H, +) -> client::error::Result> where N: TryInto, - H: AsRef<[u8]> + H: AsRef<[u8]>, { - let mut lookup_key = number_index_key(number).to_vec(); + let mut lookup_key = number_index_key(number)?.to_vec(); lookup_key.extend_from_slice(hash.as_ref()); - lookup_key + Ok(lookup_key) } /// Convert block lookup key into block number. @@ -121,8 +130,9 @@ pub fn remove_number_to_key_mapping>( transaction: &mut DBTransaction, key_lookup_col: Option, number: N, -) { - transaction.delete(key_lookup_col, number_index_key(number).as_ref()) +) -> client::error::Result<()> { + transaction.delete(key_lookup_col, number_index_key(number)?.as_ref()); + Ok(()) } /// Remove key mappings. @@ -131,9 +141,10 @@ pub fn remove_key_mappings, H: AsRef<[u8]>>( key_lookup_col: Option, number: N, hash: H, -) { - remove_number_to_key_mapping(transaction, key_lookup_col, number); +) -> client::error::Result<()> { + remove_number_to_key_mapping(transaction, key_lookup_col, number)?; transaction.delete(key_lookup_col, hash.as_ref()); + Ok(()) } /// Place a number mapping into the database. This maps number to current perceived @@ -143,12 +154,13 @@ pub fn insert_number_to_key_mapping + Clone, H: AsRef<[u8]>>( key_lookup_col: Option, number: N, hash: H, -) { +) -> client::error::Result<()> { transaction.put_vec( key_lookup_col, - number_index_key(number.clone()).as_ref(), - number_and_hash_to_lookup_key(number, hash), - ) + number_index_key(number.clone())?.as_ref(), + number_and_hash_to_lookup_key(number, hash)?, + ); + Ok(()) } /// Insert a hash to key mapping in the database. @@ -157,12 +169,13 @@ pub fn insert_hash_to_key_mapping, H: AsRef<[u8]> + Clone>( key_lookup_col: Option, number: N, hash: H, -) { +) -> client::error::Result<()> { transaction.put_vec( key_lookup_col, hash.clone().as_ref(), - number_and_hash_to_lookup_key(number, hash), - ) + number_and_hash_to_lookup_key(number, hash)?, + ); + Ok(()) } /// Convert block id to block lookup key. @@ -179,7 +192,7 @@ pub fn block_id_to_lookup_key( let res = match id { BlockId::Number(n) => db.get( key_lookup_col, - number_index_key(n).as_ref(), + number_index_key(n)?.as_ref(), ), BlockId::Hash(h) => db.get(key_lookup_col, h.as_ref()), }; @@ -315,3 +328,19 @@ pub fn read_meta(db: &dyn KeyValueDB, col_meta: Option, col_header: genesis_hash, }) } + +#[cfg(test)] +mod tests { + use super::*; + use runtime_primitives::testing::{Block as RawBlock, ExtrinsicWrapper}; + type Block = RawBlock>; + + #[test] + fn number_index_key_doesnt_panic() { + let id = BlockId::::Number(72340207214430721); + match id { + BlockId::Number(n) => number_index_key(n).expect_err("number should overflow u32"), + _ => unreachable!(), + }; + } +} diff --git a/core/client/src/backend.rs b/core/client/src/backend.rs index f2e79ad57ed62c8b49974234d4af1e0d62805d6e..6b86a3fd55fe9ff13343d6a790b34236724cefa5 100644 --- a/core/client/src/backend.rs +++ b/core/client/src/backend.rs @@ -138,6 +138,8 @@ pub trait Backend: AuxStore + Send + Sync where type Blockchain: crate::blockchain::Backend; /// Associated state backend type. type State: StateBackend; + /// Offchain workers local storage. + type OffchainStorage: OffchainStorage; /// Begin a new block insertion transaction with given parent block id. /// When constructing the genesis, this is called with all-zero hash. @@ -155,6 +157,8 @@ pub trait Backend: AuxStore + Send + Sync where fn used_state_cache_size(&self) -> Option; /// Returns reference to changes trie storage. fn changes_trie_storage(&self) -> Option<&dyn PrunableStateChangesTrieStorage>; + /// Returns a handle to offchain storage. + fn offchain_storage(&self) -> Option; /// Returns true if state for given block is available. fn have_state_at(&self, hash: &Block::Hash, _number: NumberFor) -> bool { self.state_at(BlockId::Hash(hash.clone())).is_ok() @@ -193,6 +197,26 @@ pub trait Backend: AuxStore + Send + Sync where fn get_import_lock(&self) -> &Mutex<()>; } +/// Offchain workers local storage. +pub trait OffchainStorage: Clone + Send + Sync { + /// Persist a value in storage under given key and prefix. + fn set(&mut self, prefix: &[u8], key: &[u8], value: &[u8]); + + /// Retrieve a value from storage under given key and prefix. + fn get(&self, prefix: &[u8], key: &[u8]) -> Option>; + + /// Replace the value in storage if given old_value matches the current one. + /// + /// Returns `true` if the value has been set and false otherwise. + fn compare_and_set( + &mut self, + prefix: &[u8], + key: &[u8], + old_value: Option<&[u8]>, + new_value: &[u8], + ) -> bool; +} + /// Changes trie storage that supports pruning. pub trait PrunableStateChangesTrieStorage: StateChangesTrieStorage> diff --git a/core/client/src/blockchain.rs b/core/client/src/blockchain.rs index 3baf89d24c1dc91572b425ec9864fe3344ac1e29..0fbcfc2323f2323d53537a42b3d9f79a8b6f25a0 100644 --- a/core/client/src/blockchain.rs +++ b/core/client/src/blockchain.rs @@ -84,7 +84,7 @@ pub trait Backend: HeaderBackend { /// Returns hashes of all blocks that are leaves of the block tree. /// in other words, that have no children, are chain heads. - /// Results must be ordered best (longest, heighest) chain first. + /// Results must be ordered best (longest, highest) chain first. fn leaves(&self) -> Result>; /// Return hashes of all blocks that are children of the block with `parent_hash`. diff --git a/core/client/src/call_executor.rs b/core/client/src/call_executor.rs index d36c48d8155051183b448fcf283e7c87857a11f4..5ea8c142dea3b5b6e31e4a95edbd89af059459d2 100644 --- a/core/client/src/call_executor.rs +++ b/core/client/src/call_executor.rs @@ -110,7 +110,7 @@ where manager: ExecutionManager, native_call: Option, side_effects_handler: Option<&mut O>, - ) -> Result<(NativeOrEncoded, S::Transaction, Option>), error::Error>; + ) -> Result<(NativeOrEncoded, (S::Transaction, H::Out), Option>), error::Error>; /// Execute a call to a contract on top of given state, gathering execution proof. /// @@ -317,7 +317,11 @@ where manager: ExecutionManager, native_call: Option, side_effects_handler: Option<&mut O>, - ) -> error::Result<(NativeOrEncoded, S::Transaction, Option>)> { + ) -> error::Result<( + NativeOrEncoded, + (S::Transaction, ::Out), + Option>, + )> { let changes_trie_state = match self.backend.changes_trie_storage() { Some(changes_trie_storage) => backend::changes_tries_state_at_state::<_, Block, _>( state, diff --git a/core/client/src/client.rs b/core/client/src/client.rs index 8456a18b4258effaafa69660dead7a17287591d3..2be6b328e9515574db1796a5baa0b4d23c2eaffb 100644 --- a/core/client/src/client.rs +++ b/core/client/src/client.rs @@ -21,7 +21,7 @@ use std::{ panic::UnwindSafe, result, cell::RefCell, rc::Rc, }; use crate::error::Error; -use futures::sync::mpsc; +use futures::channel::mpsc; use parking_lot::{Mutex, RwLock}; use primitives::NativeOrEncoded; use runtime_primitives::{ @@ -29,7 +29,7 @@ use runtime_primitives::{ generic::{BlockId, SignedBlock}, }; use consensus::{ - Error as ConsensusError, ImportBlock, + Error as ConsensusError, BlockImportParams, ImportResult, BlockOrigin, ForkChoiceStrategy, SelectChain, self, }; @@ -828,12 +828,12 @@ impl Client where pub fn apply_block( &self, operation: &mut ClientImportOperation, - import_block: ImportBlock, + import_block: BlockImportParams, new_cache: HashMap>, ) -> error::Result where E: CallExecutor + Send + Sync + Clone, { - let ImportBlock { + let BlockImportParams { origin, header, justification, @@ -1038,7 +1038,11 @@ impl Client where let (top, children) = overlay.into_committed(); let children = children.map(|(sk, it)| (sk, it.collect())).collect(); - Ok((Some(storage_update), Some(changes_update), Some((top.collect(), children)))) + if import_headers.post().state_root() != &storage_update.1 { + return Err(error::Error::InvalidStateRoot); + } + + Ok((Some(storage_update.0), Some(changes_update), Some((top.collect(), children)))) }, None => Ok((None, None, None)) } @@ -1082,8 +1086,13 @@ impl Client where // if the block is not a direct ancestor of the current best chain, // then some other block is the common ancestor. if route_from_best.common_block().hash != block { - // FIXME: #1442 reorganize best block to be the best chain containing - // `block`. + // NOTE: we're setting the finalized block as best block, this might + // be slightly innacurate 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` + // instead. + operation.op.mark_head(BlockId::Hash(block))?; } let enacted = route_from_finalized.enacted(); @@ -1195,6 +1204,11 @@ impl Client where /// Mark all blocks up to given as finalized in operation. If a /// justification is provided it is stored with the given finalized /// block (any other finalized blocks are left unjustified). + /// + /// If the block being finalized is on a different fork from the current + /// best block the finalized block is set as best, this might be slightly + /// innacurate (i.e. outdated), usages that require determining an accurate + /// best block should use `SelectChain` instead of the client. pub fn apply_finality( &self, operation: &mut ClientImportOperation, @@ -1210,6 +1224,11 @@ impl Client where /// Finalize a block. This will implicitly finalize all blocks up to it and /// fire finality notifications. /// + /// If the block being finalized is on a different fork from the current + /// best block the finalized block is set as best, this might be slightly + /// innacurate (i.e. outdated), usages that require determining an accurate + /// best block should use `SelectChain` instead of the client. + /// /// Pass a flag to indicate whether finality notifications should be propagated. /// This is usually tied to some synchronization state, where we don't send notifications /// while performing major synchronization work. @@ -1437,7 +1456,7 @@ impl CallRuntimeAt for Client where } } -impl consensus::BlockImport for Client where +impl<'a, B, E, Block, RA> consensus::BlockImport for &'a Client where B: backend::Backend, E: CallExecutor + Clone + Send + Sync, Block: BlockT, @@ -1445,10 +1464,10 @@ impl consensus::BlockImport for Client type Error = ConsensusError; /// Import a checked and validated block. If a justification is provided in - /// `ImportBlock` then `finalized` *must* be true. + /// `BlockImportParams` then `finalized` *must* be true. fn import_block( - &self, - import_block: ImportBlock, + &mut self, + import_block: BlockImportParams, new_cache: HashMap>, ) -> Result { self.lock_import_and_run(|operation| { @@ -1458,7 +1477,7 @@ impl consensus::BlockImport for Client /// Check block preconditions. fn check_block( - &self, + &mut self, hash: Block::Hash, parent_hash: Block::Hash, ) -> Result { @@ -1482,6 +1501,30 @@ impl consensus::BlockImport for Client } } +impl consensus::BlockImport for Client where + B: backend::Backend, + E: CallExecutor + Clone + Send + Sync, + Block: BlockT, +{ + type Error = ConsensusError; + + fn import_block( + &mut self, + import_block: BlockImportParams, + new_cache: HashMap>, + ) -> Result { + (&*self).import_block(import_block, new_cache) + } + + fn check_block( + &mut self, + hash: Block::Hash, + parent_hash: Block::Hash, + ) -> Result { + (&*self).check_block(hash, parent_hash) + } +} + impl CurrentHeight for Client where B: backend::Backend, E: CallExecutor, @@ -1565,9 +1608,12 @@ where } fn best_block_header(&self) -> error::Result<::Header> { - let info : ChainInfo = self.backend.blockchain().info(); - Ok(self.backend.blockchain().header(BlockId::Hash(info.best_hash))? - .expect("Best block header must always exist")) + let info = self.backend.blockchain().info(); + let best_hash = self.best_containing(info.best_hash, None)? + .unwrap_or(info.best_hash); + + Ok(self.backend.blockchain().header(BlockId::Hash(best_hash))? + .expect("given block hash was fetched from block in db; qed")) } /// Get the most recent block hash of the best (longest) chains @@ -1601,7 +1647,7 @@ where } } - let (leaves, best_already_checked) = { + let leaves = { // ensure no blocks are imported during this code block. // an import could trigger a reorg which could change the canonical chain. // we depend on the canonical chain staying the same during this code block. @@ -1613,29 +1659,24 @@ where .ok_or_else(|| error::Error::from(format!("failed to get hash for block number {}", target_header.number())))?; if canon_hash == target_hash { - // if no block at the given max depth exists fallback to the best block + // if a `max_number` is given we try to fetch the block at the + // given depth, if it doesn't exist or `max_number` is not + // provided, we continue to search from all leaves below. if let Some(max_number) = maybe_max_number { if let Some(header) = self.backend.blockchain().hash(max_number)? { return Ok(Some(header)); } } - - return Ok(Some(info.best_hash)); } else if info.finalized_number >= *target_header.number() { // header is on a dead fork. return Ok(None); } - (self.backend.blockchain().leaves()?, info.best_hash) + self.backend.blockchain().leaves()? }; // for each chain. longest chain first. shortest last for leaf_hash in leaves { - // ignore canonical chain which we already checked above - if leaf_hash == best_already_checked { - continue; - } - // start at the leaf let mut current_hash = leaf_hash; @@ -1762,6 +1803,53 @@ impl backend::AuxStore for Client crate::backend::AuxStore::get_aux(&*self.backend, key) } } + +/// Utility methods for the client. +pub mod utils { + use super::*; + use crate::{backend::Backend, blockchain, error}; + use primitives::H256; + + /// Returns a function for checking block ancestry, the returned function will + /// return `true` if the given hash (second parameter) is a descendent of the + /// base (first parameter). If the `current` parameter is defined, it should + /// represent the current block `hash` and its `parent hash`, if given the + /// function that's returned will assume that `hash` isn't part of the local DB + /// yet, and all searches in the DB will instead reference the parent. + pub fn is_descendent_of<'a, B, E, Block: BlockT, RA>( + client: &'a Client, + current: Option<(&'a H256, &'a H256)>, + ) -> impl Fn(&H256, &H256) -> Result + 'a + where B: Backend, + E: CallExecutor + Send + Sync, + { + move |base, hash| { + if base == hash { return Ok(false); } + + let mut hash = hash; + if let Some((current_hash, current_parent_hash)) = current { + if base == current_hash { return Ok(false); } + if hash == current_hash { + if base == current_parent_hash { + return Ok(true); + } else { + hash = current_parent_hash; + } + } + } + + let tree_route = blockchain::tree_route( + #[allow(deprecated)] + client.backend().blockchain(), + BlockId::Hash(*hash), + BlockId::Hash(*base), + )?; + + Ok(tree_route.common_block().hash == *base) + } + } +} + #[cfg(test)] pub(crate) mod tests { use std::collections::HashMap; @@ -2471,4 +2559,139 @@ pub(crate) mod tests { None, ); } + + #[test] + fn importing_diverged_finalized_block_should_trigger_reorg() { + use test_client::blockchain::HeaderBackend; + + let client = test_client::new(); + + // G -> A1 -> A2 + // \ + // -> B1 + let a1 = client.new_block_at(&BlockId::Number(0), Default::default()).unwrap().bake().unwrap(); + client.import(BlockOrigin::Own, a1.clone()).unwrap(); + + let a2 = client.new_block_at(&BlockId::Hash(a1.hash()), Default::default()).unwrap().bake().unwrap(); + client.import(BlockOrigin::Own, a2.clone()).unwrap(); + + let mut b1 = client.new_block_at(&BlockId::Number(0), Default::default()).unwrap(); + // needed to make sure B1 gets a different hash from A1 + b1.push_transfer(Transfer { + from: AccountKeyring::Alice.into(), + to: AccountKeyring::Ferdie.into(), + amount: 1, + nonce: 0, + }).unwrap(); + // create but don't import B1 just yet + let b1 = b1.bake().unwrap(); + + #[allow(deprecated)] + let blockchain = client.backend().blockchain(); + + // A2 is the current best since it's the longest chain + assert_eq!( + blockchain.info().best_hash, + a2.hash(), + ); + + // importing B1 as finalized should trigger a re-org and set it as new best + let justification = vec![1, 2, 3]; + client.import_justified(BlockOrigin::Own, b1.clone(), justification).unwrap(); + + assert_eq!( + blockchain.info().best_hash, + b1.hash(), + ); + + assert_eq!( + blockchain.info().finalized_hash, + b1.hash(), + ); + } + + #[test] + fn finalizing_diverged_block_should_trigger_reorg() { + use test_client::blockchain::HeaderBackend; + + let (client, select_chain) = TestClientBuilder::new().build_with_longest_chain(); + + // G -> A1 -> A2 + // \ + // -> B1 -> B2 + let a1 = client.new_block_at(&BlockId::Number(0), Default::default()).unwrap().bake().unwrap(); + client.import(BlockOrigin::Own, a1.clone()).unwrap(); + + let a2 = client.new_block_at(&BlockId::Hash(a1.hash()), Default::default()).unwrap().bake().unwrap(); + client.import(BlockOrigin::Own, a2.clone()).unwrap(); + + let mut b1 = client.new_block_at(&BlockId::Number(0), Default::default()).unwrap(); + // needed to make sure B1 gets a different hash from A1 + b1.push_transfer(Transfer { + from: AccountKeyring::Alice.into(), + to: AccountKeyring::Ferdie.into(), + amount: 1, + nonce: 0, + }).unwrap(); + let b1 = b1.bake().unwrap(); + client.import(BlockOrigin::Own, b1.clone()).unwrap(); + + let b2 = client.new_block_at(&BlockId::Hash(b1.hash()), Default::default()).unwrap().bake().unwrap(); + client.import(BlockOrigin::Own, b2.clone()).unwrap(); + + #[allow(deprecated)] + let blockchain = client.backend().blockchain(); + + // A2 is the current best since it's the longest chain + assert_eq!( + blockchain.info().best_hash, + a2.hash(), + ); + + // we finalize block B1 which is on a different branch from current best + // which should trigger a re-org. + client.finalize_block(BlockId::Hash(b1.hash()), None, false).unwrap(); + + // B1 should now be the latest finalized + assert_eq!( + blockchain.info().finalized_hash, + b1.hash(), + ); + + // and B1 should be the new best block (`finalize_block` as no way of + // knowing about B2) + assert_eq!( + blockchain.info().best_hash, + b1.hash(), + ); + + // `SelectChain` should report B2 as best block though + assert_eq!( + select_chain.best_chain().unwrap().hash(), + b2.hash(), + ); + + // after we build B3 on top of B2 and import it + // it should be the new best block, + let b3 = client.new_block_at( + &BlockId::Hash(b2.hash()), + Default::default(), + ).unwrap().bake().unwrap(); + client.import(BlockOrigin::Own, b3.clone()).unwrap(); + + assert_eq!( + blockchain.info().best_hash, + b3.hash(), + ); + } + + #[test] + fn get_header_by_block_number_doesnt_panic() { + let client = test_client::new(); + + // backend uses u32 for block numbers, make sure we don't panic when + // trying to convert + let id = BlockId::::Number(72340207214430721); + client.header(&id).expect_err("invalid block number overflows u32"); + } } diff --git a/core/client/src/error.rs b/core/client/src/error.rs index ab2673bc1b24661acd7b2937c24d05e67494be55..324e475407a5661bad7273cbe63140e4224e929f 100644 --- a/core/client/src/error.rs +++ b/core/client/src/error.rs @@ -97,6 +97,9 @@ pub enum Error { /// Hash that is required for building CHT is missing. #[display(fmt = "Failed to get hash of block for building CHT")] MissingHashRequiredForCHT, + /// Invalid calculated state root on block import. + #[display(fmt = "Calculated state root does not match.")] + InvalidStateRoot, /// A convenience variant for String #[display(fmt = "{}", _0)] Msg(String), diff --git a/core/client/src/genesis.rs b/core/client/src/genesis.rs index 2e56cd93df9bf20275a08f4e279d3b6d1fe5ba75..a26ed5f950e564aec7ad46e317a0bc77ddbecdfc 100644 --- a/core/client/src/genesis.rs +++ b/core/client/src/genesis.rs @@ -53,7 +53,12 @@ mod tests { use primitives::Blake2Hasher; use hex::*; - native_executor_instance!(Executor, test_client::runtime::api::dispatch, test_client::runtime::native_version, include_bytes!("../../test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm")); + native_executor_instance!( + Executor, + test_client::runtime::api::dispatch, + test_client::runtime::native_version, + test_client::runtime::WASM_BINARY + ); fn executor() -> executor::NativeExecutor { NativeExecutionDispatch::new(None) diff --git a/core/client/src/in_mem.rs b/core/client/src/in_mem.rs index 7053f6b9830bda05d6e4e236b98eb67366e54630..3fa2f7d9058fe1516c728f6f67c980110c64359c 100644 --- a/core/client/src/in_mem.rs +++ b/core/client/src/in_mem.rs @@ -588,6 +588,7 @@ where type BlockImportOperation = BlockImportOperation; type Blockchain = Blockchain; type State = InMemory; + type OffchainStorage = OffchainStorage; fn begin_operation(&self) -> error::Result { let old_state = self.state_at(BlockId::Hash(Default::default()))?; @@ -664,6 +665,10 @@ where None } + fn offchain_storage(&self) -> Option { + None + } + fn state_at(&self, block: BlockId) -> error::Result { match block { BlockId::Hash(h) if h == Default::default() => { @@ -720,8 +725,50 @@ pub fn check_genesis_storage(top: &StorageOverlay, children: &ChildrenStorageOve Ok(()) } +/// In-memory storage for offchain workers. +#[derive(Debug, Clone, Default)] +pub struct OffchainStorage { + storage: HashMap, Vec>, +} + +impl backend::OffchainStorage for OffchainStorage { + fn set(&mut self, prefix: &[u8], key: &[u8], value: &[u8]) { + let key = prefix.iter().chain(key).cloned().collect(); + self.storage.insert(key, value.to_vec()); + } + + fn get(&self, prefix: &[u8], key: &[u8]) -> Option> { + let key: Vec = prefix.iter().chain(key).cloned().collect(); + self.storage.get(&key).cloned() + } + + fn compare_and_set( + &mut self, + prefix: &[u8], + key: &[u8], + old_value: Option<&[u8]>, + new_value: &[u8], + ) -> bool { + use std::collections::hash_map::Entry; + let key = prefix.iter().chain(key).cloned().collect(); + + match self.storage.entry(key) { + Entry::Vacant(entry) => if old_value.is_none() { + entry.insert(new_value.to_vec()); + true + } else { false }, + Entry::Occupied(ref mut entry) if Some(entry.get().as_slice()) == old_value => { + entry.insert(new_value.to_vec()); + true + }, + _ => false, + } + } +} + #[cfg(test)] mod tests { + use super::*; use std::sync::Arc; use test_client; use primitives::Blake2Hasher; @@ -741,4 +788,26 @@ mod tests { test_client::trait_tests::test_blockchain_query_by_number_gets_canonical(backend); } + + #[test] + fn in_memory_offchain_storage() { + use crate::backend::OffchainStorage as _; + + let mut storage = OffchainStorage::default(); + assert_eq!(storage.get(b"A", b"B"), None); + assert_eq!(storage.get(b"B", b"A"), None); + + storage.set(b"A", b"B", b"C"); + assert_eq!(storage.get(b"A", b"B"), Some(b"C".to_vec())); + assert_eq!(storage.get(b"B", b"A"), None); + + storage.compare_and_set(b"A", b"B", Some(b"X"), b"D"); + assert_eq!(storage.get(b"A", b"B"), Some(b"C".to_vec())); + storage.compare_and_set(b"A", b"B", Some(b"C"), b"D"); + assert_eq!(storage.get(b"A", b"B"), Some(b"D".to_vec())); + + assert!(!storage.compare_and_set(b"B", b"A", Some(b""), b"Y")); + assert!(storage.compare_and_set(b"B", b"A", None, b"X")); + assert_eq!(storage.get(b"B", b"A"), Some(b"X".to_vec())); + } } diff --git a/core/client/src/lib.rs b/core/client/src/lib.rs index 276cc1b6215f08210044e973bbda8b1423e80408..1a566c40f4977f4d6ec2c724365b6e772cea318c 100644 --- a/core/client/src/lib.rs +++ b/core/client/src/lib.rs @@ -58,8 +58,9 @@ pub use crate::client::{ new_with_backend, new_in_mem, BlockBody, BlockStatus, ImportNotifications, FinalityNotifications, BlockchainEvents, - BlockImportNotification, Client, ClientInfo, ExecutionStrategies, + BlockImportNotification, Client, ClientInfo, ExecutionStrategies, FinalityNotification, LongestChain, + utils, }; #[cfg(feature = "std")] pub use crate::notifications::{StorageEventStream, StorageChangeSet}; diff --git a/core/client/src/light/backend.rs b/core/client/src/light/backend.rs index a0b84f36ea5f8678003d1b93179d02308ae2a337..e1438e8d734c775c0df1432e956b228ad41d94f9 100644 --- a/core/client/src/light/backend.rs +++ b/core/client/src/light/backend.rs @@ -19,7 +19,6 @@ use std::collections::HashMap; use std::sync::{Arc, Weak}; -use futures::{Future, IntoFuture}; use parking_lot::{RwLock, Mutex}; use runtime_primitives::{generic::BlockId, Justification, StorageOverlay, ChildrenStorageOverlay}; @@ -37,7 +36,7 @@ use crate::light::fetcher::{Fetcher, RemoteReadRequest}; use hash_db::Hasher; use trie::MemoryDB; -const IN_MEMORY_EXPECT_PROOF: &str = "InMemory state backend has Void error type and always suceeds; qed"; +const IN_MEMORY_EXPECT_PROOF: &str = "InMemory state backend has Void error type and always succeeds; qed"; /// Light client backend. pub struct Backend { @@ -116,6 +115,7 @@ impl ClientBackend for Backend where type BlockImportOperation = ImportOperation; type Blockchain = Blockchain; type State = OnDemandOrGenesisState; + type OffchainStorage = crate::in_mem::OffchainStorage; fn begin_operation(&self) -> ClientResult { Ok(ImportOperation { @@ -193,6 +193,10 @@ impl ClientBackend for Backend where None } + fn offchain_storage(&self) -> Option { + None + } + fn state_at(&self, block: BlockId) -> ClientResult { let block_number = self.blockchain.expect_block_number_from_id(&block)?; @@ -352,14 +356,15 @@ where *self.cached_header.write() = Some(cached_header); } - self.fetcher.upgrade().ok_or(ClientError::NotAvailableOnLightClient)? - .remote_read(RemoteReadRequest { - block: self.block, - header: header.expect("if block above guarantees that header is_some(); qed"), - key: key.to_vec(), - retry_count: None, - }) - .into_future().wait() + futures::executor::block_on( + self.fetcher.upgrade().ok_or(ClientError::NotAvailableOnLightClient)? + .remote_read(RemoteReadRequest { + block: self.block, + header: header.expect("if block above guarantees that header is_some(); qed"), + key: key.to_vec(), + retry_count: None, + }) + ) } fn child_storage(&self, _storage_key: &[u8], _key: &[u8]) -> ClientResult>> { diff --git a/core/client/src/light/blockchain.rs b/core/client/src/light/blockchain.rs index 13bc4a0a0863ff1b7b61696199d609d5dcc16a3f..ce164b5589cad820f0213c81ca0861cc87411ea6 100644 --- a/core/client/src/light/blockchain.rs +++ b/core/client/src/light/blockchain.rs @@ -18,7 +18,6 @@ //! blocks. CHT roots are stored for headers of ancient blocks. use std::{sync::{Weak, Arc}, collections::HashMap}; -use futures::{Future, IntoFuture}; use parking_lot::Mutex; use runtime_primitives::{Justification, generic::BlockId}; @@ -124,14 +123,15 @@ impl BlockchainHeaderBackend for Blockchain where Bloc return Ok(None); } - self.fetcher().upgrade().ok_or(ClientError::NotAvailableOnLightClient)? - .remote_header(RemoteHeaderRequest { - cht_root: self.storage.header_cht_root(cht::size(), number)?, - block: number, - retry_count: None, + futures::executor::block_on( + self.fetcher().upgrade() + .ok_or(ClientError::NotAvailableOnLightClient)? + .remote_header(RemoteHeaderRequest { + cht_root: self.storage.header_cht_root(cht::size(), number)?, + block: number, + retry_count: None, }) - .into_future().wait() - .map(Some) + ).map(Some) } } } @@ -160,13 +160,13 @@ impl BlockchainBackend for Blockchain where Block: Blo None => return Ok(None), }; - self.fetcher().upgrade().ok_or(ClientError::NotAvailableOnLightClient)? - .remote_body(RemoteBodyRequest { - header, - retry_count: None, - }) - .into_future().wait() - .map(Some) + futures::executor::block_on( + self.fetcher().upgrade().ok_or(ClientError::NotAvailableOnLightClient)? + .remote_body(RemoteBodyRequest { + header, + retry_count: None, + }) + ).map(Some) } fn justification(&self, _id: BlockId) -> ClientResult> { diff --git a/core/client/src/light/call_executor.rs b/core/client/src/light/call_executor.rs index 4dba803921527d836f2a9acab7fbe7b40290dbc8..faa7c10def0702b2f52735704113e16168b3d2d6 100644 --- a/core/client/src/light/call_executor.rs +++ b/core/client/src/light/call_executor.rs @@ -21,7 +21,6 @@ use std::{ collections::HashSet, sync::Arc, panic::UnwindSafe, result, marker::PhantomData, cell::RefCell, rc::Rc, }; -use futures::{IntoFuture, Future}; use parity_codec::{Encode, Decode}; use primitives::{offchain, H256, Blake2Hasher, convert_hash, NativeOrEncoded}; @@ -100,13 +99,13 @@ where let block_hash = self.blockchain.expect_block_hash_from_id(id)?; let block_header = self.blockchain.expect_header(id.clone())?; - self.fetcher.remote_call(RemoteCallRequest { + futures::executor::block_on(self.fetcher.remote_call(RemoteCallRequest { block: block_hash, header: block_header, method: method.into(), call_data: call_data.to_vec(), retry_count: None, - }).into_future().wait() + })) } fn contextual_call< @@ -170,7 +169,11 @@ where _m: ExecutionManager, _native_call: Option, _side_effects_handler: Option<&mut O>, - ) -> ClientResult<(NativeOrEncoded, S::Transaction, Option>)> { + ) -> ClientResult<( + NativeOrEncoded, + (S::Transaction, ::Out), + Option>, + )> { Err(ClientError::NotAvailableOnLightClient.into()) } @@ -343,7 +346,11 @@ impl CallExecutor for _manager: ExecutionManager, native_call: Option, side_effects_handler: Option<&mut O>, - ) -> ClientResult<(NativeOrEncoded, S::Transaction, Option>)> { + ) -> ClientResult<( + NativeOrEncoded, + (S::Transaction, ::Out), + Option>, + )> { // there's no actual way/need to specify native/wasm execution strategy on light node // => we can safely ignore passed values diff --git a/core/client/src/light/fetcher.rs b/core/client/src/light/fetcher.rs index 6f8228fa16511f874e3e77896661ea5f08cf39d0..1c94d1e47d64ab9cce6a6e89d9a7c12f5c3b9127 100644 --- a/core/client/src/light/fetcher.rs +++ b/core/client/src/light/fetcher.rs @@ -19,7 +19,7 @@ use std::sync::Arc; use std::collections::BTreeMap; use std::marker::PhantomData; -use futures::IntoFuture; +use std::future::Future; use hash_db::{HashDB, Hasher}; use parity_codec::{Decode, Encode}; @@ -141,15 +141,15 @@ pub struct RemoteBodyRequest { /// is correct (see FetchedDataChecker) and return already checked data. pub trait Fetcher: Send + Sync { /// Remote header future. - type RemoteHeaderResult: IntoFuture; + type RemoteHeaderResult: Future>; /// Remote storage read future. - type RemoteReadResult: IntoFuture>, Error = ClientError>; + type RemoteReadResult: Future>, ClientError>>; /// Remote call result future. - type RemoteCallResult: IntoFuture, Error = ClientError>; + type RemoteCallResult: Future, ClientError>>; /// Remote changes result future. - type RemoteChangesResult: IntoFuture, u32)>, Error = ClientError>; + type RemoteChangesResult: Future, u32)>, ClientError>>; /// Remote block body result future. - type RemoteBodyResult: IntoFuture, Error = ClientError>; + type RemoteBodyResult: Future, ClientError>>; /// Fetch remote header. fn remote_header(&self, request: RemoteHeaderRequest) -> Self::RemoteHeaderResult; @@ -485,7 +485,7 @@ impl<'a, H, Number, Hash> ChangesTrieRootsStorage for RootsStorage<'a #[cfg(test)] pub mod tests { - use futures::future::{ok, err, FutureResult}; + use futures::future::Ready; use parking_lot::Mutex; use parity_codec::Decode; use crate::client::tests::prepare_client_with_key_changes; @@ -509,19 +509,19 @@ pub mod tests { pub type OkCallFetcher = Mutex>; - fn not_implemented_in_tests() -> FutureResult + fn not_implemented_in_tests() -> Ready> where E: std::convert::From<&'static str>, { - err("Not implemented on test node".into()) + futures::future::ready(Err("Not implemented on test node".into())) } impl Fetcher for OkCallFetcher { - type RemoteHeaderResult = FutureResult; - type RemoteReadResult = FutureResult>, ClientError>; - type RemoteCallResult = FutureResult, ClientError>; - type RemoteChangesResult = FutureResult, u32)>, ClientError>; - type RemoteBodyResult = FutureResult, ClientError>; + type RemoteHeaderResult = Ready>; + type RemoteReadResult = Ready>, ClientError>>; + type RemoteCallResult = Ready, ClientError>>; + type RemoteChangesResult = Ready, u32)>, ClientError>>; + type RemoteBodyResult = Ready, ClientError>>; fn remote_header(&self, _request: RemoteHeaderRequest
) -> Self::RemoteHeaderResult { not_implemented_in_tests() @@ -536,7 +536,7 @@ pub mod tests { } fn remote_call(&self, _request: RemoteCallRequest
) -> Self::RemoteCallResult { - ok((*self.lock()).clone()) + futures::future::ready(Ok((*self.lock()).clone())) } fn remote_changes(&self, _request: RemoteChangesRequest
) -> Self::RemoteChangesResult { diff --git a/core/client/src/notifications.rs b/core/client/src/notifications.rs index 931a40f20d37e39750e22366084ff321380b785d..bfd97df95c12d808663923550e50e568f7de6e2e 100644 --- a/core/client/src/notifications.rs +++ b/core/client/src/notifications.rs @@ -22,7 +22,7 @@ use std::{ }; use fnv::{FnvHashSet, FnvHashMap}; -use futures::sync::mpsc; +use futures::channel::mpsc; use primitives::storage::{StorageKey, StorageData}; use runtime_primitives::traits::Block as BlockT; @@ -309,7 +309,6 @@ impl StorageNotifications { mod tests { use runtime_primitives::testing::{H256 as Hash, Block as RawBlock, ExtrinsicWrapper}; use super::*; - use futures::Stream; use std::iter::{empty, Empty}; type TestChangeSet = ( @@ -348,7 +347,9 @@ mod tests { // given let mut notifications = StorageNotifications::::default(); let child_filter = [(StorageKey(vec![4]), None)]; - let mut recv = notifications.listen(None, Some(&child_filter[..])).wait(); + let mut recv = futures::executor::block_on_stream( + notifications.listen(None, Some(&child_filter[..])) + ); // when let changeset = vec![ @@ -367,13 +368,13 @@ mod tests { ); // then - assert_eq!(recv.next().unwrap(), Ok((Hash::from_low_u64_be(1), (vec![ + assert_eq!(recv.next().unwrap(), (Hash::from_low_u64_be(1), (vec![ (StorageKey(vec![2]), Some(StorageData(vec![3]))), (StorageKey(vec![3]), None), ], vec![(StorageKey(vec![4]), vec![ (StorageKey(vec![5]), Some(StorageData(vec![4]))), (StorageKey(vec![6]), None), - ])]).into()))); + ])]).into())); } #[test] @@ -381,9 +382,15 @@ mod tests { // given let mut notifications = StorageNotifications::::default(); let child_filter = [(StorageKey(vec![4]), Some(vec![StorageKey(vec![5])]))]; - let mut recv1 = notifications.listen(Some(&[StorageKey(vec![1])]), None).wait(); - let mut recv2 = notifications.listen(Some(&[StorageKey(vec![2])]), None).wait(); - let mut recv3 = notifications.listen(Some(&[]), Some(&child_filter)).wait(); + let mut recv1 = futures::executor::block_on_stream( + notifications.listen(Some(&[StorageKey(vec![1])]), None) + ); + let mut recv2 = futures::executor::block_on_stream( + notifications.listen(Some(&[StorageKey(vec![2])]), None) + ); + let mut recv3 = futures::executor::block_on_stream( + notifications.listen(Some(&[]), Some(&child_filter)) + ); // when let changeset = vec![ @@ -403,16 +410,16 @@ mod tests { ); // then - assert_eq!(recv1.next().unwrap(), Ok((Hash::from_low_u64_be(1), (vec![ + assert_eq!(recv1.next().unwrap(), (Hash::from_low_u64_be(1), (vec![ (StorageKey(vec![1]), None), - ], vec![]).into()))); - assert_eq!(recv2.next().unwrap(), Ok((Hash::from_low_u64_be(1), (vec![ + ], vec![]).into())); + assert_eq!(recv2.next().unwrap(), (Hash::from_low_u64_be(1), (vec![ (StorageKey(vec![2]), Some(StorageData(vec![3]))), - ], vec![]).into()))); - assert_eq!(recv3.next().unwrap(), Ok((Hash::from_low_u64_be(1), (vec![], + ], vec![]).into())); + assert_eq!(recv3.next().unwrap(), (Hash::from_low_u64_be(1), (vec![], vec![ (StorageKey(vec![4]), vec![(StorageKey(vec![5]), Some(StorageData(vec![4])))]), - ]).into()))); + ]).into())); } @@ -422,10 +429,18 @@ mod tests { let mut notifications = StorageNotifications::::default(); { let child_filter = [(StorageKey(vec![4]), Some(vec![StorageKey(vec![5])]))]; - let _recv1 = notifications.listen(Some(&[StorageKey(vec![1])]), None).wait(); - let _recv2 = notifications.listen(Some(&[StorageKey(vec![2])]), None).wait(); - let _recv3 = notifications.listen(None, None).wait(); - let _recv4 = notifications.listen(None, Some(&child_filter)).wait(); + let _recv1 = futures::executor::block_on_stream( + notifications.listen(Some(&[StorageKey(vec![1])]), None) + ); + let _recv2 = futures::executor::block_on_stream( + notifications.listen(Some(&[StorageKey(vec![2])]), None) + ); + let _recv3 = futures::executor::block_on_stream( + notifications.listen(None, None) + ); + let _recv4 = futures::executor::block_on_stream( + notifications.listen(None, Some(&child_filter)) + ); assert_eq!(notifications.listeners.len(), 2); assert_eq!(notifications.wildcard_listeners.len(), 2); assert_eq!(notifications.child_listeners.len(), 1); @@ -450,7 +465,7 @@ mod tests { // given let mut recv = { let mut notifications = StorageNotifications::::default(); - let recv = notifications.listen(None, None).wait(); + let recv = futures::executor::block_on_stream(notifications.listen(None, None)); // when let changeset = vec![]; diff --git a/core/consensus/aura/Cargo.toml b/core/consensus/aura/Cargo.toml index 400f209a9b7c36b712c7bf5bc72659074ca84763..03ddf79be3418fde50053aa48fdf9ba1de15d51a 100644 --- a/core/consensus/aura/Cargo.toml +++ b/core/consensus/aura/Cargo.toml @@ -6,7 +6,7 @@ description = "Aura consensus algorithm for substrate" edition = "2018" [dependencies] -parity-codec = "3.4" +parity-codec = "4.1.1" primitives = { package = "substrate-primitives", path = "../../primitives" } runtime_support = { package = "srml-support", path = "../../../srml/support" } runtime_version = { package = "sr-version", path = "../../sr-version" } @@ -25,6 +25,7 @@ parking_lot = "0.8.0" log = "0.4" [dev-dependencies] +futures03 = { package = "futures-preview", version = "0.3.0-alpha.17", features = ["compat"] } keyring = { package = "substrate-keyring", path = "../../keyring" } substrate-executor = { path = "../../executor" } network = { package = "substrate-network", path = "../../network", features = ["test-helpers"]} diff --git a/core/consensus/aura/primitives/Cargo.toml b/core/consensus/aura/primitives/Cargo.toml index 74f678a6c0bc9f64ebf8100b7b2462e64408c7fd..bc51c90d8c7e0450c15fc62cb20a29382778c01c 100644 --- a/core/consensus/aura/primitives/Cargo.toml +++ b/core/consensus/aura/primitives/Cargo.toml @@ -6,7 +6,7 @@ description = "Primitives for Aura consensus" edition = "2018" [dependencies] -parity-codec = { version = "3.5", default-features = false } +parity-codec = { version = "4.1.1", default-features = false } substrate-client = { path = "../../../client", default-features = false } substrate-primitives = { path = "../../../primitives", default-features = false } rstd = { package = "sr-std", path = "../../../sr-std", default-features = false } diff --git a/core/consensus/aura/primitives/src/lib.rs b/core/consensus/aura/primitives/src/lib.rs index 9bdf39d29337d5d4e2ae0766a34216821f77626c..47b1399a677f420fcc18f5efad733d10d3cb82f0 100644 --- a/core/consensus/aura/primitives/src/lib.rs +++ b/core/consensus/aura/primitives/src/lib.rs @@ -26,11 +26,18 @@ use runtime_primitives::ConsensusEngineId; /// The `ConsensusEngineId` of AuRa. pub const AURA_ENGINE_ID: ConsensusEngineId = [b'a', b'u', b'r', b'a']; +/// The index of an authority. +pub type AuthorityIndex = u64; + /// An consensus log item for Aura. #[derive(Decode, Encode)] pub enum ConsensusLog { /// The authorities have changed. + #[codec(index = "1")] AuthoritiesChange(Vec), + /// Disable the authority with given index. + #[codec(index = "2")] + OnDisabled(AuthorityIndex), } decl_runtime_apis! { diff --git a/core/consensus/aura/src/lib.rs b/core/consensus/aura/src/lib.rs index 5b320eb67652a298ae10f5491a806404671f1cab..06fd7f1aff6cb18898dd5e7b51f3762289a1df16 100644 --- a/core/consensus/aura/src/lib.rs +++ b/core/consensus/aura/src/lib.rs @@ -32,12 +32,11 @@ use std::{sync::Arc, time::Duration, thread, marker::PhantomData, hash::Hash, fm use parity_codec::{Encode, Decode, Codec}; use consensus_common::{self, BlockImport, Environment, Proposer, - ForkChoiceStrategy, ImportBlock, BlockOrigin, Error as ConsensusError, + ForkChoiceStrategy, BlockImportParams, BlockOrigin, Error as ConsensusError, SelectChain, }; use consensus_common::import_queue::{ - Verifier, BasicQueue, SharedBlockImport, SharedJustificationImport, SharedFinalityProofImport, - SharedFinalityProofRequestBuilder, + Verifier, BasicQueue, BoxBlockImport, BoxJustificationImport, BoxFinalityProofImport, }; use client::{ block_builder::api::BlockBuilder as BlockBuilderApi, @@ -49,12 +48,13 @@ use client::{ }; use runtime_primitives::{generic::{self, BlockId, OpaqueDigestItemId}, Justification}; -use runtime_primitives::traits::{Block, Header, DigestItemFor, ProvideRuntimeApi, Zero, Member}; +use runtime_primitives::traits::{Block as BlockT, Header, DigestItemFor, ProvideRuntimeApi, Zero, Member}; use primitives::Pair; use inherents::{InherentDataProviders, InherentData}; use futures::{Future, IntoFuture, future}; +use parking_lot::Mutex; use tokio_timer::Timeout; use log::{error, warn, debug, info, trace}; @@ -85,7 +85,7 @@ impl SlotDuration { pub fn get_or_compute(client: &C) -> CResult where A: Codec, - B: Block, + B: BlockT, C: AuxStore + ProvideRuntimeApi, C::Api: AuraApi, { @@ -118,12 +118,14 @@ struct AuraSlotCompatible; impl SlotCompatible for AuraSlotCompatible { fn extract_timestamp_and_slot( + &self, data: &InherentData - ) -> Result<(TimestampInherent, AuraInherent), consensus_common::Error> { + ) -> Result<(TimestampInherent, AuraInherent, std::time::Duration), consensus_common::Error> { data.timestamp_inherent_data() .and_then(|t| data.aura_inherent_data().map(|a| (t, a))) .map_err(Into::into) .map_err(consensus_common::Error::InherentData) + .map(|(x, y)| (x, y, Default::default())) } } @@ -133,13 +135,13 @@ pub fn start_aura( local_key: Arc

, client: Arc, select_chain: SC, - block_import: Arc, + block_import: I, env: Arc, sync_oracle: SO, inherent_data_providers: InherentDataProviders, force_authoring: bool, ) -> Result, consensus_common::Error> where - B: Block, + B: BlockT, C: ProvideRuntimeApi + ProvideCache + AuxStore + Send + Sync, C::Api: AuraApi>, SC: SelectChain, @@ -156,7 +158,7 @@ pub fn start_aura( { let worker = AuraWorker { client: client.clone(), - block_import, + block_import: Arc::new(Mutex::new(block_import)), env, local_key, sync_oracle: sync_oracle.clone(), @@ -171,13 +173,14 @@ pub fn start_aura( select_chain, worker, sync_oracle, - inherent_data_providers + inherent_data_providers, + AuraSlotCompatible, )) } struct AuraWorker { client: Arc, - block_import: Arc, + block_import: Arc>, env: Arc, local_key: Arc

, sync_oracle: SO, @@ -185,7 +188,7 @@ struct AuraWorker { } impl SlotWorker for AuraWorker where - B: Block, + B: BlockT, C: ProvideRuntimeApi + ProvideCache + Sync, C::Api: AuraApi>, E: Environment, @@ -315,7 +318,7 @@ impl SlotWorker for AuraWorker w let signature = pair.sign(header_hash.as_ref()); let signature_digest_item = as CompatibleDigestItem

>::aura_seal(signature); - let import_block: ImportBlock = ImportBlock { + let import_block: BlockImportParams = BlockImportParams { origin: BlockOrigin::Own, header, justification: None, @@ -337,7 +340,7 @@ impl SlotWorker for AuraWorker w "hash_previously" => ?header_hash ); - if let Err(e) = block_import.import_block(import_block, Default::default()) { + if let Err(e) = block_import.lock().import_block(import_block, Default::default()) { warn!(target: "aura", "Error with block built on {:?}: {:?}", parent_hash, e); telemetry!(CONSENSUS_WARN; "aura.err_with_block_built_on"; @@ -356,7 +359,7 @@ macro_rules! aura_err { }; } -fn find_pre_digest(header: &B::Header) -> Result +fn find_pre_digest(header: &B::Header) -> Result where DigestItemFor: CompatibleDigestItem

, P::Signature: Decode, P::Public: Encode + Decode + PartialEq + Clone, @@ -380,7 +383,7 @@ fn find_pre_digest(header: &B::Header) -> Result /// This digest item will always return `Some` when used with `as_aura_seal`. // // FIXME #1018 needs misbehavior types -fn check_header( +fn check_header( client: &C, slot_now: u64, mut header: B::Header, @@ -449,7 +452,7 @@ pub struct AuraVerifier { impl AuraVerifier where P: Send + Sync + 'static { - fn check_inherents( + fn check_inherents( &self, block: B, block_id: BlockId, @@ -499,7 +502,7 @@ impl AuraVerifier } #[forbid(deprecated)] -impl Verifier for AuraVerifier where +impl Verifier for AuraVerifier where C: ProvideRuntimeApi + Send + Sync + client::backend::AuxStore + ProvideCache, C::Api: BlockBuilderApi + AuraApi>, DigestItemFor: CompatibleDigestItem

, @@ -513,9 +516,9 @@ impl Verifier for AuraVerifier where header: B::Header, justification: Option, mut body: Option>, - ) -> Result<(ImportBlock, Option)>>), String> { + ) -> Result<(BlockImportParams, Option)>>), String> { let mut inherent_data = self.inherent_data_providers.create_inherent_data().map_err(String::from)?; - let (timestamp_now, slot_now) = AuraSlotCompatible::extract_timestamp_and_slot(&inherent_data) + let (timestamp_now, slot_now, _) = AuraSlotCompatible.extract_timestamp_and_slot(&inherent_data) .map_err(|e| format!("Could not extract timestamp and slot: {:?}", e))?; let hash = header.hash(); let parent_hash = *header.parent_hash(); @@ -562,16 +565,21 @@ impl Verifier for AuraVerifier where trace!(target: "aura", "Checked {:?}; importing.", pre_header); telemetry!(CONSENSUS_TRACE; "aura.checked_and_importing"; "pre_header" => ?pre_header); - // `Consensus` is the Aura-specific authorities change log. + // Look for an authorities-change log. let maybe_keys = pre_header.digest() - .convert_first(|l| l.try_to::>>( + .logs() + .iter() + .filter_map(|l| l.try_to::>>( OpaqueDigestItemId::Consensus(&AURA_ENGINE_ID) )) - .map(|ConsensusLog::AuthoritiesChange(a)| - vec![(well_known_cache_keys::AUTHORITIES, a.encode())] - ); - - let import_block = ImportBlock { + .find_map(|l| match l { + ConsensusLog::AuthoritiesChange(a) => Some( + vec![(well_known_cache_keys::AUTHORITIES, a.encode())] + ), + _ => None, + }); + + let import_block = BlockImportParams { origin, header: pre_header, post_digests: vec![seal], @@ -597,7 +605,7 @@ impl Verifier for AuraVerifier where fn initialize_authorities_cache(client: &C) -> Result<(), ConsensusError> where A: Codec, - B: Block, + B: BlockT, C: ProvideRuntimeApi + ProvideCache, C::Api: AuraApi, { @@ -631,7 +639,7 @@ fn initialize_authorities_cache(client: &C) -> Result<(), ConsensusErro #[allow(deprecated)] fn authorities(client: &C, at: &BlockId) -> Result, ConsensusError> where A: Codec, - B: Block, + B: BlockT, C: ProvideRuntimeApi + ProvideCache, C::Api: AuraApi, { @@ -666,14 +674,13 @@ fn register_aura_inherent_data_provider( /// Start an import queue for the Aura consensus algorithm. pub fn import_queue( slot_duration: SlotDuration, - block_import: SharedBlockImport, - justification_import: Option>, - finality_proof_import: Option>, - finality_proof_request_builder: Option>, + block_import: BoxBlockImport, + justification_import: Option>, + finality_proof_import: Option>, client: Arc, inherent_data_providers: InherentDataProviders, ) -> Result, consensus_common::Error> where - B: Block, + B: BlockT, C: 'static + ProvideRuntimeApi + ProvideCache + Send + Sync + AuxStore, C::Api: BlockBuilderApi + AuraApi>, DigestItemFor: CompatibleDigestItem

, @@ -696,14 +703,14 @@ pub fn import_queue( block_import, justification_import, finality_proof_import, - finality_proof_request_builder, )) } #[cfg(test)] mod tests { use super::*; - use futures::stream::Stream as _; + use futures::{Async, stream::Stream as _}; + use futures03::{StreamExt as _, TryStreamExt as _}; use consensus_common::NoNetwork as DummyOracle; use network::test::*; use network::test::{Block as TestBlock, PeersClient, PeersFullClient}; @@ -754,11 +761,9 @@ mod tests { } const SLOT_DURATION: u64 = 1; - const TEST_ROUTING_INTERVAL: Duration = Duration::from_millis(50); pub struct AuraTestNet { - peers: Vec>>, - started: bool, + peers: Vec>, } impl TestNetFactory for AuraTestNet { @@ -770,7 +775,6 @@ mod tests { fn from_config(_config: &ProtocolConfig) -> Self { AuraTestNet { peers: Vec::new(), - started: false, } } @@ -798,38 +802,24 @@ mod tests { } } - fn uses_tokio(&self) -> bool { - true + fn peer(&mut self, i: usize) -> &mut Peer { + &mut self.peers[i] } - fn peer(&self, i: usize) -> &Peer { - &self.peers[i] - } - - fn peers(&self) -> &Vec>> { + fn peers(&self) -> &Vec> { &self.peers } - fn mut_peers>>)>(&mut self, closure: F) { + fn mut_peers>)>(&mut self, closure: F) { closure(&mut self.peers); } - - fn started(&self) -> bool { - self.started - } - - fn set_started(&mut self, new: bool) { - self.started = new; - } } #[test] #[allow(deprecated)] fn authoring_blocks() { let _ = ::env_logger::try_init(); - let mut net = AuraTestNet::new(3); - - net.start(); + let net = AuraTestNet::new(3); let peers = &[ (0, Keyring::Alice), @@ -850,6 +840,7 @@ mod tests { let environ = Arc::new(DummyFactory(client.clone())); import_notifications.push( client.import_notification_stream() + .map(|v| Ok::<_, ()>(v)).compat() .take_while(|n| Ok(!(n.origin != BlockOrigin::Own && n.header.number() < &5))) .for_each(move |_| Ok(())) ); @@ -882,15 +873,7 @@ mod tests { .map(|_| ()) .map_err(|_| ()); - let drive_to_completion = ::tokio_timer::Interval::new_interval(TEST_ROUTING_INTERVAL) - .for_each(move |_| { - net.lock().send_import_notifications(); - net.lock().sync_without_disconnects(); - Ok(()) - }) - .map(|_| ()) - .map_err(|_| ()); - + let drive_to_completion = futures::future::poll_fn(|| { net.lock().poll(); Ok(Async::NotReady) }); let _ = runtime.block_on(wait_for.select(drive_to_completion).map_err(|_| ())).unwrap(); } diff --git a/core/consensus/babe/Cargo.toml b/core/consensus/babe/Cargo.toml index a20b7de00149d17a636286903cb6677ba36b1601..8b473932dd40977f84df6eb9a3f54b3c623fbca1 100644 --- a/core/consensus/babe/Cargo.toml +++ b/core/consensus/babe/Cargo.toml @@ -6,8 +6,7 @@ description = "BABE consensus algorithm for substrate" edition = "2018" [dependencies] -parity-codec = "3.4.0" -parity-codec-derive = "3.3.0" +parity-codec = { version = "4.1.1", features = ["derive"] } babe_primitives = { package = "substrate-consensus-babe-primitives", path = "primitives" } primitives = { package = "substrate-primitives", path = "../../primitives" } runtime_support = { package = "srml-support", path = "../../../srml/support" } @@ -20,7 +19,9 @@ client = { package = "substrate-client", path = "../../client" } consensus_common = { package = "substrate-consensus-common", path = "../common" } slots = { package = "substrate-consensus-slots", path = "../slots" } runtime_primitives = { package = "sr-primitives", path = "../../sr-primitives" } +fork-tree = { path = "../../utils/fork-tree" } futures = "0.1.26" +futures03 = { package = "futures-preview", version = "0.3.0-alpha.17", features = ["compat"] } tokio-timer = "0.2.11" parking_lot = "0.8.0" log = "0.4.6" diff --git a/core/consensus/babe/primitives/Cargo.toml b/core/consensus/babe/primitives/Cargo.toml index fd45f6470a27e236aa95a33d6cf342d897d21647..6eb0a251e1b1aa03f7c6ed3a4ee7bb9d028ae623 100644 --- a/core/consensus/babe/primitives/Cargo.toml +++ b/core/consensus/babe/primitives/Cargo.toml @@ -11,7 +11,8 @@ rstd = { package = "sr-std", path = "../../../sr-std", default-features = false runtime_primitives = { package = "sr-primitives", path = "../../../sr-primitives", default-features = false } substrate-primitives = { path = "../../../primitives", default-features = false } slots = { package = "substrate-consensus-slots", path = "../../slots", optional = true } -parity-codec = { version = "3.5.1", default-features = false } +parity-codec = { version = "4.1.1", default-features = false } +schnorrkel = { version = "0.1.1", optional = true } [features] default = ["std"] @@ -20,5 +21,6 @@ std = [ "runtime_primitives/std", "substrate-client/std", "parity-codec/std", + "schnorrkel", "slots", ] diff --git a/core/consensus/babe/src/digest.rs b/core/consensus/babe/primitives/src/digest.rs similarity index 52% rename from core/consensus/babe/src/digest.rs rename to core/consensus/babe/primitives/src/digest.rs index 7356001cf798843bd9216d36c0482038db68f7c2..f39cc962878d5f714877cbad3732d660d51d2166 100644 --- a/core/consensus/babe/src/digest.rs +++ b/core/consensus/babe/primitives/src/digest.rs @@ -14,67 +14,87 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -//! Private mplementation details of BABE digests. - -use primitives::sr25519::{Public, Signature}; -use babe_primitives::BABE_ENGINE_ID; +//! Private implementation details of BABE digests. + +#[cfg(feature = "std")] +use substrate_primitives::sr25519::Signature; +#[cfg(feature = "std")] +use super::{BABE_ENGINE_ID, Epoch}; +#[cfg(not(feature = "std"))] +use super::{VRF_OUTPUT_LENGTH, VRF_PROOF_LENGTH}; +use super::SlotNumber; +#[cfg(feature = "std")] use runtime_primitives::{DigestItem, generic::OpaqueDigestItemId}; +#[cfg(feature = "std")] use std::fmt::Debug; -use parity_codec::{Decode, Encode, Codec, Input}; -use schnorrkel::{ - vrf::{VRFProof, VRFOutput, VRF_OUTPUT_LENGTH, VRF_PROOF_LENGTH}, - PUBLIC_KEY_LENGTH, -}; - -/// A BABE pre-digest. It includes: -/// -/// * The public key of the author. -/// * The VRF proof. -/// * The VRF output. -/// * The slot number. -#[derive(Clone, Debug, PartialEq, Eq)] +use parity_codec::{Decode, Encode}; +#[cfg(feature = "std")] +use parity_codec::{Codec, Input}; +#[cfg(feature = "std")] +use schnorrkel::vrf::{VRFProof, VRFOutput, VRF_OUTPUT_LENGTH, VRF_PROOF_LENGTH}; + +/// A BABE pre-digest +#[cfg(feature = "std")] +#[derive(Clone, Debug)] pub struct BabePreDigest { - pub(super) vrf_output: VRFOutput, - pub(super) proof: VRFProof, - pub(super) author: Public, - pub(super) slot_num: u64, + /// VRF output + pub vrf_output: VRFOutput, + /// VRF proof + pub vrf_proof: VRFProof, + /// Authority index + pub authority_index: super::AuthorityIndex, + /// Slot number + pub slot_number: SlotNumber, } /// The prefix used by BABE for its VRF keys. pub const BABE_VRF_PREFIX: &'static [u8] = b"substrate-babe-vrf"; -type TmpDecode = ( - [u8; VRF_OUTPUT_LENGTH], - [u8; VRF_PROOF_LENGTH], - [u8; PUBLIC_KEY_LENGTH], - u64, -); +/// A raw version of `BabePreDigest`, usable on `no_std`. +#[derive(Copy, Clone, Encode, Decode)] +pub struct RawBabePreDigest { + /// Slot number + pub slot_number: SlotNumber, + /// Authority index + pub authority_index: super::AuthorityIndex, + /// VRF output + pub vrf_output: [u8; VRF_OUTPUT_LENGTH], + /// VRF proof + pub vrf_proof: [u8; VRF_PROOF_LENGTH], +} +#[cfg(feature = "std")] impl Encode for BabePreDigest { fn encode(&self) -> Vec { - let tmp: TmpDecode = ( - *self.vrf_output.as_bytes(), - self.proof.to_bytes(), - self.author.0, - self.slot_num, - ); + let tmp = RawBabePreDigest { + vrf_output: *self.vrf_output.as_bytes(), + vrf_proof: self.vrf_proof.to_bytes(), + authority_index: self.authority_index, + slot_number: self.slot_number, + }; parity_codec::Encode::encode(&tmp) } } +#[cfg(feature = "std")] impl Decode for BabePreDigest { fn decode(i: &mut R) -> Option { - let (output, proof, public_key, slot_num): TmpDecode = Decode::decode(i)?; + let RawBabePreDigest { vrf_output, vrf_proof, authority_index, slot_number } = Decode::decode(i)?; + + // Verify (at compile time) that the sizes in babe_primitives are correct + let _: [u8; super::VRF_OUTPUT_LENGTH] = vrf_output; + let _: [u8; super::VRF_PROOF_LENGTH] = vrf_proof; Some(BabePreDigest { - proof: VRFProof::from_bytes(&proof).ok()?, - vrf_output: VRFOutput::from_bytes(&output).ok()?, - author: Public(public_key), - slot_num, + vrf_proof: VRFProof::from_bytes(&vrf_proof).ok()?, + vrf_output: VRFOutput::from_bytes(&vrf_output).ok()?, + authority_index, + slot_number, }) } } /// A digest item which is usable with BABE consensus. +#[cfg(feature = "std")] pub trait CompatibleDigestItem: Sized { /// Construct a digest item which contains a BABE pre-digest. fn babe_pre_digest(seal: BabePreDigest) -> Self; @@ -87,8 +107,12 @@ pub trait CompatibleDigestItem: Sized { /// If this item is a BABE signature, return the signature. fn as_babe_seal(&self) -> Option; + + /// If this item is a BABE epoch, return it. + fn as_babe_epoch(&self) -> Option; } +#[cfg(feature = "std")] impl CompatibleDigestItem for DigestItem where Hash: Debug + Send + Sync + Eq + Clone + Codec + 'static { @@ -107,4 +131,8 @@ impl CompatibleDigestItem for DigestItem where fn as_babe_seal(&self) -> Option { self.try_to(OpaqueDigestItemId::Seal(&BABE_ENGINE_ID)) } + + fn as_babe_epoch(&self) -> Option { + self.try_to(OpaqueDigestItemId::Consensus(&BABE_ENGINE_ID)) + } } diff --git a/core/consensus/babe/primitives/src/lib.rs b/core/consensus/babe/primitives/src/lib.rs index f1d4a452d68725d7f1b1d69250d06fcc7152d7d9..a7b49364f4ac806eaa56a4a559588a2af04cd333 100644 --- a/core/consensus/babe/primitives/src/lib.rs +++ b/core/consensus/babe/primitives/src/lib.rs @@ -15,15 +15,22 @@ // along with Substrate. If not, see . //! Primitives for BABE. -#![deny(warnings, unsafe_code, missing_docs)] +#![deny(warnings)] +#![forbid(unsafe_code, missing_docs, unused_variables, unused_imports)] #![cfg_attr(not(feature = "std"), no_std)] +mod digest; + use parity_codec::{Encode, Decode}; use rstd::vec::Vec; use runtime_primitives::ConsensusEngineId; use substrate_primitives::sr25519::Public; use substrate_client::decl_runtime_apis; +#[cfg(feature = "std")] +pub use digest::{BabePreDigest, CompatibleDigestItem}; +pub use digest::{BABE_VRF_PREFIX, RawBabePreDigest}; + /// A Babe authority identifier. Necessarily equivalent to the schnorrkel public key used in /// the main Babe module. If that ever changes, then this must, too. pub type AuthorityId = Public; @@ -31,6 +38,52 @@ pub type AuthorityId = Public; /// The `ConsensusEngineId` of BABE. pub const BABE_ENGINE_ID: ConsensusEngineId = *b"BABE"; +/// The length of the VRF output +pub const VRF_OUTPUT_LENGTH: usize = 32; + +/// The length of the VRF proof +pub const VRF_PROOF_LENGTH: usize = 64; + +/// The length of the public key +pub const PUBLIC_KEY_LENGTH: usize = 32; + +/// The index of an authority. +pub type AuthorityIndex = u64; + +/// A slot number. +pub type SlotNumber = u64; + +/// The weight of an authority. +pub type Weight = u64; + +/// BABE epoch information +#[derive(Decode, Encode, Default, PartialEq, Eq, Clone)] +#[cfg_attr(any(feature = "std", test), derive(Debug))] +pub struct Epoch { + /// The authorities and their weights + pub authorities: Vec<(AuthorityId, Weight)>, + /// The epoch index + pub epoch_index: u64, + /// Randomness for this epoch + pub randomness: [u8; VRF_OUTPUT_LENGTH], + /// The duration of this epoch + pub duration: SlotNumber, +} + +/// An consensus log item for BABE. +#[derive(Decode, Encode, Clone, PartialEq, Eq)] +pub enum ConsensusLog { + /// The epoch has changed. This provides information about the + /// epoch _after_ next: what slot number it will start at, who are the authorities (and their weights) + /// and the next epoch randomness. The information for the _next_ epoch should already + /// be available. + #[codec(index = "1")] + NextEpochData(Epoch), + /// Disable the authority with given index. + #[codec(index = "2")] + OnDisabled(AuthorityIndex), +} + /// Configuration data used by the BABE consensus engine. #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug, Encode, Decode)] pub struct BabeConfiguration { @@ -40,6 +93,12 @@ pub struct BabeConfiguration { /// Dynamic slot duration may be supported in the future. pub slot_duration: u64, + /// The number of slots per BABE epoch. Currently, only + /// the value provided by this type at genesis will be used. + /// + /// Dynamic slot duration may be supported in the future. + pub slots_per_epoch: u64, + /// The expected block time in milliseconds for BABE. Currently, /// only the value provided by this type at genesis will be used. /// @@ -84,7 +143,7 @@ decl_runtime_apis! { /// Dynamic configuration may be supported in the future. fn startup_data() -> BabeConfiguration; - /// Get the current authorites for Babe. - fn authorities() -> Vec; + /// Get the current epoch data for Babe. + fn epoch() -> Epoch; } } diff --git a/core/consensus/babe/src/aux_schema.rs b/core/consensus/babe/src/aux_schema.rs new file mode 100644 index 0000000000000000000000000000000000000000..50d2a727f190fd6e0028505ad0ac62f548e5e685 --- /dev/null +++ b/core/consensus/babe/src/aux_schema.rs @@ -0,0 +1,69 @@ +// 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 . + +//! Schema for BABE epoch changes in the aux-db. + +use log::info; +use parity_codec::{Decode, Encode}; + +use client::backend::AuxStore; +use client::error::{Result as ClientResult, Error as ClientError}; +use runtime_primitives::traits::Block as BlockT; + +use super::{EpochChanges, SharedEpochChanges}; + +const BABE_EPOCH_CHANGES: &[u8] = b"babe_epoch_changes"; + +fn load_decode(backend: &B, key: &[u8]) -> ClientResult> + where + B: AuxStore, + T: Decode, +{ + let corrupt = || ClientError::Backend(format!("BABE DB is corrupted.")).into(); + match backend.get_aux(key)? { + None => Ok(None), + Some(t) => T::decode(&mut &t[..]).ok_or_else(corrupt).map(Some) + } +} + +/// Load or initialize persistent epoch change data from backend. +pub(crate) fn load_epoch_changes( + backend: &B, +) -> ClientResult> { + let epoch_changes = load_decode::<_, EpochChanges>(backend, BABE_EPOCH_CHANGES)? + .map(Into::into) + .unwrap_or_else(|| { + info!(target: "babe", + "Creating empty BABE epoch changes on what appears to be first startup." + ); + SharedEpochChanges::new() + }); + + Ok(epoch_changes) +} + +/// Update the epoch changes on disk after a change. +pub(crate) fn write_epoch_changes( + epoch_changes: &EpochChanges, + write_aux: F, +) -> R where + F: FnOnce(&[(&'static [u8], &[u8])]) -> R, +{ + let encoded_epoch_changes = epoch_changes.encode(); + write_aux( + &[(BABE_EPOCH_CHANGES, encoded_epoch_changes.as_slice())], + ) +} diff --git a/core/consensus/babe/src/lib.rs b/core/consensus/babe/src/lib.rs index 61850b0fc6c2d306f781ff24398b005dd625b946..276a1906d9776612cef69f58cdf2b42b6b19eaad 100644 --- a/core/consensus/babe/src/lib.rs +++ b/core/consensus/babe/src/lib.rs @@ -17,33 +17,25 @@ //! # BABE consensus //! //! BABE (Blind Assignment for Blockchain Extension) consensus in Substrate. -//! -//! # Stability -//! -//! This crate is highly unstable and experimental. Breaking changes may -//! happen at any point. This crate is also missing features, such as banning -//! of malicious validators, that are essential for a production network. -#![forbid(unsafe_code, missing_docs)] -extern crate core; -mod digest; -use digest::CompatibleDigestItem; -pub use digest::{BabePreDigest, BABE_VRF_PREFIX}; + +#![forbid(unsafe_code, missing_docs, unused_must_use, unused_imports, unused_variables)] +#![cfg_attr(not(test), forbid(dead_code))] pub use babe_primitives::*; pub use consensus_common::SyncOracle; +use consensus_common::ImportResult; use consensus_common::import_queue::{ - SharedBlockImport, SharedJustificationImport, SharedFinalityProofImport, - SharedFinalityProofRequestBuilder, + BoxJustificationImport, BoxFinalityProofImport, }; -use runtime_primitives::{generic, generic::{BlockId, OpaqueDigestItemId}, Justification}; +use runtime_primitives::{generic, generic::BlockId, Justification}; use runtime_primitives::traits::{ - Block, Header, DigestItemFor, ProvideRuntimeApi, + Block as BlockT, Header, DigestItemFor, NumberFor, ProvideRuntimeApi, SimpleBitOps, Zero, }; -use std::{sync::Arc, u64, fmt::{Debug, Display}, time::{Instant, Duration}}; +use std::{collections::HashMap, sync::Arc, u64, fmt::{Debug, Display}, time::{Instant, Duration}}; use runtime_support::serde::{Serialize, Deserialize}; use parity_codec::{Decode, Encode}; -use parking_lot::Mutex; -use primitives::{crypto::Pair, sr25519}; +use parking_lot::{Mutex, MutexGuard}; +use primitives::{Blake2Hasher, H256, Pair, Public, sr25519}; use merlin::Transcript; use inherents::{InherentDataProviders, InherentData}; use substrate_telemetry::{ @@ -61,7 +53,7 @@ use schnorrkel::{ }; use consensus_common::{ self, BlockImport, Environment, Proposer, - ForkChoiceStrategy, ImportBlock, BlockOrigin, Error as ConsensusError, + ForkChoiceStrategy, BlockImportParams, BlockOrigin, Error as ConsensusError, }; use srml_babe::{ BabeInherentData, @@ -71,19 +63,27 @@ use consensus_common::SelectChain; use consensus_common::import_queue::{Verifier, BasicQueue}; use client::{ block_builder::api::BlockBuilder as BlockBuilderApi, - blockchain::ProvideCache, + blockchain::{self, HeaderBackend, ProvideCache}, + BlockchainEvents, + CallExecutor, Client, runtime_api::ApiExt, - error::Result as CResult, - backend::AuxStore, + error::Result as ClientResult, + backend::{AuxStore, Backend}, + utils::is_descendent_of, well_known_cache_keys::{self, Id as CacheKeyId}, }; +use fork_tree::ForkTree; use slots::{CheckedHeader, check_equivocation}; -use futures::{Future, IntoFuture, future}; +use futures::{Future, IntoFuture, future, stream::Stream}; +use futures03::{StreamExt as _, TryStreamExt as _}; use tokio_timer::Timeout; use log::{error, warn, debug, info, trace}; use slots::{SlotWorker, SlotData, SlotInfo, SlotCompatible, SignedDuration}; +mod aux_schema; +#[cfg(test)] +mod tests; pub use babe_primitives::AuthorityId; /// A slot duration. Create with `get_or_compute`. @@ -95,9 +95,9 @@ pub struct Config(slots::SlotDuration); impl Config { /// Either fetch the slot duration from disk or compute it from the genesis /// state. - pub fn get_or_compute(client: &C) -> CResult + pub fn get_or_compute(client: &C) -> ClientResult where - C: AuxStore, C: ProvideRuntimeApi, C::Api: BabeApi, + C: AuxStore + ProvideRuntimeApi, C::Api: BabeApi, { trace!(target: "babe", "Getting slot duration"); match slots::SlotDuration::get_or_compute(client, |a, b| a.startup_data(b)).map(Self) { @@ -120,24 +120,24 @@ impl Config { } } -struct BabeSlotCompatible; - -impl SlotCompatible for BabeSlotCompatible { +impl SlotCompatible for BabeLink { fn extract_timestamp_and_slot( - data: &InherentData - ) -> Result<(TimestampInherent, u64), consensus_common::Error> { + &self, + data: &InherentData, + ) -> Result<(TimestampInherent, u64, std::time::Duration), consensus_common::Error> { trace!(target: "babe", "extract timestamp"); data.timestamp_inherent_data() .and_then(|t| data.babe_inherent_data().map(|a| (t, a))) .map_err(Into::into) .map_err(consensus_common::Error::InherentData) + .map(|(x, y)| (x, y, self.0.lock().0.take().unwrap_or_default())) } } /// Parameters for BABE. pub struct BabeParams { - /// The configuration for BABE. Includes the slot duration, threshold, and + /// The configuration for BABE. Includes the slot duration, threshold, and /// other parameters. pub config: Config, @@ -151,7 +151,7 @@ pub struct BabeParams { pub select_chain: SC, /// A block importer - pub block_import: Arc, + pub block_import: I, /// The environment pub env: Arc, @@ -164,6 +164,9 @@ pub struct BabeParams { /// Force authoring of blocks even if we are offline pub force_authoring: bool, + + /// The source of timestamps for relative slots + pub time_source: BabeLink, } /// Start the babe worker. The returned future should be run in a tokio runtime. @@ -177,11 +180,12 @@ pub fn start_babe(BabeParams { sync_oracle, inherent_data_providers, force_authoring, + time_source, }: BabeParams) -> Result< impl Future, consensus_common::Error, > where - B: Block, + B: BlockT, C: ProvideRuntimeApi + ProvideCache, C::Api: BabeApi, SC: SelectChain, @@ -195,7 +199,7 @@ pub fn start_babe(BabeParams { { let worker = BabeWorker { client: client.clone(), - block_import, + block_import: Arc::new(Mutex::new(block_import)), env, local_key, sync_oracle: sync_oracle.clone(), @@ -203,18 +207,19 @@ pub fn start_babe(BabeParams { threshold: config.threshold(), }; register_babe_inherent_data_provider(&inherent_data_providers, config.0.slot_duration())?; - Ok(slots::start_slot_worker::<_, _, _, _, _, BabeSlotCompatible>( + Ok(slots::start_slot_worker( config.0, select_chain, worker, sync_oracle, - inherent_data_providers + inherent_data_providers, + time_source, )) } struct BabeWorker { client: Arc, - block_import: Arc, + block_import: Arc>, env: Arc, local_key: Arc, sync_oracle: SO, @@ -223,7 +228,7 @@ struct BabeWorker { } impl SlotWorker for BabeWorker where - B: Block, + B: BlockT, C: ProvideRuntimeApi + ProvideCache, C::Api: BabeApi, E: Environment, @@ -249,10 +254,10 @@ impl SlotWorker for BabeWorker w let block_import = self.block_import.clone(); let ref env = self.env; - let (timestamp, slot_num, slot_duration) = + let (timestamp, slot_number, slot_duration) = (slot_info.timestamp, slot_info.number, slot_info.duration); - let authorities = match authorities(client.as_ref(), &BlockId::Hash(chain_head.hash())) { + let epoch = match epoch(client.as_ref(), &BlockId::Hash(chain_head.hash())) { Ok(authorities) => authorities, Err(e) => { error!( @@ -268,6 +273,12 @@ impl SlotWorker for BabeWorker w } }; + let Epoch { ref authorities, randomness, epoch_index, .. } = epoch; + + if authorities.is_empty() { + error!(target: "babe", "No authorities at block {:?}", chain_head.hash()); + } + if !self.force_authoring && self.sync_oracle.is_offline() && authorities.len() > 1 { debug!(target: "babe", "Skipping proposal slot. Waiting for the network."); telemetry!(CONSENSUS_DEBUG; "babe.skipping_proposal_slot"; @@ -276,44 +287,40 @@ impl SlotWorker for BabeWorker w return Box::new(future::ok(())); } - // FIXME replace the dummy empty slices with real data - // https://github.com/paritytech/substrate/issues/2435 - // https://github.com/paritytech/substrate/issues/2436 - let proposal_work = if let Some((inout, proof, _batchable_proof)) = claim_slot( - &[0u8; 0], + let proposal_work = if let Some(((inout, vrf_proof, _batchable_proof), authority_index)) = claim_slot( + &randomness, slot_info.number, - &[0u8; 0], - 0, - &authorities, + epoch_index, + epoch, &pair, self.threshold, ) { debug!( target: "babe", "Starting authorship at slot {}; timestamp = {}", - slot_num, + slot_number, timestamp, ); telemetry!(CONSENSUS_DEBUG; "babe.starting_authorship"; - "slot_num" => slot_num, "timestamp" => timestamp + "slot_number" => slot_number, "timestamp" => timestamp ); // we are the slot author. make a block and sign it. let proposer = match env.init(&chain_head) { Ok(p) => p, Err(e) => { - warn!(target: "babe", "Unable to author block in slot {:?}: {:?}", slot_num, e); + warn!(target: "babe", "Unable to author block in slot {:?}: {:?}", slot_number, e); telemetry!(CONSENSUS_WARN; "babe.unable_authoring_block"; - "slot" => slot_num, "err" => ?e + "slot" => slot_number, "err" => ?e ); return Box::new(future::ok(())) } }; let inherent_digest = BabePreDigest { - proof, + vrf_proof, vrf_output: inout.to_output(), - author: pair.public(), - slot_num, + authority_index: authority_index as u64, + slot_number, }; // deadline our production to approx. the end of the slot @@ -338,27 +345,19 @@ impl SlotWorker for BabeWorker w // minor hack since we don't have access to the timestamp // that is actually set by the proposer. let slot_after_building = SignedDuration::default().slot_now(slot_duration); - if slot_after_building != slot_num { + if slot_after_building != slot_number { info!( target: "babe", "Discarding proposal for slot {}; block production took too long", - slot_num + slot_number ); telemetry!(CONSENSUS_INFO; "babe.discarding_proposal_took_too_long"; - "slot" => slot_num + "slot" => slot_number ); - return + return; } let (header, body) = b.deconstruct(); - let pre_digest: Result = find_pre_digest::(&header); - if let Err(e) = pre_digest { - error!(target: "babe", "FATAL ERROR: Invalid pre-digest: {}!", e); - return - } else { - trace!(target: "babe", "Got correct number of seals. Good!") - }; - let header_num = header.number().clone(); let parent_hash = header.parent_hash().clone(); @@ -368,7 +367,12 @@ impl SlotWorker for BabeWorker w let signature = pair.sign(header_hash.as_ref()); let signature_digest_item = DigestItemFor::::babe_seal(signature); - let import_block: ImportBlock = ImportBlock { + let cache = find_epoch_digest::(&header) + .map(|epoch| vec![(well_known_cache_keys::AUTHORITIES, epoch.encode())]) + .map(|keys| keys.into_iter().collect()) + .unwrap_or_default(); + + let import_block = BlockImportParams:: { origin: BlockOrigin::Own, header, justification: None, @@ -385,13 +389,14 @@ impl SlotWorker for BabeWorker w import_block.post_header().hash(), header_hash, ); + telemetry!(CONSENSUS_INFO; "babe.pre_sealed_block"; "header_num" => ?header_num, "hash_now" => ?import_block.post_header().hash(), "hash_previously" => ?header_hash, ); - if let Err(e) = block_import.import_block(import_block, Default::default()) { + if let Err(e) = block_import.lock().import_block(import_block, cache) { warn!(target: "babe", "Error with block built on {:?}: {:?}", parent_hash, e); telemetry!(CONSENSUS_WARN; "babe.err_with_block_built_on"; @@ -413,22 +418,34 @@ macro_rules! babe_err { }; } -fn find_pre_digest(header: &B::Header) -> Result +/// Extract the BABE pre digest from the given header. Pre-runtime digests are +/// mandatory, the function will return `Err` if none is found. +fn find_pre_digest(header: &B::Header) -> Result where DigestItemFor: CompatibleDigestItem, { - let mut pre_digest: Option<_> = None; for log in header.digest().logs() { - trace!(target: "babe", "Checking log {:?}", log); - match (log.as_babe_pre_digest(), pre_digest.is_some()) { - (Some(_), true) => Err(babe_err!("Multiple BABE pre-runtime headers, rejecting!"))?, - (None, _) => trace!(target: "babe", "Ignoring digest not meant for us"), - (s, false) => pre_digest = s, + if let Some(pre_digest) = log.as_babe_pre_digest() { + return Ok(pre_digest); } } - pre_digest.ok_or_else(|| babe_err!("No BABE pre-runtime digest found")) + + Err(babe_err!("No BABE pre-runtime digest found")) } -/// check a header has been signed by the right key. If the slot is too far in +/// Extract the BABE epoch change digest from the given header, if it exists. +fn find_epoch_digest(header: &B::Header) -> Option + where DigestItemFor: CompatibleDigestItem, +{ + for log in header.digest().logs() { + if let Some(epoch_digest) = log.as_babe_epoch() { + return Some(epoch_digest); + } + } + + return None; +} + +/// 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. /// @@ -436,14 +453,15 @@ fn find_pre_digest(header: &B::Header) -> Result( +fn check_header( client: &C, slot_now: u64, mut header: B::Header, hash: B::Hash, authorities: &[AuthorityId], + randomness: [u8; 32], + epoch_index: u64, threshold: u64, ) -> Result, DigestItemFor)>, String> where DigestItemFor: CompatibleDigestItem, @@ -459,26 +477,27 @@ fn check_header( })?; let pre_digest = find_pre_digest::(&header)?; - let BabePreDigest { slot_num, ref author, ref proof, ref vrf_output } = pre_digest; - if slot_num > slot_now { + let BabePreDigest { slot_number, authority_index, ref vrf_proof, ref vrf_output } = pre_digest; + + if slot_number > slot_now { header.digest_mut().push(seal); - Ok(CheckedHeader::Deferred(header, slot_num)) - } else if !authorities.contains(&author) { + Ok(CheckedHeader::Deferred(header, slot_number)) + } else if authority_index > authorities.len() as u64 { Err(babe_err!("Slot author not found")) } else { - let pre_hash = header.hash(); + let (pre_hash, author) = (header.hash(), &authorities[authority_index as usize]); - if sr25519::Pair::verify(&sig, pre_hash, author) { + if sr25519::Pair::verify(&sig, pre_hash, author.clone()) { let (inout, _batchable_proof) = { let transcript = make_transcript( - Default::default(), - slot_num, - Default::default(), - 0, + &randomness, + slot_number, + epoch_index, ); + schnorrkel::PublicKey::from_bytes(author.as_slice()).and_then(|p| { - p.vrf_verify(transcript, vrf_output, proof) + p.vrf_verify(transcript, vrf_output, vrf_proof) }).map_err(|s| { babe_err!("VRF verification failed: {:?}", s) })? @@ -492,14 +511,14 @@ fn check_header( if let Some(equivocation_proof) = check_equivocation( client, slot_now, - slot_num, + slot_number, &header, author, ).map_err(|e| e.to_string())? { info!( "Slot author {:?} is equivocating at slot {} with headers {:?} and {:?}", author, - slot_num, + slot_number, equivocation_proof.fst_header().hash(), equivocation_proof.snd_header().hash(), ); @@ -513,16 +532,20 @@ fn check_header( } } +/// State that must be shared between the import queue and the authoring logic. +#[derive(Default, Clone, Debug)] +pub struct BabeLink(Arc, Vec<(Instant, u64)>)>>); + /// A verifier for Babe blocks. pub struct BabeVerifier { - client: Arc, + api: Arc, inherent_data_providers: inherents::InherentDataProviders, config: Config, - timestamps: Mutex<(Option, Vec<(Instant, u64)>)>, + time_source: BabeLink, } impl BabeVerifier { - fn check_inherents( + fn check_inherents( &self, block: B, block_id: BlockId, @@ -530,7 +553,7 @@ impl BabeVerifier { ) -> Result<(), String> where C: ProvideRuntimeApi, C::Api: BlockBuilderApi { - let inherent_res = self.client.runtime_api().check_inherents( + let inherent_res = self.api.runtime_api().check_inherents( &block_id, block, inherent_data, @@ -549,39 +572,42 @@ impl BabeVerifier { fn median_algorithm( median_required_blocks: u64, slot_duration: u64, - slot_num: u64, + slot_number: u64, slot_now: u64, - timestamps: &mut (Option, Vec<(Instant, u64)>), + time_source: &mut (Option, Vec<(Instant, u64)>), ) { - let num_timestamps = timestamps.1.len(); + let num_timestamps = time_source.1.len(); if num_timestamps as u64 >= median_required_blocks && median_required_blocks > 0 { - let mut new_list: Vec<_> = timestamps.1.iter().map(|&(t, sl)| { - let offset: u128 = u128::from(slot_duration) - .checked_mul(1_000_000u128) // self.config.get() returns *milliseconds* - .and_then(|x| x.checked_mul(u128::from(slot_num).saturating_sub(u128::from(sl)))) - .expect("we cannot have timespans long enough for this to overflow; qed"); - const NANOS_PER_SEC: u32 = 1_000_000_000; - let nanos = (offset % u128::from(NANOS_PER_SEC)) as u32; - let secs = (offset / u128::from(NANOS_PER_SEC)) as u64; - t + Duration::new(secs, nanos) - }).collect(); + let mut new_list: Vec<_> = time_source.1.iter().map(|&(t, sl)| { + let offset: u128 = u128::from(slot_duration) + .checked_mul(1_000_000u128) // self.config.get() returns *milliseconds* + .and_then(|x| x.checked_mul(u128::from(slot_number).saturating_sub(u128::from(sl)))) + .expect("we cannot have timespans long enough for this to overflow; qed"); + + const NANOS_PER_SEC: u32 = 1_000_000_000; + let nanos = (offset % u128::from(NANOS_PER_SEC)) as u32; + let secs = (offset / u128::from(NANOS_PER_SEC)) as u64; + + t + Duration::new(secs, nanos) + }).collect(); + // FIXME #2926: use a selection algorithm instead of a full sorting algorithm. new_list.sort_unstable(); + let &median = new_list .get(num_timestamps / 2) .expect("we have at least one timestamp, so this is a valid index; qed"); - timestamps.1.clear(); - // FIXME #2927: pass this to the block authoring logic somehow - timestamps.0.replace(Instant::now() - median); + + time_source.1.clear(); + time_source.0.replace(Instant::now() - median); } else { - timestamps.1.push((Instant::now(), slot_now)) + time_source.1.push((Instant::now(), slot_now)) } } -impl Verifier for BabeVerifier where +impl Verifier for BabeVerifier where C: ProvideRuntimeApi + Send + Sync + AuxStore + ProvideCache, C::Api: BlockBuilderApi + BabeApi, - DigestItemFor: CompatibleDigestItem, { fn verify( &self, @@ -589,7 +615,7 @@ impl Verifier for BabeVerifier where header: B::Header, justification: Option, mut body: Option>, - ) -> Result<(ImportBlock, Option)>>), String> { + ) -> Result<(BlockImportParams, Option)>>), String> { trace!( target: "babe", "Verifying origin: {:?} header: {:?} justification: {:?} body: {:?}", @@ -604,34 +630,41 @@ impl Verifier for BabeVerifier where .inherent_data_providers .create_inherent_data() .map_err(String::from)?; - let (_, slot_now) = BabeSlotCompatible::extract_timestamp_and_slot(&inherent_data) + + let (_, slot_now, _) = self.time_source.extract_timestamp_and_slot(&inherent_data) .map_err(|e| format!("Could not extract timestamp and slot: {:?}", e))?; + let hash = header.hash(); let parent_hash = *header.parent_hash(); - let authorities = authorities(self.client.as_ref(), &BlockId::Hash(parent_hash)) - .map_err(|e| format!("Could not fetch authorities at {:?}: {:?}", parent_hash, e))?; + let Epoch { authorities, randomness, epoch_index, .. } = + epoch(self.api.as_ref(), &BlockId::Hash(parent_hash)) + .map_err(|e| format!("Could not fetch epoch at {:?}: {:?}", parent_hash, e))?; + + let authorities: Vec<_> = authorities.into_iter().map(|(s, _)| s).collect(); - // we add one to allow for some small drift. - // FIXME #1019 in the future, alter this queue to allow deferring of - // headers + // We add one to allow for some small drift. + // FIXME #1019 in the future, alter this queue to allow deferring of headers let checked_header = check_header::( - &self.client, + &self.api, slot_now + 1, header, hash, &authorities[..], + randomness, + epoch_index, self.config.threshold(), )?; + match checked_header { CheckedHeader::Checked(pre_header, (pre_digest, seal)) => { - let BabePreDigest { slot_num, .. } = pre_digest.as_babe_pre_digest() + let BabePreDigest { slot_number, .. } = pre_digest.as_babe_pre_digest() .expect("check_header always returns a pre-digest digest item; qed"); // if the body is passed through, we need to use the runtime // to check that the internally-set timestamp in the inherents // actually matches the slot set in the seal. if let Some(inner_body) = body.take() { - inherent_data.babe_replace_inherent_data(slot_num); + inherent_data.babe_replace_inherent_data(slot_number); let block = B::new(pre_header.clone(), inner_body); self.check_inherents( @@ -651,13 +684,12 @@ impl Verifier for BabeVerifier where "pre_header" => ?pre_header); // `Consensus` is the Babe-specific authorities change log. - // It's an encoded `Vec`, the same format as is stored in the cache, - // so no need to decode/re-encode. - let maybe_keys = pre_header.digest() - .log(|l| l.try_as_raw(OpaqueDigestItemId::Consensus(&BABE_ENGINE_ID))) - .map(|blob| vec![(well_known_cache_keys::AUTHORITIES, blob.to_vec())]); + // It's an encoded `Epoch`, the same format as is stored in the + // cache, so no need to decode/re-encode. + let maybe_keys = find_epoch_digest::(&pre_header) + .map(|epoch| vec![(well_known_cache_keys::AUTHORITIES, epoch.encode())]); - let import_block = ImportBlock { + let import_block = BlockImportParams { origin, header: pre_header, post_digests: vec![seal], @@ -667,13 +699,16 @@ impl Verifier for BabeVerifier where auxiliary: Vec::new(), fork_choice: ForkChoiceStrategy::LongestChain, }; + + // FIXME: this should eventually be moved to BabeBlockImport median_algorithm( self.config.0.median_required_blocks, self.config.get(), - slot_num, + slot_number, slot_now, - &mut *self.timestamps.lock(), + &mut *self.time_source.0.lock(), ); + // FIXME #1019 extract authorities Ok((import_block, maybe_keys)) } @@ -688,11 +723,10 @@ impl Verifier for BabeVerifier where } } -fn authorities(client: &C, at: &BlockId) -> Result< - Vec, - ConsensusError, -> where - B: Block, +/// Extract current epoch data from cache and fallback to querying the runtime +/// if the cache isn't populated. +fn epoch(client: &C, at: &BlockId) -> Result where + B: BlockT, C: ProvideRuntimeApi + ProvideCache, C::Api: BabeApi, { @@ -702,10 +736,16 @@ fn authorities(client: &C, at: &BlockId) -> Result< .and_then(|(_, _, v)| Decode::decode(&mut &v[..]))) .or_else(|| { if client.runtime_api().has_api::>(at).unwrap_or(false) { - BabeApi::authorities(&*client.runtime_api(), at).ok() + let s = BabeApi::epoch(&*client.runtime_api(), at).ok()?; + if s.authorities.is_empty() { + error!("No authorities!"); + None + } else { + Some(s) + } } else { - panic!("We don’t support deprecated code with new consensus algorithms, \ - therefore this is unreachable; qed") + error!("bad api!"); + None } }).ok_or(consensus_common::Error::InvalidAuthoritiesSet) } @@ -737,12 +777,10 @@ fn get_keypair(q: &sr25519::Pair) -> &Keypair { fn make_transcript( randomness: &[u8], slot_number: u64, - genesis_hash: &[u8], epoch: u64, ) -> Transcript { let mut transcript = Transcript::new(&BABE_ENGINE_ID); transcript.commit_bytes(b"slot number", &slot_number.to_le_bytes()); - transcript.commit_bytes(b"genesis block hash", genesis_hash); transcript.commit_bytes(b"current epoch", &epoch.to_le_bytes()); transcript.commit_bytes(b"chain randomness", randomness); transcript @@ -760,31 +798,28 @@ fn check(inout: &VRFInOut, threshold: u64) -> bool { fn claim_slot( randomness: &[u8], slot_number: u64, - genesis_hash: &[u8], epoch: u64, - authorities: &[AuthorityId], + Epoch { ref authorities, .. }: Epoch, key: &sr25519::Pair, threshold: u64, -) -> Option<(VRFInOut, VRFProof, VRFProofBatchable)> { - if !authorities.contains(&key.public()) { return None } - let transcript = make_transcript( - randomness, - slot_number, - genesis_hash, - epoch, - ); +) -> Option<((VRFInOut, VRFProof, VRFProofBatchable), usize)> { + let public = &key.public(); + let authority_index = authorities.iter().position(|s| &s.0 == public)?; + let transcript = make_transcript(randomness, slot_number, epoch); // Compute the threshold we will use. // - // We already checked that authorities contains `key.public()`, so it can’t + // We already checked that authorities contains `key.public()`, so it can't // be empty. Therefore, this division is safe. let threshold = threshold / authorities.len() as u64; - get_keypair(key).vrf_sign_n_check(transcript, |inout| check(inout, threshold)) + get_keypair(key) + .vrf_sign_n_check(transcript, |inout| check(inout, threshold)) + .map(|s|(s, authority_index)) } fn initialize_authorities_cache(client: &C) -> Result<(), ConsensusError> where - B: Block, + B: BlockT, C: ProvideRuntimeApi + ProvideCache, C::Api: BabeApi, { @@ -796,10 +831,10 @@ fn initialize_authorities_cache(client: &C) -> Result<(), ConsensusError> // check if we already have initialized the cache let genesis_id = BlockId::Number(Zero::zero()); - let genesis_authorities: Option> = cache + let genesis_epoch: Option = cache .get_at(&well_known_cache_keys::AUTHORITIES, &genesis_id) .and_then(|(_, _, v)| Decode::decode(&mut &v[..])); - if genesis_authorities.is_some() { + if genesis_epoch.is_some() { return Ok(()); } @@ -808,311 +843,298 @@ fn initialize_authorities_cache(client: &C) -> Result<(), ConsensusError> "Error initializing authorities cache: {}", error, ))); - let genesis_authorities = authorities(client, &genesis_id)?; - cache.initialize(&well_known_cache_keys::AUTHORITIES, genesis_authorities.encode()) + + let genesis_epoch = epoch(client, &genesis_id)?; + cache.initialize(&well_known_cache_keys::AUTHORITIES, genesis_epoch.encode()) .map_err(map_err) } -/// Start an import queue for the Babe consensus algorithm. -pub fn import_queue( - config: Config, - block_import: SharedBlockImport, - justification_import: Option>, - finality_proof_import: Option>, - finality_proof_request_builder: Option>, - client: Arc, - inherent_data_providers: InherentDataProviders, -) -> Result, consensus_common::Error> where - B: Block, - C: 'static + ProvideRuntimeApi + ProvideCache + Send + Sync + AuxStore, - C::Api: BlockBuilderApi + BabeApi, - DigestItemFor: CompatibleDigestItem, - E: 'static, -{ - register_babe_inherent_data_provider(&inherent_data_providers, config.get())?; - initialize_authorities_cache(&*client)?; - - let verifier = Arc::new( - BabeVerifier { - client: client, - inherent_data_providers, - timestamps: Default::default(), - config, - } - ); - Ok(BasicQueue::new( - verifier, - block_import, - justification_import, - finality_proof_import, - finality_proof_request_builder, - )) +/// Tree of all epoch changes across all *seen* forks. Data stored in tree is the +/// hash and block number of the block signaling the epoch change, the new epoch +/// index and the minimum *slot number* when the next epoch should start (i.e. +/// slot number begin + duration). +type EpochChanges = ForkTree< + ::Hash, + NumberFor, + (u64, SlotNumber), +>; + +/// A shared epoch changes tree. +#[derive(Clone)] +struct SharedEpochChanges { + inner: Arc>>, } -#[cfg(test)] -#[allow(dead_code, unused_imports, deprecated)] -// FIXME #2532: need to allow deprecated until refactor is done -// https://github.com/paritytech/substrate/issues/2532 - -mod tests { - use super::*; - - use client::LongestChain; - use consensus_common::NoNetwork as DummyOracle; - use network::test::*; - use network::test::{Block as TestBlock, PeersClient}; - use runtime_primitives::traits::{Block as BlockT, DigestFor}; - use network::config::ProtocolConfig; - use tokio::runtime::current_thread; - use keyring::sr25519::Keyring; - use super::generic::DigestItem; - use client::BlockchainEvents; - use test_client; - use futures::stream::Stream; - use log::debug; - use std::time::Duration; - type Item = generic::DigestItem; - use test_client::AuthorityKeyring; - - type Error = client::error::Error; - - type TestClient = client::Client< - test_client::Backend, - test_client::Executor, - TestBlock, - test_client::runtime::RuntimeApi, - >; - - struct DummyFactory(Arc); - struct DummyProposer(u64, Arc); - - impl Environment for DummyFactory { - type Proposer = DummyProposer; - type Error = Error; - - fn init(&self, parent_header: &::Header) - -> Result - { - Ok(DummyProposer(parent_header.number + 1, self.0.clone())) +impl SharedEpochChanges { + fn new() -> Self { + SharedEpochChanges { + inner: Arc::new(Mutex::new(EpochChanges::::new())) } } - impl Proposer for DummyProposer { - type Error = Error; - type Create = Result; + fn lock(&self) -> MutexGuard> { + self.inner.lock() + } +} - fn propose(&self, _: InherentData, digests: DigestFor, _: Duration) -> Result { - self.1.new_block(digests).unwrap().bake().map_err(|e| e.into()) +impl From> for SharedEpochChanges { + fn from(epoch_changes: EpochChanges) -> Self { + SharedEpochChanges { + inner: Arc::new(Mutex::new(epoch_changes)) } } +} - const SLOT_DURATION: u64 = 1; - const TEST_ROUTING_INTERVAL: Duration = Duration::from_millis(50); +/// A block-import handler for BABE. +/// +/// This scans each imported block for epoch change signals. The signals are +/// tracked in a tree (of all forks), and the import logic validates all epoch +/// change transitions, i.e. whether a given epoch change is expected or whether +/// it is missing. +/// +/// The epoch change tree should be pruned as blocks are finalized. +pub struct BabeBlockImport { + inner: I, + client: Arc>, + epoch_changes: SharedEpochChanges, +} - pub struct BabeTestNet { - peers: Vec>>, - started: bool, +impl Clone for BabeBlockImport { + fn clone(&self) -> Self { + BabeBlockImport { + inner: self.inner.clone(), + client: self.client.clone(), + epoch_changes: self.epoch_changes.clone(), + } } +} - impl TestNetFactory for BabeTestNet { - type Specialization = DummySpecialization; - type Verifier = BabeVerifier; - type PeerData = (); - - /// Create new test network with peers and given config. - fn from_config(_config: &ProtocolConfig) -> Self { - debug!(target: "babe", "Creating test network from config"); - BabeTestNet { - peers: Vec::new(), - started: false, - } +impl BabeBlockImport { + fn new( + client: Arc>, + epoch_changes: SharedEpochChanges, + block_import: I, + ) -> Self { + BabeBlockImport { + client, + inner: block_import, + epoch_changes, } + } +} - fn make_verifier(&self, client: PeersClient, _cfg: &ProtocolConfig) - -> Arc - { - let client = client.as_full().expect("only full clients are used in test"); - trace!(target: "babe", "Creating a verifier"); - let config = Config::get_or_compute(&*client) - .expect("slot duration available"); - let inherent_data_providers = InherentDataProviders::new(); - register_babe_inherent_data_provider( - &inherent_data_providers, - config.get() - ).expect("Registers babe inherent data provider"); - trace!(target: "babe", "Provider registered"); - - assert_eq!(config.get(), SLOT_DURATION); - Arc::new(BabeVerifier { - client, - inherent_data_providers, - config, - timestamps: Default::default(), - }) +impl BlockImport for BabeBlockImport where + Block: BlockT, + I: BlockImport + Send + Sync, + I::Error: Into, + B: Backend + 'static, + E: CallExecutor + 'static + Clone + Send + Sync, + RA: Send + Sync, +{ + type Error = ConsensusError; + + fn import_block( + &mut self, + mut block: BlockImportParams, + new_cache: HashMap>, + ) -> Result { + let hash = block.post_header().hash(); + let number = block.header.number().clone(); + + // early exit if block already in chain, otherwise the check for + // epoch changes will error when trying to re-import an epoch change + #[allow(deprecated)] + match self.client.backend().blockchain().status(BlockId::Hash(hash)) { + Ok(blockchain::BlockStatus::InChain) => return Ok(ImportResult::AlreadyInChain), + Ok(blockchain::BlockStatus::Unknown) => {}, + Err(e) => return Err(ConsensusError::ClientImport(e.to_string()).into()), } - fn uses_tokio(&self) -> bool { - true - } + let slot_number = { + let pre_digest = find_pre_digest::(&block.header) + .expect("valid babe headers must contain a predigest; \ + header has been already verified; qed"); + let BabePreDigest { slot_number, .. } = pre_digest; + slot_number + }; - fn peer(&self, i: usize) -> &Peer { - trace!(target: "babe", "Retreiving a peer"); - &self.peers[i] - } + // returns a function for checking whether a block is a descendent of another + // consistent with querying client directly after importing the block. + let parent_hash = *block.header.parent_hash(); + let is_descendent_of = is_descendent_of(&self.client, Some((&hash, &parent_hash))); + + // check if there's any epoch change expected to happen at this slot + let mut epoch_changes = self.epoch_changes.lock(); + let epoch_change = epoch_changes.find_node_where( + &hash, + &number, + &is_descendent_of, + &|(_, expected_epoch_change_slot)| { + *expected_epoch_change_slot <= slot_number + } + ).map_err(|e| ConsensusError::from(ConsensusError::ClientImport(e.to_string())))?; - fn peers(&self) -> &Vec>> { - trace!(target: "babe", "Retreiving peers"); - &self.peers - } + let check_roots = || -> Result { + // this can only happen when the chain starts, since there's no epoch change at genesis. + // afterwards every time we expect an epoch change it means we will import another one. + for (root, _, _) in epoch_changes.roots() { + let is_descendent_of = is_descendent_of(root, &hash) + .map_err(|e| ConsensusError::from(ConsensusError::ClientImport(e.to_string())))?; - fn mut_peers>>)>( - &mut self, - closure: F, - ) { - closure(&mut self.peers); - } + if is_descendent_of { + return Ok(false); + } + } - fn started(&self) -> bool { - self.started - } + Ok(true) + }; - fn set_started(&mut self, new: bool) { - self.started = new; + let expected_epoch_change = epoch_change.is_some(); + + match (expected_epoch_change, new_cache.contains_key(&well_known_cache_keys::AUTHORITIES)) { + (true, true) => {}, + (false, false) => {}, + (true, false) => { + return Err( + ConsensusError::ClientImport("Expected epoch change to happen by this block".into()) + ); + }, + (false, true) => { + if !check_roots()? { + return Err(ConsensusError::ClientImport("Unexpected epoch change".into())); + } + }, } - } - #[test] - fn can_serialize_block() { - drop(env_logger::try_init()); - assert!(BabePreDigest::decode(&mut &b""[..]).is_none()); - } + // if there's a pending epoch we'll save the previous epoch changes here + // this way we can revert it if there's any error + let mut old_epoch_changes = None; + + if let Some(entry) = new_cache.get(&well_known_cache_keys::AUTHORITIES) { + if let Some(epoch) = Epoch::decode(&mut &entry[..]) { + if let Some(last_epoch_change) = epoch_change { + let last_epoch_index = last_epoch_change.data.0; + if epoch.epoch_index.checked_sub(last_epoch_index) != Some(1) { + return Err(ConsensusError::ClientImport(format!( + "Invalid BABE epoch change: expected next epoch to be {:?}, got {:?}", + last_epoch_index.saturating_add(1), + epoch.epoch_index, + ))); + } + } - #[test] - fn authoring_blocks() { - drop(env_logger::try_init()); - debug!(target: "babe", "checkpoint 1"); - let mut net = BabeTestNet::new(3); - debug!(target: "babe", "checkpoint 2"); - - net.start(); - debug!(target: "babe", "checkpoint 3"); - - let peers = &[ - (0, Keyring::Alice), - (1, Keyring::Bob), - (2, Keyring::Charlie), - ]; - - let net = Arc::new(Mutex::new(net)); - let mut import_notifications = Vec::new(); - debug!(target: "babe", "checkpoint 4"); - let mut runtime = current_thread::Runtime::new().unwrap(); - for (peer_id, key) in peers { - let client = net.lock().peer(*peer_id).client().as_full().unwrap(); - let environ = Arc::new(DummyFactory(client.clone())); - import_notifications.push( - client.import_notification_stream() - .take_while(|n| Ok(!(n.origin != BlockOrigin::Own && n.header.number() < &5))) - .for_each(move |_| Ok(())) - ); + old_epoch_changes = Some(epoch_changes.clone()); + + // track the epoch change in the fork tree + epoch_changes.import( + hash, + number, + (epoch.epoch_index, slot_number + epoch.duration), + &is_descendent_of, + ).map_err(|e| ConsensusError::from(ConsensusError::ClientImport(e.to_string())))?; + + crate::aux_schema::write_epoch_changes::( + &*epoch_changes, + |insert| block.auxiliary.extend( + insert.iter().map(|(k, v)| (k.to_vec(), Some(v.to_vec()))) + ) + ); + } else { + return Err( + ConsensusError::ClientImport("Failed to decode epoch change digest".into()) + ); + } + } - let config = Config::get_or_compute(&*client) - .expect("slot duration available"); + let import_result = self.inner.import_block(block, new_cache); - let inherent_data_providers = InherentDataProviders::new(); - register_babe_inherent_data_provider( - &inherent_data_providers, config.get() - ).expect("Registers babe inherent data provider"); + // revert to the original epoch changes in case there's an error + // importing the block + if let Err(_) = import_result { + if let Some(old_epoch_changes) = old_epoch_changes { + *epoch_changes = old_epoch_changes; + } + } + import_result.map_err(Into::into) + } - #[allow(deprecated)] - let select_chain = LongestChain::new(client.backend().clone()); + fn check_block( + &mut self, + hash: Block::Hash, + parent_hash: Block::Hash, + ) -> Result { + self.inner.check_block(hash, parent_hash).map_err(Into::into) + } +} - let babe = start_babe(BabeParams { - config, - local_key: Arc::new(key.clone().into()), - block_import: client.clone(), - select_chain, - client, - env: environ.clone(), - sync_oracle: DummyOracle, - inherent_data_providers, - force_authoring: false, - }).expect("Starts babe"); +/// Start an import queue for the BABE consensus algorithm. This method returns +/// the import queue, some data that needs to be passed to the block authoring +/// logic (`BabeLink`), a `BabeBlockImport` which should be used by the +/// authoring when importing its own blocks, and a future that must be run to +/// completion and is responsible for listening to finality notifications and +/// pruning the epoch changes tree. +pub fn import_queue, I, RA, PRA>( + config: Config, + block_import: I, + justification_import: Option>, + finality_proof_import: Option>, + client: Arc>, + api: Arc, + inherent_data_providers: InherentDataProviders, +) -> ClientResult<( + BabeImportQueue, + BabeLink, + BabeBlockImport, + impl Future, +)> where + B: Backend + 'static, + I: BlockImport + Clone + Send + Sync + 'static, + I::Error: Into, + E: CallExecutor + Clone + Send + Sync + 'static, + RA: Send + Sync + 'static, + PRA: ProvideRuntimeApi + ProvideCache + Send + Sync + AuxStore + 'static, + PRA::Api: BlockBuilderApi + BabeApi, +{ + register_babe_inherent_data_provider(&inherent_data_providers, config.get())?; + initialize_authorities_cache(&*api)?; - runtime.spawn(babe); - } - debug!(target: "babe", "checkpoint 5"); - - // wait for all finalized on each. - let wait_for = ::futures::future::join_all(import_notifications) - .map(drop) - .map_err(drop); - - let drive_to_completion = ::tokio_timer::Interval::new_interval(TEST_ROUTING_INTERVAL) - .for_each(move |_| { - net.lock().send_import_notifications(); - net.lock().sync_without_disconnects(); - Ok(()) - }) - .map(drop) - .map_err(drop); - - let _ = runtime.block_on(wait_for.select(drive_to_completion).map_err(drop)).unwrap(); - } + let verifier = BabeVerifier { + api, + inherent_data_providers, + time_source: Default::default(), + config, + }; - #[test] - fn wrong_consensus_engine_id_rejected() { - drop(env_logger::try_init()); - let sig = sr25519::Pair::generate().0.sign(b""); - let bad_seal: Item = DigestItem::Seal([0; 4], sig.0.to_vec()); - assert!(bad_seal.as_babe_pre_digest().is_none()); - assert!(bad_seal.as_babe_seal().is_none()) - } + #[allow(deprecated)] + let epoch_changes = aux_schema::load_epoch_changes(&**client.backend())?; - #[test] - fn malformed_pre_digest_rejected() { - drop(env_logger::try_init()); - let bad_seal: Item = DigestItem::Seal(BABE_ENGINE_ID, [0; 64].to_vec()); - assert!(bad_seal.as_babe_pre_digest().is_none()); - } + let block_import = BabeBlockImport::new( + client.clone(), + epoch_changes.clone(), + block_import, + ); - #[test] - fn sig_is_not_pre_digest() { - drop(env_logger::try_init()); - let sig = sr25519::Pair::generate().0.sign(b""); - let bad_seal: Item = DigestItem::Seal(BABE_ENGINE_ID, sig.0.to_vec()); - assert!(bad_seal.as_babe_pre_digest().is_none()); - assert!(bad_seal.as_babe_seal().is_some()) - } + let pruning_task = client.finality_notification_stream() + .map(|v| Ok::<_, ()>(v)).compat() + .for_each(move |notification| { + let is_descendent_of = is_descendent_of(&client, None); + epoch_changes.lock().prune( + ¬ification.hash, + *notification.header.number(), + &is_descendent_of, + ).map_err(|e| debug!(target: "babe", "Error pruning epoch changes fork tree: {:?}", e))?; - #[test] - fn can_author_block() { - drop(env_logger::try_init()); - let randomness = &[]; - let (pair, _) = sr25519::Pair::generate(); - let mut i = 0; - loop { - match claim_slot(randomness, i, &[], 0, &[pair.public()], &pair, u64::MAX / 10) { - None => i += 1, - Some(s) => { - debug!(target: "babe", "Authored block {:?}", s); - break - } - } - } - } + Ok(()) + }); - #[test] - fn authorities_call_works() { - drop(env_logger::try_init()); - let client = test_client::new(); - - assert_eq!(client.info().chain.best_number, 0); - assert_eq!(authorities(&client, &BlockId::Number(0)).unwrap(), vec![ - Keyring::Alice.into(), - Keyring::Bob.into(), - Keyring::Charlie.into() - ]); - } + let timestamp_core = verifier.time_source.clone(); + let queue = BasicQueue::new( + Arc::new(verifier), + Box::new(block_import.clone()), + justification_import, + finality_proof_import, + ); + + Ok((queue, timestamp_core, block_import, pruning_task)) } diff --git a/core/consensus/babe/src/tests.rs b/core/consensus/babe/src/tests.rs new file mode 100644 index 0000000000000000000000000000000000000000..17864142db92847d42e614eecb0cb13f39df1de7 --- /dev/null +++ b/core/consensus/babe/src/tests.rs @@ -0,0 +1,340 @@ +// 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 . + +//! BABE testsuite + +// FIXME #2532: need to allow deprecated until refactor is done +// https://github.com/paritytech/substrate/issues/2532 +#![allow(deprecated)] +use super::*; + +use client::{LongestChain, block_builder::BlockBuilder}; +use consensus_common::NoNetwork as DummyOracle; +use network::test::*; +use network::test::{Block as TestBlock, PeersClient}; +use runtime_primitives::traits::{Block as BlockT, DigestFor}; +use network::config::ProtocolConfig; +use tokio::runtime::current_thread; +use keyring::sr25519::Keyring; +use super::generic::DigestItem; +use client::BlockchainEvents; +use test_client; +use futures::Async; +use log::debug; +use std::{time::Duration, borrow::Borrow, cell::RefCell}; +type Item = generic::DigestItem; + +type Error = client::error::Error; + +type TestClient = client::Client< + test_client::Backend, + test_client::Executor, + TestBlock, + test_client::runtime::RuntimeApi, +>; + +struct DummyFactory(Arc); +struct DummyProposer(u64, Arc); + +impl Environment for DummyFactory { + type Proposer = DummyProposer; + type Error = Error; + + fn init(&self, parent_header: &::Header) + -> Result + { + Ok(DummyProposer(parent_header.number + 1, self.0.clone())) + } +} + +impl Proposer for DummyProposer { + type Error = Error; + type Create = Result; + + fn propose(&self, _: InherentData, digests: DigestFor, _: Duration) -> Result { + self.1.new_block(digests).unwrap().bake().map_err(|e| e.into()) + } +} + +type Mutator = Arc Fn(&'r mut TestHeader) + Send + Sync>; + +thread_local! { + static MUTATOR: RefCell = RefCell::new(Arc::new(|_|())); +} + +pub struct BabeTestNet { + peers: Vec>, +} + +type TestHeader = ::Header; +type TestExtrinsic = ::Extrinsic; + +pub struct TestVerifier { + inner: BabeVerifier, + mutator: Mutator, +} + +impl Verifier for TestVerifier { + /// Verify the given data and return the BlockImportParams and an optional + /// new set of validators to import. If not, err with an Error-Message + /// presented to the User in the logs. + fn verify( + &self, + origin: BlockOrigin, + mut header: TestHeader, + justification: Option, + body: Option>, + ) -> Result<(BlockImportParams, Option)>>), String> { + let cb: &(dyn Fn(&mut TestHeader) + Send + Sync) = self.mutator.borrow(); + cb(&mut header); + Ok(self.inner.verify(origin, header, justification, body).expect("verification failed!")) + } +} + +impl TestNetFactory for BabeTestNet { + type Specialization = DummySpecialization; + type Verifier = TestVerifier; + type PeerData = (); + + /// Create new test network with peers and given config. + fn from_config(_config: &ProtocolConfig) -> Self { + debug!(target: "babe", "Creating test network from config"); + BabeTestNet { + peers: Vec::new(), + } + } + + /// KLUDGE: this function gets the mutator from thread-local storage. + fn make_verifier(&self, client: PeersClient, _cfg: &ProtocolConfig) + -> Arc + { + let api = client.as_full().expect("only full clients are used in test"); + trace!(target: "babe", "Creating a verifier"); + let config = Config::get_or_compute(&*api) + .expect("slot duration available"); + let inherent_data_providers = InherentDataProviders::new(); + register_babe_inherent_data_provider( + &inherent_data_providers, + config.get() + ).expect("Registers babe inherent data provider"); + trace!(target: "babe", "Provider registered"); + + Arc::new(TestVerifier { + inner: BabeVerifier { + api, + inherent_data_providers, + config, + time_source: Default::default(), + }, + mutator: MUTATOR.with(|s| s.borrow().clone()), + }) + } + + fn peer(&mut self, i: usize) -> &mut Peer { + trace!(target: "babe", "Retreiving a peer"); + &mut self.peers[i] + } + + fn peers(&self) -> &Vec> { + trace!(target: "babe", "Retreiving peers"); + &self.peers + } + + fn mut_peers>)>( + &mut self, + closure: F, + ) { + closure(&mut self.peers); + } +} + +#[test] +fn can_serialize_block() { + let _ = env_logger::try_init(); + assert!(BabePreDigest::decode(&mut &b""[..]).is_none()); +} + +#[test] +#[should_panic] +fn rejects_empty_block() { + env_logger::try_init().unwrap(); + let mut net = BabeTestNet::new(3); + let block_builder = |builder: BlockBuilder<_, _>| { + builder.bake().unwrap() + }; + net.mut_peers(|peer| { + peer[0].generate_blocks(1, BlockOrigin::NetworkInitialSync, block_builder); + }) +} + +fn run_one_test() { + let _ = env_logger::try_init(); + let net = BabeTestNet::new(3); + + let peers = &[ + (0, Keyring::Alice), + (1, Keyring::Bob), + (2, Keyring::Charlie), + ]; + + let net = Arc::new(Mutex::new(net)); + let mut import_notifications = Vec::new(); + let mut runtime = current_thread::Runtime::new().unwrap(); + for (peer_id, key) in peers { + let client = net.lock().peer(*peer_id).client().as_full().unwrap(); + let environ = Arc::new(DummyFactory(client.clone())); + import_notifications.push( + client.import_notification_stream() + .map(|v| Ok::<_, ()>(v)).compat() + .take_while(|n| Ok(n.header.number() < &5)) + .for_each(move |_| Ok(())) + ); + + let config = Config::get_or_compute(&*client) + .expect("slot duration available"); + + let inherent_data_providers = InherentDataProviders::new(); + register_babe_inherent_data_provider( + &inherent_data_providers, config.get() + ).expect("Registers babe inherent data provider"); + + + #[allow(deprecated)] + let select_chain = LongestChain::new(client.backend().clone()); + + runtime.spawn(start_babe(BabeParams { + config, + local_key: Arc::new(key.clone().into()), + block_import: client.clone(), + select_chain, + client, + env: environ.clone(), + sync_oracle: DummyOracle, + inherent_data_providers, + force_authoring: false, + time_source: Default::default(), + }).expect("Starts babe")); + } + + // wait for all finalized on each. + let wait_for = futures::future::join_all(import_notifications); + + let drive_to_completion = futures::future::poll_fn(|| { net.lock().poll(); Ok(Async::NotReady) }); + let _ = runtime.block_on(wait_for.select(drive_to_completion).map_err(|_| ())).unwrap(); +} + +#[test] +fn authoring_blocks() { run_one_test() } + +#[test] +#[should_panic] +fn rejects_missing_inherent_digest() { + MUTATOR.with(|s| *s.borrow_mut() = Arc::new(move |header: &mut TestHeader| { + let v = std::mem::replace(&mut header.digest_mut().logs, vec![]); + header.digest_mut().logs = v.into_iter() + .filter(|v| v.as_babe_pre_digest().is_none()) + .collect() + })); + run_one_test() +} + +#[test] +#[should_panic] +fn rejects_missing_seals() { + MUTATOR.with(|s| *s.borrow_mut() = Arc::new(move |header: &mut TestHeader| { + let v = std::mem::replace(&mut header.digest_mut().logs, vec![]); + header.digest_mut().logs = v.into_iter() + .filter(|v| v.as_babe_seal().is_none()) + .collect() + })); + run_one_test() +} + +// TODO: this test assumes that the test runtime will trigger epoch changes +// which isn't the case since it doesn't include the session module. +#[test] +#[should_panic] +#[ignore] +fn rejects_missing_consensus_digests() { + MUTATOR.with(|s| *s.borrow_mut() = Arc::new(move |header: &mut TestHeader| { + let v = std::mem::replace(&mut header.digest_mut().logs, vec![]); + header.digest_mut().logs = v.into_iter() + .filter(|v| v.as_babe_epoch().is_none()) + .collect() + })); + run_one_test() +} + +#[test] +fn wrong_consensus_engine_id_rejected() { + let _ = env_logger::try_init(); + let sig = sr25519::Pair::generate().0.sign(b""); + let bad_seal: Item = DigestItem::Seal([0; 4], sig.0.to_vec()); + assert!(bad_seal.as_babe_pre_digest().is_none()); + assert!(bad_seal.as_babe_seal().is_none()) +} + +#[test] +fn malformed_pre_digest_rejected() { + let _ = env_logger::try_init(); + let bad_seal: Item = DigestItem::Seal(BABE_ENGINE_ID, [0; 64].to_vec()); + assert!(bad_seal.as_babe_pre_digest().is_none()); +} + +#[test] +fn sig_is_not_pre_digest() { + let _ = env_logger::try_init(); + let sig = sr25519::Pair::generate().0.sign(b""); + let bad_seal: Item = DigestItem::Seal(BABE_ENGINE_ID, sig.0.to_vec()); + assert!(bad_seal.as_babe_pre_digest().is_none()); + assert!(bad_seal.as_babe_seal().is_some()) +} + +#[test] +fn can_author_block() { + let _ = env_logger::try_init(); + let randomness = &[]; + let (pair, _) = sr25519::Pair::generate(); + let mut i = 0; + let epoch = Epoch { + authorities: vec![(pair.public(), 0)], + randomness: [0; 32], + epoch_index: 1, + duration: 100, + }; + loop { + match claim_slot(randomness, i, 0, epoch.clone(), &pair, u64::MAX / 10) { + None => i += 1, + Some(s) => { + debug!(target: "babe", "Authored block {:?}", s); + break + } + } + } +} + +#[test] +fn authorities_call_works() { + let _ = env_logger::try_init(); + let client = test_client::new(); + + assert_eq!(client.info().chain.best_number, 0); + assert_eq!(epoch(&client, &BlockId::Number(0)).unwrap().authorities, vec![ + (Keyring::Alice.into(), 1), + (Keyring::Bob.into(), 1), + (Keyring::Charlie.into(), 1), + ]); +} diff --git a/core/consensus/common/Cargo.toml b/core/consensus/common/Cargo.toml index c1a847da74fef5f27d705b43ee45792d9b3fa576..c6da0c682e705c072c1f0b200ecb27ddf14a6ab5 100644 --- a/core/consensus/common/Cargo.toml +++ b/core/consensus/common/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" [dependencies] derive_more = "0.14.0" -libp2p = { version = "0.9.0", default-features = false } +libp2p = { version = "0.10.0", default-features = false } log = "0.4" primitives = { package = "substrate-primitives", path= "../../primitives" } inherents = { package = "substrate-inherents", path = "../../inherents" } @@ -17,7 +17,7 @@ runtime_version = { package = "sr-version", path = "../../sr-version" } runtime_primitives = { package = "sr-primitives", path = "../../sr-primitives" } tokio-executor = "0.1.6" tokio-timer = "0.2" -parity-codec = { version = "3.3", features = ["derive"] } +parity-codec = { version = "4.1.1", features = ["derive"] } parking_lot = "0.8.0" [dev-dependencies] @@ -25,4 +25,3 @@ test-client = { package = "substrate-test-runtime-client", path = "../../test-ru [features] default = [] -test-helpers = [] diff --git a/core/consensus/common/src/block_import.rs b/core/consensus/common/src/block_import.rs index 0cb1832b1690c823139f9ff8c2fc18e15c1b35c3..12638285d61b716e9045fcc0a879b49c389b5cda 100644 --- a/core/consensus/common/src/block_import.rs +++ b/core/consensus/common/src/block_import.rs @@ -20,6 +20,7 @@ use runtime_primitives::traits::{Block as BlockT, DigestItemFor, Header as Heade use runtime_primitives::Justification; use std::borrow::Cow; use std::collections::HashMap; +use std::sync::Arc; use crate::import_queue::{Verifier, CacheKeyId}; @@ -95,7 +96,7 @@ pub enum ForkChoiceStrategy { } /// Data required to import a Block -pub struct ImportBlock { +pub struct BlockImportParams { /// Origin of the Block pub origin: BlockOrigin, /// The header, without consensus post-digests applied. This should be in the same @@ -128,7 +129,7 @@ pub struct ImportBlock { pub fork_choice: ForkChoiceStrategy, } -impl ImportBlock { +impl BlockImportParams { /// Deconstruct the justified header into parts. pub fn into_inner(self) -> ( @@ -174,7 +175,7 @@ pub trait BlockImport { /// Check block preconditions. fn check_block( - &self, + &mut self, hash: B::Hash, parent_hash: B::Hash, ) -> Result; @@ -183,22 +184,45 @@ pub trait BlockImport { /// /// Cached data can be accessed through the blockchain cache. fn import_block( - &self, - block: ImportBlock, + &mut self, + block: BlockImportParams, cache: HashMap>, ) -> Result; } +impl BlockImport for Arc +where for<'r> &'r T: BlockImport +{ + type Error = E; + + fn check_block( + &mut self, + hash: B::Hash, + parent_hash: B::Hash, + ) -> Result { + (&**self).check_block(hash, parent_hash) + } + + fn import_block( + &mut self, + block: BlockImportParams, + cache: HashMap>, + ) -> Result { + (&**self).import_block(block, cache) + } +} + /// Justification import trait pub trait JustificationImport { type Error: ::std::error::Error + Send + 'static; - /// Called by the import queue when it is started. - fn on_start(&self, _link: &mut dyn crate::import_queue::Link) { } + /// Called by the import queue when it is started. Returns a list of justifications to request + /// from the network. + fn on_start(&mut self) -> Vec<(B::Hash, NumberFor)> { Vec::new() } /// Import a Block justification and finalize the given block. fn import_justification( - &self, + &mut self, hash: B::Hash, number: NumberFor, justification: Justification, @@ -209,21 +233,16 @@ pub trait JustificationImport { pub trait FinalityProofImport { type Error: std::error::Error + Send + 'static; - /// Called by the import queue when it is started. - fn on_start(&self, _link: &mut dyn crate::import_queue::Link) { } + /// Called by the import queue when it is started. Returns a list of finality proofs to request + /// from the network. + fn on_start(&mut self) -> Vec<(B::Hash, NumberFor)> { Vec::new() } /// Import a Block justification and finalize the given block. Returns finalized block or error. fn import_finality_proof( - &self, + &mut self, hash: B::Hash, number: NumberFor, finality_proof: Vec, verifier: &dyn Verifier, ) -> Result<(B::Hash, NumberFor), Self::Error>; } - -/// Finality proof request builder. -pub trait FinalityProofRequestBuilder: Send { - /// Build data blob, associated with the request. - fn build_request_data(&self, hash: &B::Hash) -> Vec; -} diff --git a/core/consensus/common/src/import_queue.rs b/core/consensus/common/src/import_queue.rs index 03db50097d8e81c3f9a1fa11d7d99cc9514f8644..7445d4f33371f4fb79534153bc053bd273d20da3 100644 --- a/core/consensus/common/src/import_queue.rs +++ b/core/consensus/common/src/import_queue.rs @@ -26,35 +26,26 @@ //! queues to be instantiated simply. use std::{sync::Arc, collections::HashMap}; -use futures::{prelude::*, sync::mpsc}; -use runtime_primitives::{Justification, traits::{ - Block as BlockT, Header as HeaderT, NumberFor, -}}; -use crate::{error::Error as ConsensusError, block_import::{ - BlockImport, BlockOrigin, ImportBlock, ImportedAux, ImportResult, JustificationImport, - FinalityProofImport, FinalityProofRequestBuilder, -}}; +use runtime_primitives::{Justification, traits::{Block as BlockT, Header as _, NumberFor}}; +use crate::error::Error as ConsensusError; +use crate::block_import::{ + BlockImport, BlockOrigin, BlockImportParams, ImportedAux, JustificationImport, ImportResult, + FinalityProofImport, +}; -/// Reputation change for peers which send us a block with an incomplete header. -const INCOMPLETE_HEADER_REPUTATION_CHANGE: i32 = -(1 << 20); -/// Reputation change for peers which send us a block which we fail to verify. -const VERIFICATION_FAIL_REPUTATION_CHANGE: i32 = -(1 << 20); -/// Reputation change for peers which send us a bad block. -const BAD_BLOCK_REPUTATION_CHANGE: i32 = -(1 << 29); -/// Reputation change for peers which send us a block with bad justifications. -const BAD_JUSTIFICATION_REPUTATION_CHANGE: i32 = -(1 << 16); +pub use basic_queue::BasicQueue; + +mod basic_queue; +pub mod buffered_link; /// Shared block import struct used by the queue. -pub type SharedBlockImport = Arc + Send + Sync>; +pub type BoxBlockImport = Box + Send + Sync>; /// Shared justification import struct used by the queue. -pub type SharedJustificationImport = Arc + Send + Sync>; +pub type BoxJustificationImport = Box + Send + Sync>; /// Shared finality proof import struct used by the queue. -pub type SharedFinalityProofImport = Arc + Send + Sync>; - -/// Shared finality proof request builder struct used by the queue. -pub type SharedFinalityProofRequestBuilder = Arc + Send + Sync>; +pub type BoxFinalityProofImport = Box + Send + Sync>; /// Maps to the Origin used by the network. pub type Origin = libp2p::PeerId; @@ -79,7 +70,7 @@ pub type CacheKeyId = [u8; 4]; /// Verify a justification of a block pub trait Verifier: Send + Sync { - /// Verify the given data and return the ImportBlock and an optional + /// Verify the given data and return the BlockImportParams and an optional /// new set of validators to import. If not, err with an Error-Message /// presented to the User in the logs. fn verify( @@ -88,7 +79,7 @@ pub trait Verifier: Send + Sync { header: B::Header, justification: Option, body: Option>, - ) -> Result<(ImportBlock, Option)>>), String>; + ) -> Result<(BlockImportParams, Option)>>), String>; } /// Blocks import queue API. @@ -122,312 +113,18 @@ pub trait ImportQueue: Send { fn poll_actions(&mut self, link: &mut dyn Link); } -/// Interface to a basic block import queue that is importing blocks sequentially in a separate -/// task, with pluggable verification. -pub struct BasicQueue { - /// Channel to send messages to the background task. - sender: mpsc::UnboundedSender>, - /// Results coming from the worker task. - result_port: BufferedLinkReceiver, - /// Sent through the link as soon as possible. - finality_proof_request_builder: Option>, - /// Since we have to be in a tokio context in order to spawn background tasks, we first store - /// the task to spawn here, then extract it as soon as we are in a tokio context. - /// If `Some`, contains the task to spawn in the background. If `None`, the future has already - /// been spawned. - future_to_spawn: Option + Send>>, -} - -impl BasicQueue { - /// Instantiate a new basic queue, with given verifier. - /// - /// This creates a background task, and calls `on_start` on the justification importer and - /// finality proof importer. - pub fn new>( - verifier: Arc, - block_import: SharedBlockImport, - justification_import: Option>, - finality_proof_import: Option>, - finality_proof_request_builder: Option>, - ) -> Self { - let (result_sender, result_port) = buffered_link(); - let (future, worker_sender) = BlockImportWorker::new( - result_sender, - verifier, - block_import, - justification_import, - finality_proof_import, - ); - - Self { - sender: worker_sender, - result_port, - finality_proof_request_builder, - future_to_spawn: Some(Box::new(future)), - } - } - - /// Send synchronization request to the block import channel. - /// - /// The caller should wait for Link::synchronized() call to ensure that it - /// has synchronized with ImportQueue. - #[cfg(any(test, feature = "test-helpers"))] - pub fn synchronize(&self) { - let _ = self.sender.unbounded_send(ToWorkerMsg::Synchronize); - } -} - -impl ImportQueue for BasicQueue { - fn import_blocks(&mut self, origin: BlockOrigin, blocks: Vec>) { - if blocks.is_empty() { - return; - } - - trace!(target: "sync", "Scheduling {} blocks for import", blocks.len()); - let _ = self.sender.unbounded_send(ToWorkerMsg::ImportBlocks(origin, blocks)); - } - - fn import_justification( - &mut self, - who: Origin, - hash: B::Hash, - number: NumberFor, - justification: Justification - ) { - let _ = self.sender.unbounded_send(ToWorkerMsg::ImportJustification(who.clone(), hash, number, justification)); - } - - fn import_finality_proof(&mut self, who: Origin, hash: B::Hash, number: NumberFor, finality_proof: Vec) { - trace!(target: "sync", "Scheduling finality proof of {}/{} for import", number, hash); - let _ = self.sender.unbounded_send(ToWorkerMsg::ImportFinalityProof(who, hash, number, finality_proof)); - } - - fn poll_actions(&mut self, link: &mut dyn Link) { - if let Some(future) = self.future_to_spawn.take() { - tokio_executor::spawn(future); - } - - if let Some(fprb) = self.finality_proof_request_builder.take() { - link.set_finality_proof_request_builder(fprb); - } - - self.result_port.poll_actions(link); - } -} - -/// Message destinated to the background worker. -#[derive(Debug)] -enum ToWorkerMsg { - ImportBlocks(BlockOrigin, Vec>), - ImportJustification(Origin, B::Hash, NumberFor, Justification), - ImportFinalityProof(Origin, B::Hash, NumberFor, Vec), - #[cfg(any(test, feature = "test-helpers"))] - Synchronize, -} - -struct BlockImportWorker> { - result_sender: BufferedLinkSender, - block_import: SharedBlockImport, - justification_import: Option>, - finality_proof_import: Option>, - verifier: Arc, -} - -impl> BlockImportWorker { - fn new( - result_sender: BufferedLinkSender, - verifier: Arc, - block_import: SharedBlockImport, - justification_import: Option>, - finality_proof_import: Option>, - ) -> (impl Future + Send, mpsc::UnboundedSender>) { - let (sender, mut port) = mpsc::unbounded(); - - let mut worker = BlockImportWorker { - result_sender, - verifier, - justification_import, - block_import, - finality_proof_import, - }; - - if let Some(justification_import) = worker.justification_import.as_ref() { - justification_import.on_start(&mut worker.result_sender); - } - if let Some(finality_proof_import) = worker.finality_proof_import.as_ref() { - finality_proof_import.on_start(&mut worker.result_sender); - } - - let future = futures::future::poll_fn(move || { - loop { - let msg = match port.poll() { - Ok(Async::Ready(Some(msg))) => msg, - Err(_) | Ok(Async::Ready(None)) => return Ok(Async::Ready(())), - Ok(Async::NotReady) => return Ok(Async::NotReady), - }; - - match msg { - ToWorkerMsg::ImportBlocks(origin, blocks) => { - worker.import_a_batch_of_blocks(origin, blocks); - }, - ToWorkerMsg::ImportFinalityProof(who, hash, number, proof) => { - worker.import_finality_proof(who, hash, number, proof); - }, - ToWorkerMsg::ImportJustification(who, hash, number, justification) => { - worker.import_justification(who, hash, number, justification); - } - #[cfg(any(test, feature = "test-helpers"))] - ToWorkerMsg::Synchronize => { - trace!(target: "sync", "Sending sync message"); - worker.result_sender.synchronized(); - }, - } - } - }); - - (future, sender) - } - - fn import_a_batch_of_blocks(&mut self, origin: BlockOrigin, blocks: Vec>) { - let (imported, count, results) = import_many_blocks( - &*self.block_import, - origin, - blocks, - self.verifier.clone(), - ); - - trace!(target: "sync", "Imported {} of {}", imported, count); - - let mut has_error = false; - let mut hashes = vec![]; - for (result, hash) in results { - hashes.push(hash); - - if has_error { - continue; - } - - if result.is_err() { - has_error = true; - } - - match result { - Ok(BlockImportResult::ImportedKnown(number)) => self.result_sender.block_imported(&hash, number), - Ok(BlockImportResult::ImportedUnknown(number, aux, who)) => { - self.result_sender.block_imported(&hash, number); - - if aux.clear_justification_requests { - trace!( - target: "sync", - "Block imported clears all pending justification requests {}: {:?}", - number, - hash - ); - self.result_sender.clear_justification_requests(); - } - - if aux.needs_justification { - trace!(target: "sync", "Block imported but requires justification {}: {:?}", number, hash); - self.result_sender.request_justification(&hash, number); - } - - if aux.bad_justification { - if let Some(peer) = who { - info!("Sent block with bad justification to import"); - self.result_sender.report_peer(peer, BAD_JUSTIFICATION_REPUTATION_CHANGE); - } - } - - if aux.needs_finality_proof { - trace!(target: "sync", "Block imported but requires finality proof {}: {:?}", number, hash); - self.result_sender.request_finality_proof(&hash, number); - } - }, - Err(BlockImportError::IncompleteHeader(who)) => { - if let Some(peer) = who { - info!("Peer sent block with incomplete header to import"); - self.result_sender.report_peer(peer, INCOMPLETE_HEADER_REPUTATION_CHANGE); - self.result_sender.restart(); - } - }, - Err(BlockImportError::VerificationFailed(who, e)) => { - if let Some(peer) = who { - info!("Verification failed from peer: {}", e); - self.result_sender.report_peer(peer, VERIFICATION_FAIL_REPUTATION_CHANGE); - self.result_sender.restart(); - } - }, - Err(BlockImportError::BadBlock(who)) => { - if let Some(peer) = who { - info!("Bad block"); - self.result_sender.report_peer(peer, BAD_BLOCK_REPUTATION_CHANGE); - self.result_sender.restart(); - } - }, - Err(BlockImportError::UnknownParent) | Err(BlockImportError::Error) => { - self.result_sender.restart(); - }, - }; - } - - self.result_sender.blocks_processed(hashes, has_error); - } - - fn import_finality_proof(&mut self, who: Origin, hash: B::Hash, number: NumberFor, finality_proof: Vec) { - let result = self.finality_proof_import.as_ref().map(|finality_proof_import| { - finality_proof_import.import_finality_proof(hash, number, finality_proof, &*self.verifier) - .map_err(|e| { - debug!( - "Finality proof import failed with {:?} for hash: {:?} number: {:?} coming from node: {:?}", - e, - hash, - number, - who, - ); - }) - }).unwrap_or(Err(())); - - trace!(target: "sync", "Imported finality proof for {}/{}", number, hash); - self.result_sender.finality_proof_imported(who, (hash, number), result); - } - - fn import_justification( - &mut self, - who: Origin, - hash: B::Hash, - number: NumberFor, - justification: Justification - ) { - let success = self.justification_import.as_ref().map(|justification_import| { - justification_import.import_justification(hash, number, justification) - .map_err(|e| { - debug!( - target: "sync", - "Justification import failed with {:?} for hash: {:?} number: {:?} coming from node: {:?}", - e, - hash, - number, - who, - ); - e - }).is_ok() - }).unwrap_or(false); - - self.result_sender.justification_imported(who, &hash, number, success); - } -} - /// Hooks that the verification queue can use to influence the synchronization /// algorithm. pub trait Link: Send { - /// Block imported. - fn block_imported(&mut self, _hash: &B::Hash, _number: NumberFor) {} /// Batch of blocks imported, with or without error. - fn blocks_processed(&mut self, _processed_blocks: Vec, _has_error: bool) {} + fn blocks_processed( + &mut self, + _imported: usize, + _count: usize, + _results: Vec<(Result>, BlockImportError>, B::Hash)> + ) {} /// Justification import result. fn justification_imported(&mut self, _who: Origin, _hash: &B::Hash, _number: NumberFor, _success: bool) {} - /// Clear all pending justification requests. - fn clear_justification_requests(&mut self) {} /// Request a justification for the given block. fn request_justification(&mut self, _hash: &B::Hash, _number: NumberFor) {} /// Finality proof import result. @@ -443,155 +140,6 @@ pub trait Link: Send { ) {} /// Request a finality proof for the given block. fn request_finality_proof(&mut self, _hash: &B::Hash, _number: NumberFor) {} - /// Remember finality proof request builder on start. - fn set_finality_proof_request_builder(&mut self, _request_builder: SharedFinalityProofRequestBuilder) {} - /// Adjusts the reputation of the given peer. - fn report_peer(&mut self, _who: Origin, _reputation_change: i32) {} - /// Restart sync. - fn restart(&mut self) {} - /// Synchronization request has been processed. - #[cfg(any(test, feature = "test-helpers"))] - fn synchronized(&mut self) {} -} - -/// Wraps around an unbounded channel from the `futures` crate. The sender implements `Link` and -/// can be used to buffer commands, and the receiver can be used to poll said commands and transfer -/// them to another link. -pub fn buffered_link() -> (BufferedLinkSender, BufferedLinkReceiver) { - let (tx, rx) = mpsc::unbounded(); - let tx = BufferedLinkSender { tx }; - let rx = BufferedLinkReceiver { rx }; - (tx, rx) -} - -/// See [`buffered_link`]. -pub struct BufferedLinkSender { - tx: mpsc::UnboundedSender>, -} - -/// Internal buffered message. -enum BlockImportWorkerMsg { - BlockImported(B::Hash, NumberFor), - BlocksProcessed(Vec, bool), - JustificationImported(Origin, B::Hash, NumberFor, bool), - ClearJustificationRequests, - RequestJustification(B::Hash, NumberFor), - FinalityProofImported(Origin, (B::Hash, NumberFor), Result<(B::Hash, NumberFor), ()>), - RequestFinalityProof(B::Hash, NumberFor), - SetFinalityProofRequestBuilder(SharedFinalityProofRequestBuilder), - ReportPeer(Origin, i32), - Restart, - #[cfg(any(test, feature = "test-helpers"))] - Synchronized, -} - -impl Link for BufferedLinkSender { - fn block_imported(&mut self, hash: &B::Hash, number: NumberFor) { - let _ = self.tx.unbounded_send(BlockImportWorkerMsg::BlockImported(hash.clone(), number)); - } - - fn blocks_processed(&mut self, processed_blocks: Vec, has_error: bool) { - let _ = self.tx.unbounded_send(BlockImportWorkerMsg::BlocksProcessed(processed_blocks, has_error)); - } - - fn justification_imported( - &mut self, - who: Origin, - hash: &B::Hash, - number: NumberFor, - success: bool - ) { - let msg = BlockImportWorkerMsg::JustificationImported(who, hash.clone(), number, success); - let _ = self.tx.unbounded_send(msg); - } - - fn clear_justification_requests(&mut self) { - let _ = self.tx.unbounded_send(BlockImportWorkerMsg::ClearJustificationRequests); - } - - fn request_justification(&mut self, hash: &B::Hash, number: NumberFor) { - let _ = self.tx.unbounded_send(BlockImportWorkerMsg::RequestJustification(hash.clone(), number)); - } - - fn finality_proof_imported( - &mut self, - who: Origin, - request_block: (B::Hash, NumberFor), - finalization_result: Result<(B::Hash, NumberFor), ()>, - ) { - let msg = BlockImportWorkerMsg::FinalityProofImported(who, request_block, finalization_result); - let _ = self.tx.unbounded_send(msg); - } - - fn request_finality_proof(&mut self, hash: &B::Hash, number: NumberFor) { - let _ = self.tx.unbounded_send(BlockImportWorkerMsg::RequestFinalityProof(hash.clone(), number)); - } - - fn set_finality_proof_request_builder(&mut self, request_builder: SharedFinalityProofRequestBuilder) { - let _ = self.tx.unbounded_send(BlockImportWorkerMsg::SetFinalityProofRequestBuilder(request_builder)); - } - - fn report_peer(&mut self, who: Origin, reputation_change: i32) { - let _ = self.tx.unbounded_send(BlockImportWorkerMsg::ReportPeer(who, reputation_change)); - } - - fn restart(&mut self) { - let _ = self.tx.unbounded_send(BlockImportWorkerMsg::Restart); - } - - #[cfg(any(test, feature = "test-helpers"))] - fn synchronized(&mut self) { - let _ = self.tx.unbounded_send(BlockImportWorkerMsg::Synchronized); - } -} - -/// See [`buffered_link`]. -pub struct BufferedLinkReceiver { - rx: mpsc::UnboundedReceiver>, -} - -impl BufferedLinkReceiver { - /// Polls for the buffered link actions. Any enqueued action will be propagated to the link - /// passed as parameter. - /// - /// This method should behave in a way similar to `Future::poll`. It can register the current - /// task and notify later when more actions are ready to be polled. To continue the comparison, - /// it is as if this method always returned `Ok(Async::NotReady)`. - pub fn poll_actions(&mut self, link: &mut dyn Link) { - loop { - let msg = if let Ok(Async::Ready(Some(msg))) = self.rx.poll() { - msg - } else { - break - }; - - match msg { - BlockImportWorkerMsg::BlockImported(hash, number) => - link.block_imported(&hash, number), - BlockImportWorkerMsg::BlocksProcessed(blocks, has_error) => - link.blocks_processed(blocks, has_error), - BlockImportWorkerMsg::JustificationImported(who, hash, number, success) => - link.justification_imported(who, &hash, number, success), - BlockImportWorkerMsg::ClearJustificationRequests => - link.clear_justification_requests(), - BlockImportWorkerMsg::RequestJustification(hash, number) => - link.request_justification(&hash, number), - BlockImportWorkerMsg::FinalityProofImported(who, block, result) => - link.finality_proof_imported(who, block, result), - BlockImportWorkerMsg::RequestFinalityProof(hash, number) => - link.request_finality_proof(&hash, number), - BlockImportWorkerMsg::SetFinalityProofRequestBuilder(builder) => - link.set_finality_proof_request_builder(builder), - BlockImportWorkerMsg::ReportPeer(who, reput) => - link.report_peer(who, reput), - BlockImportWorkerMsg::Restart => - link.restart(), - #[cfg(any(test, feature = "test-helpers"))] - BlockImportWorkerMsg::Synchronized => - link.synchronized(), - } - } - } } /// Block import successful result. @@ -604,7 +152,7 @@ pub enum BlockImportResult { } /// Block import error. -#[derive(Debug, PartialEq)] +#[derive(Debug)] pub enum BlockImportError { /// Block missed header, can't be imported IncompleteHeader(Option), @@ -614,66 +162,15 @@ pub enum BlockImportError { BadBlock(Option), /// Block has an unknown parent UnknownParent, - /// Other Error. - Error, -} - -/// Import several blocks at once, returning import result for each block. -fn import_many_blocks>( - import_handle: &dyn BlockImport, - blocks_origin: BlockOrigin, - blocks: Vec>, - verifier: Arc, -) -> (usize, usize, Vec<( - Result>, BlockImportError>, - B::Hash, -)>) { - let count = blocks.len(); - let mut imported = 0; - - let blocks_range = match ( - blocks.first().and_then(|b| b.header.as_ref().map(|h| h.number())), - blocks.last().and_then(|b| b.header.as_ref().map(|h| h.number())), - ) { - (Some(first), Some(last)) if first != last => format!(" ({}..{})", first, last), - (Some(first), Some(_)) => format!(" ({})", first), - _ => Default::default(), - }; - - trace!(target: "sync", "Starting import of {} blocks {}", count, blocks_range); - - let mut results = vec![]; - - let mut has_error = false; - - // Blocks in the response/drain should be in ascending order. - for block in blocks { - let block_hash = block.hash; - let import_result = if has_error { - Err(BlockImportError::Error) - } else { - import_single_block( - import_handle, - blocks_origin.clone(), - block, - verifier.clone(), - ) - }; - let was_ok = import_result.is_ok(); - results.push((import_result, block_hash)); - if was_ok { - imported += 1; - } else { - has_error = true; - } - } - - (imported, count, results) + /// Block import has been cancelled. This can happen if the parent block fails to be imported. + Cancelled, + /// Other error. + Other(ConsensusError), } /// Single block import function. pub fn import_single_block>( - import_handle: &dyn BlockImport, + import_handle: &mut dyn BlockImport, block_origin: BlockOrigin, block: IncomingBlock, verifier: Arc, @@ -715,7 +212,7 @@ pub fn import_single_block>( }, Err(e) => { debug!(target: "sync", "Error importing block {}: {:?}: {:?}", number, hash, e); - Err(BlockImportError::Error) + Err(BlockImportError::Other(e)) } } }; diff --git a/core/consensus/common/src/import_queue/basic_queue.rs b/core/consensus/common/src/import_queue/basic_queue.rs new file mode 100644 index 0000000000000000000000000000000000000000..51d30cddbb729d93379a055077864ba5862865c8 --- /dev/null +++ b/core/consensus/common/src/import_queue/basic_queue.rs @@ -0,0 +1,315 @@ +// 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 . + +use std::sync::Arc; +use futures::{prelude::*, future::Executor, sync::mpsc}; +use runtime_primitives::{Justification, traits::{Block as BlockT, Header as HeaderT, NumberFor}}; + +use crate::error::Error as ConsensusError; +use crate::block_import::{BlockImport, BlockOrigin}; +use crate::import_queue::{ + BlockImportResult, BlockImportError, Verifier, BoxBlockImport, BoxFinalityProofImport, + BoxJustificationImport, ImportQueue, Link, Origin, + IncomingBlock, import_single_block, + buffered_link::{self, BufferedLinkSender, BufferedLinkReceiver} +}; + +/// Interface to a basic block import queue that is importing blocks sequentially in a separate +/// task, with pluggable verification. +pub struct BasicQueue { + /// Channel to send messages to the background task. + sender: mpsc::UnboundedSender>, + /// Results coming from the worker task. + result_port: BufferedLinkReceiver, + /// Since we have to be in a tokio context in order to spawn background tasks, we first store + /// the task to spawn here, then extract it as soon as we are in a tokio context. + /// If `Some`, contains the task to spawn in the background. If `None`, the future has already + /// been spawned. + future_to_spawn: Option + Send>>, + /// If it isn't possible to spawn the future in `future_to_spawn` (which is notably the case in + /// "no std" environment), we instead put it in `manual_poll`. It is then polled manually from + /// `poll_actions`. + manual_poll: Option + Send>>, +} + +impl BasicQueue { + /// Instantiate a new basic queue, with given verifier. + /// + /// This creates a background task, and calls `on_start` on the justification importer and + /// finality proof importer. + pub fn new>( + verifier: Arc, + block_import: BoxBlockImport, + justification_import: Option>, + finality_proof_import: Option>, + ) -> Self { + let (result_sender, result_port) = buffered_link::buffered_link(); + let (future, worker_sender) = BlockImportWorker::new( + result_sender, + verifier, + block_import, + justification_import, + finality_proof_import, + ); + + Self { + sender: worker_sender, + result_port, + future_to_spawn: Some(Box::new(future)), + manual_poll: None, + } + } +} + +impl ImportQueue for BasicQueue { + fn import_blocks(&mut self, origin: BlockOrigin, blocks: Vec>) { + if blocks.is_empty() { + return; + } + + trace!(target: "sync", "Scheduling {} blocks for import", blocks.len()); + let _ = self.sender.unbounded_send(ToWorkerMsg::ImportBlocks(origin, blocks)); + } + + fn import_justification( + &mut self, + who: Origin, + hash: B::Hash, + number: NumberFor, + justification: Justification + ) { + let _ = self.sender.unbounded_send(ToWorkerMsg::ImportJustification(who.clone(), hash, number, justification)); + } + + fn import_finality_proof(&mut self, who: Origin, hash: B::Hash, number: NumberFor, finality_proof: Vec) { + trace!(target: "sync", "Scheduling finality proof of {}/{} for import", number, hash); + let _ = self.sender.unbounded_send(ToWorkerMsg::ImportFinalityProof(who, hash, number, finality_proof)); + } + + fn poll_actions(&mut self, link: &mut dyn Link) { + // Try to spawn the future in `future_to_spawn`. + if let Some(future) = self.future_to_spawn.take() { + if let Err(err) = tokio_executor::DefaultExecutor::current().execute(future) { + debug_assert!(self.manual_poll.is_none()); + self.manual_poll = Some(err.into_future()); + } + } + + // As a backup mechanism, if we failed to spawn the `future_to_spawn`, we instead poll + // manually here. + if let Some(manual_poll) = self.manual_poll.as_mut() { + match manual_poll.poll() { + Ok(Async::NotReady) => {} + _ => self.manual_poll = None, + } + } + + self.result_port.poll_actions(link); + } +} + +/// Message destinated to the background worker. +#[derive(Debug)] +enum ToWorkerMsg { + ImportBlocks(BlockOrigin, Vec>), + ImportJustification(Origin, B::Hash, NumberFor, Justification), + ImportFinalityProof(Origin, B::Hash, NumberFor, Vec), +} + +struct BlockImportWorker> { + result_sender: BufferedLinkSender, + block_import: BoxBlockImport, + justification_import: Option>, + finality_proof_import: Option>, + verifier: Arc, +} + +impl> BlockImportWorker { + fn new( + result_sender: BufferedLinkSender, + verifier: Arc, + block_import: BoxBlockImport, + justification_import: Option>, + finality_proof_import: Option>, + ) -> (impl Future + Send, mpsc::UnboundedSender>) { + let (sender, mut port) = mpsc::unbounded(); + + let mut worker = BlockImportWorker { + result_sender, + verifier, + justification_import, + block_import, + finality_proof_import, + }; + + if let Some(justification_import) = worker.justification_import.as_mut() { + for (hash, number) in justification_import.on_start() { + worker.result_sender.request_justification(&hash, number); + } + } + + if let Some(finality_proof_import) = worker.finality_proof_import.as_mut() { + for (hash, number) in finality_proof_import.on_start() { + worker.result_sender.request_finality_proof(&hash, number); + } + } + + let future = futures::future::poll_fn(move || { + loop { + let msg = match port.poll() { + Ok(Async::Ready(Some(msg))) => msg, + Err(_) | Ok(Async::Ready(None)) => return Ok(Async::Ready(())), + Ok(Async::NotReady) => return Ok(Async::NotReady), + }; + + match msg { + ToWorkerMsg::ImportBlocks(origin, blocks) => { + worker.import_a_batch_of_blocks(origin, blocks); + }, + ToWorkerMsg::ImportFinalityProof(who, hash, number, proof) => { + worker.import_finality_proof(who, hash, number, proof); + }, + ToWorkerMsg::ImportJustification(who, hash, number, justification) => { + worker.import_justification(who, hash, number, justification); + } + } + } + }); + + (future, sender) + } + + fn import_a_batch_of_blocks(&mut self, origin: BlockOrigin, blocks: Vec>) { + let result_sender = &self.result_sender; + let (imported, count, results) = import_many_blocks( + &mut *self.block_import, + origin, + blocks, + self.verifier.clone(), + || !result_sender.is_closed(), + ); + + self.result_sender.blocks_processed(imported, count, results); + } + + fn import_finality_proof(&mut self, who: Origin, hash: B::Hash, number: NumberFor, finality_proof: Vec) { + let verifier = &*self.verifier; + let result = self.finality_proof_import.as_mut().map(|finality_proof_import| { + finality_proof_import.import_finality_proof(hash, number, finality_proof, verifier) + .map_err(|e| { + debug!( + "Finality proof import failed with {:?} for hash: {:?} number: {:?} coming from node: {:?}", + e, + hash, + number, + who, + ); + }) + }).unwrap_or(Err(())); + + trace!(target: "sync", "Imported finality proof for {}/{}", number, hash); + self.result_sender.finality_proof_imported(who, (hash, number), result); + } + + fn import_justification( + &mut self, + who: Origin, + hash: B::Hash, + number: NumberFor, + justification: Justification + ) { + let success = self.justification_import.as_mut().map(|justification_import| { + justification_import.import_justification(hash, number, justification) + .map_err(|e| { + debug!( + target: "sync", + "Justification import failed with {:?} for hash: {:?} number: {:?} coming from node: {:?}", + e, + hash, + number, + who, + ); + e + }).is_ok() + }).unwrap_or(false); + + self.result_sender.justification_imported(who, &hash, number, success); + } +} + +/// Import several blocks at once, returning import result for each block. +/// +/// The `keep_going` closure will be called regularly. If it returns false, then the function will +/// end prematurely. +fn import_many_blocks>( + import_handle: &mut dyn BlockImport, + blocks_origin: BlockOrigin, + blocks: Vec>, + verifier: Arc, + keep_going: impl Fn() -> bool, +) -> (usize, usize, Vec<( + Result>, BlockImportError>, + B::Hash, +)>) { + let count = blocks.len(); + let mut imported = 0; + + let blocks_range = match ( + blocks.first().and_then(|b| b.header.as_ref().map(|h| h.number())), + blocks.last().and_then(|b| b.header.as_ref().map(|h| h.number())), + ) { + (Some(first), Some(last)) if first != last => format!(" ({}..{})", first, last), + (Some(first), Some(_)) => format!(" ({})", first), + _ => Default::default(), + }; + + trace!(target: "sync", "Starting import of {} blocks {}", count, blocks_range); + + let mut results = vec![]; + + let mut has_error = false; + + // Blocks in the response/drain should be in ascending order. + for block in blocks { + if !keep_going() { + // Setting `has_error` to true cancels the rest of the import. + has_error = true; + } + + let block_number = block.header.as_ref().map(|h| h.number().clone()); + let block_hash = block.hash; + let import_result = if has_error { + Err(BlockImportError::Cancelled) + } else { + import_single_block( + import_handle, + blocks_origin.clone(), + block, + verifier.clone(), + ) + }; + let was_ok = import_result.is_ok(); + if was_ok { + trace!(target: "sync", "Block imported successfully {:?} ({})", block_number, block_hash); + imported += 1; + } else { + has_error = true; + } + results.push((import_result, block_hash)); + } + + (imported, count, results) +} diff --git a/core/consensus/common/src/import_queue/buffered_link.rs b/core/consensus/common/src/import_queue/buffered_link.rs new file mode 100644 index 0000000000000000000000000000000000000000..9c555ba9d964dfb82d1f34388ab55b9d3193b12d --- /dev/null +++ b/core/consensus/common/src/import_queue/buffered_link.rs @@ -0,0 +1,159 @@ +// 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 . + +//! Provides the `buffered_link` utility. +//! +//! The buffered link is a channel that allows buffering the method calls on `Link`. +//! +//! # Example +//! +//! ```no_run +//! use substrate_consensus_common::import_queue::Link; +//! # use substrate_consensus_common::import_queue::buffered_link::buffered_link; +//! # use test_client::runtime::Block; +//! # struct DummyLink; impl Link for DummyLink {} +//! # let mut my_link = DummyLink; +//! let (mut tx, mut rx) = buffered_link::(); +//! tx.blocks_processed(0, 0, vec![]); +//! rx.poll_actions(&mut my_link); // Calls `my_link.blocks_processed(0, 0, vec![])` +//! ``` +//! + +use futures::{prelude::*, sync::mpsc}; +use runtime_primitives::traits::{Block as BlockT, NumberFor}; +use crate::import_queue::{Origin, Link, BlockImportResult, BlockImportError}; + +/// Wraps around an unbounded channel from the `futures` crate. The sender implements `Link` and +/// can be used to buffer commands, and the receiver can be used to poll said commands and transfer +/// them to another link. +pub fn buffered_link() -> (BufferedLinkSender, BufferedLinkReceiver) { + let (tx, rx) = mpsc::unbounded(); + let tx = BufferedLinkSender { tx }; + let rx = BufferedLinkReceiver { rx }; + (tx, rx) +} + +/// See [`buffered_link`]. +pub struct BufferedLinkSender { + tx: mpsc::UnboundedSender>, +} + +impl BufferedLinkSender { + /// Returns true if the sender points to nowhere. + /// + /// Once `true` is returned, it is pointless to use the sender anymore. + pub fn is_closed(&self) -> bool { + self.tx.is_closed() + } +} + +/// Internal buffered message. +enum BlockImportWorkerMsg { + BlocksProcessed(usize, usize, Vec<(Result>, BlockImportError>, B::Hash)>), + JustificationImported(Origin, B::Hash, NumberFor, bool), + RequestJustification(B::Hash, NumberFor), + FinalityProofImported(Origin, (B::Hash, NumberFor), Result<(B::Hash, NumberFor), ()>), + RequestFinalityProof(B::Hash, NumberFor), +} + +impl Link for BufferedLinkSender { + fn blocks_processed( + &mut self, + imported: usize, + count: usize, + results: Vec<(Result>, BlockImportError>, B::Hash)> + ) { + let _ = self.tx.unbounded_send(BlockImportWorkerMsg::BlocksProcessed(imported, count, results)); + } + + fn justification_imported( + &mut self, + who: Origin, + hash: &B::Hash, + number: NumberFor, + success: bool + ) { + let msg = BlockImportWorkerMsg::JustificationImported(who, hash.clone(), number, success); + let _ = self.tx.unbounded_send(msg); + } + + fn request_justification(&mut self, hash: &B::Hash, number: NumberFor) { + let _ = self.tx.unbounded_send(BlockImportWorkerMsg::RequestJustification(hash.clone(), number)); + } + + fn finality_proof_imported( + &mut self, + who: Origin, + request_block: (B::Hash, NumberFor), + finalization_result: Result<(B::Hash, NumberFor), ()>, + ) { + let msg = BlockImportWorkerMsg::FinalityProofImported(who, request_block, finalization_result); + let _ = self.tx.unbounded_send(msg); + } + + fn request_finality_proof(&mut self, hash: &B::Hash, number: NumberFor) { + let _ = self.tx.unbounded_send(BlockImportWorkerMsg::RequestFinalityProof(hash.clone(), number)); + } +} + +/// See [`buffered_link`]. +pub struct BufferedLinkReceiver { + rx: mpsc::UnboundedReceiver>, +} + +impl BufferedLinkReceiver { + /// Polls for the buffered link actions. Any enqueued action will be propagated to the link + /// passed as parameter. + /// + /// This method should behave in a way similar to `Future::poll`. It can register the current + /// task and notify later when more actions are ready to be polled. To continue the comparison, + /// it is as if this method always returned `Ok(Async::NotReady)`. + pub fn poll_actions(&mut self, link: &mut dyn Link) { + loop { + let msg = if let Ok(Async::Ready(Some(msg))) = self.rx.poll() { + msg + } else { + break + }; + + match msg { + BlockImportWorkerMsg::BlocksProcessed(imported, count, results) => + link.blocks_processed(imported, count, results), + BlockImportWorkerMsg::JustificationImported(who, hash, number, success) => + link.justification_imported(who, &hash, number, success), + BlockImportWorkerMsg::RequestJustification(hash, number) => + link.request_justification(&hash, number), + BlockImportWorkerMsg::FinalityProofImported(who, block, result) => + link.finality_proof_imported(who, block, result), + BlockImportWorkerMsg::RequestFinalityProof(hash, number) => + link.request_finality_proof(&hash, number), + } + } + } +} + +#[cfg(test)] +mod tests { + use test_client::runtime::Block; + + #[test] + fn is_closed() { + let (tx, rx) = super::buffered_link::(); + assert!(!tx.is_closed()); + drop(rx); + assert!(tx.is_closed()); + } +} diff --git a/core/consensus/common/src/lib.rs b/core/consensus/common/src/lib.rs index f5dd8936a1630dde6ca4f3f35a1f0a323a08e754..aad44bcac87424666113165298775cb038c77360 100644 --- a/core/consensus/common/src/lib.rs +++ b/core/consensus/common/src/lib.rs @@ -31,7 +31,7 @@ use std::sync::Arc; use std::time::Duration; -use runtime_primitives::traits::{Block, DigestFor}; +use runtime_primitives::traits::{Block as BlockT, DigestFor}; use futures::prelude::*; pub use inherents::InherentData; @@ -47,13 +47,13 @@ const MAX_BLOCK_SIZE: usize = 4 * 1024 * 1024 + 512; pub use self::error::Error; pub use block_import::{ - BlockImport, BlockOrigin, ForkChoiceStrategy, ImportedAux, ImportBlock, ImportResult, - JustificationImport, FinalityProofImport, FinalityProofRequestBuilder, + BlockImport, BlockOrigin, ForkChoiceStrategy, ImportedAux, BlockImportParams, ImportResult, + JustificationImport, FinalityProofImport, }; pub use select_chain::SelectChain; /// Environment producer for a Consensus instance. Creates proposer instance and communication streams. -pub trait Environment { +pub trait Environment { /// The proposer type this creates. type Proposer: Proposer; /// Error which can occur upon creation. @@ -71,7 +71,7 @@ pub trait Environment { /// block. /// /// Proposers are generic over bits of "consensus data" which are engine-specific. -pub trait Proposer { +pub trait Proposer { /// Error type which can occur when proposing or evaluating. type Error: From + ::std::fmt::Debug + 'static; /// Future that resolves to a committed proposal. diff --git a/core/consensus/rhd/Cargo.toml b/core/consensus/rhd/Cargo.toml index d9632bac8715e9e9dd8f9becae6177c187f789e5..7eda2d69045174e5e7e1ee618b63941b37247fd1 100644 --- a/core/consensus/rhd/Cargo.toml +++ b/core/consensus/rhd/Cargo.toml @@ -8,7 +8,7 @@ edition = "2018" [dependencies] derive_more = "0.14.0" futures = "0.1.17" -codec = { package = "parity-codec", version = "3.2", features = ["derive"] } +codec = { package = "parity-codec", version = "4.1.1", features = ["derive"] } primitives = { package = "substrate-primitives", path = "../../primitives" } consensus = { package = "substrate-consensus-common", path = "../common" } client = { package = "substrate-client", path = "../../client" } @@ -21,7 +21,7 @@ runtime_io = { package = "sr-io", path = "../../sr-io" } tokio = "0.1.7" parking_lot = "0.8.0" log = "0.4" -rhododendron = { version = "0.5.0", features = ["codec"] } +rhododendron = { version = "0.6.0", features = ["codec"] } exit-future = "0.1" [dev-dependencies] diff --git a/core/consensus/rhd/src/lib.rs b/core/consensus/rhd/src/lib.rs index 4a3e03759b4d47d3f34e0b64d301161d6916a54e..4eb118c853dd5d09efa99bdbbc7375df2ddebfae 100644 --- a/core/consensus/rhd/src/lib.rs +++ b/core/consensus/rhd/src/lib.rs @@ -46,7 +46,7 @@ use consensus::error::{ErrorKind as CommonErrorKind}; use consensus::{Authorities, BlockImport, Environment, Proposer as BaseProposer}; use client::{Client as SubstrateClient, CallExecutor}; use client::runtime_api::{Core, BlockBuilder as BlockBuilderAPI, OldTxQueue, BlockBuilderError}; -use runtime_primitives::generic::{BlockId, Era, ImportResult, ImportBlock, BlockOrigin}; +use runtime_primitives::generic::{BlockId, Era, ImportResult, BlockImportParams, BlockOrigin}; use runtime_primitives::traits::{Block, Header}; use runtime_primitives::traits::{ Block as BlockT, Hash as HashT, Header as HeaderT, @@ -150,7 +150,7 @@ pub type Misbehavior = rhododendron::Misbehavior; pub type SharedOfflineTracker = Arc>; /// A proposer for a rhododendron instance. This must implement the base proposer logic. -pub trait LocalProposer: BaseProposer { +pub trait LocalProposer: BaseProposer { /// Import witnessed rhododendron misbehavior. fn import_misbehavior(&self, misbehavior: Vec<(AuthorityId, Misbehavior)>); @@ -224,7 +224,7 @@ struct RoundCache { } /// Instance of BFT agreement. -struct BftInstance { +struct BftInstance { key: Arc, authorities: Vec, parent_hash: B::Hash, @@ -233,7 +233,7 @@ struct BftInstance { proposer: P, } -impl> BftInstance +impl> BftInstance where B: Clone + Eq, B::Hash: ::std::hash::Hash @@ -262,7 +262,7 @@ impl> BftInstance } } -impl> rhododendron::Context for BftInstance +impl> rhododendron::Context for BftInstance where B: Clone + Eq, B::Hash: ::std::hash::Hash, @@ -391,7 +391,7 @@ impl Future for BftFuture { +pub struct BftService { client: Arc, live_agreement: Mutex>, round_cache: Arc>>, @@ -638,14 +638,14 @@ impl BftService /// This stream is localized to a specific parent block-hash, as all messages /// will be signed in a way that accounts for it. When using this with /// `BftService::build_upon`, the user should take care to use the same hash as for that. -pub struct CheckedStream { +pub struct CheckedStream { inner: S, local_id: AuthorityId, authorities: Vec, parent_hash: B::Hash, } -impl CheckedStream { +impl CheckedStream { /// Construct a new checked stream. pub fn new( inner: S, @@ -662,7 +662,7 @@ impl CheckedStream { } } -impl>> Stream for CheckedStream +impl>> Stream for CheckedStream where S::Error: From, { type Item = Communication; @@ -780,7 +780,7 @@ fn check_justification_signed_message( /// Provide all valid authorities. /// /// On failure, returns the justification back. -pub fn check_justification( +pub fn check_justification( authorities: &[AuthorityId], parent: B::Hash, just: UncheckedJustification @@ -795,9 +795,11 @@ pub fn check_justification( /// Provide all valid authorities. /// /// On failure, returns the justification back. -pub fn check_prepare_justification(authorities: &[AuthorityId], parent: B::Hash, just: UncheckedJustification) - -> Result, UncheckedJustification> -{ +pub fn check_prepare_justification( + authorities: &[AuthorityId], + parent: B::Hash, + just: UncheckedJustification +) -> Result, UncheckedJustification> { let vote: Action = Action::Prepare(just.0.round_number as u32, just.0.digest.clone()); let message = localized_encode(parent, vote); @@ -824,7 +826,7 @@ pub fn check_proposal( /// Check vote message signatures and authority. /// Provide all valid authorities. -pub fn check_vote( +pub fn check_vote( authorities: &[AuthorityId], parent_hash: &B::Hash, vote: &rhododendron::LocalizedVote) @@ -842,7 +844,11 @@ pub fn check_vote( check_action::(action, parent_hash, &vote.signature) } -fn check_action(action: Action, parent_hash: &B::Hash, sig: &LocalizedSignature) -> Result<(), Error> { +fn check_action( + action: Action, + parent_hash: &B::Hash, + sig: &LocalizedSignature +) -> Result<(), Error> { let message = localized_encode(*parent_hash, action); if ed25519::Pair::verify(&sig.signature, &message, &sig.signer) { Ok(()) @@ -981,7 +987,8 @@ impl consensus::Environment<::Block> for ProposerFac let id = BlockId::hash(parent_hash); let random_seed = self.client.random_seed(&id)?; - let random_seed = <<::Block as BlockT>::Header as HeaderT>::Hashing::hash(random_seed.as_ref()); + let random_seed = <<::Block as BlockT>::Header as HeaderT> + ::Hashing::hash(random_seed.as_ref()); let validators = self.client.validators(&id)?; self.offline.write().note_new_block(&validators[..]); @@ -1225,7 +1232,10 @@ impl LocalProposer<::Block> for Proposer where proposer } - fn import_misbehavior(&self, _misbehavior: Vec<(AuthorityId, Misbehavior<<::Block as BlockT>::Hash>)>) { + fn import_misbehavior( + &self, + _misbehavior: Vec<(AuthorityId, Misbehavior<<::Block as BlockT>::Hash>)> + ) { use rhododendron::Misbehavior as GenericMisbehavior; use runtime_primitives::bft::{MisbehaviorKind, MisbehaviorReport}; use node_runtime::{Call, UncheckedExtrinsic, ConsensusCall}; @@ -1334,7 +1344,7 @@ mod tests { type Error = Error; fn import_block(&self, - block: ImportBlock, + block: BlockImportParams, _new_authorities: Option> ) -> Result { assert!(self.imported_heights.lock().insert(block.header.number)); diff --git a/core/consensus/slots/Cargo.toml b/core/consensus/slots/Cargo.toml index 5c187024c9f49f70665c7aa015e306016b0436ee..fa856bbfbb8df53aaee5b150d4e47c945cf89008 100644 --- a/core/consensus/slots/Cargo.toml +++ b/core/consensus/slots/Cargo.toml @@ -6,7 +6,7 @@ description = "Generic slots-based utilities for consensus" edition = "2018" [dependencies] -codec = { package = "parity-codec", version = "3.2" } +codec = { package = "parity-codec", version = "4.1.1" } client = { package = "substrate-client", path = "../../client" } primitives = { package = "substrate-primitives", path = "../../primitives" } runtime_primitives = { package = "sr-primitives", path = "../../sr-primitives" } diff --git a/core/consensus/slots/src/lib.rs b/core/consensus/slots/src/lib.rs index c26b6e2ff6c7681387d97ccc3154f0f5821f3667..816a61babd5a503da9b2acac075c48c5e04c22f7 100644 --- a/core/consensus/slots/src/lib.rs +++ b/core/consensus/slots/src/lib.rs @@ -20,12 +20,14 @@ //! time during which certain events can and/or must occur. This crate //! provides generic functionality for slots. -#![forbid(warnings, unsafe_code, missing_docs)] +#![deny(warnings)] +#![forbid(unsafe_code, missing_docs)] mod slots; mod aux_schema; -pub use slots::{SignedDuration, SlotInfo, Slots}; +pub use slots::{SignedDuration, SlotInfo}; +use slots::Slots; pub use aux_schema::{check_equivocation, MAX_SLOT_CAPACITY, PRUNING_BOUND}; use codec::{Decode, Encode}; @@ -38,20 +40,16 @@ use futures::{ use inherents::{InherentData, InherentDataProviders}; use log::{debug, error, info, warn}; use runtime_primitives::generic::BlockId; -use runtime_primitives::traits::{ApiRef, Block, ProvideRuntimeApi}; +use runtime_primitives::traits::{ApiRef, Block as BlockT, ProvideRuntimeApi}; use std::fmt::Debug; use std::ops::Deref; /// A worker that should be invoked at every new slot. -pub trait SlotWorker { +pub trait SlotWorker { /// The type of the future that will be returned when a new slot is /// triggered. type OnSlot: IntoFuture; - /// Called when the proposer starts. - #[deprecated(note = "Not called. Please perform any initialization before calling start_slot_worker.")] - fn on_start(&self, _slot_duration: u64) -> Result<(), consensus_common::Error> { Ok(()) } - /// Called when a new slot is triggered. fn on_slot(&self, chain_head: B::Header, slot_info: SlotInfo) -> Self::OnSlot; } @@ -60,8 +58,13 @@ pub trait SlotWorker { pub trait SlotCompatible { /// Extract timestamp and slot from inherent data. fn extract_timestamp_and_slot( + &self, inherent: &InherentData, - ) -> Result<(u64, u64), consensus_common::Error>; + ) -> Result<(u64, u64, std::time::Duration), consensus_common::Error>; + + /// Get the difference between chain time and local time. Defaults to + /// always returning zero. + fn time_offset() -> SignedDuration { Default::default() } } /// Start a new slot worker. @@ -74,9 +77,10 @@ pub fn start_slot_worker( worker: W, sync_oracle: SO, inherent_data_providers: InherentDataProviders, + timestamp_extractor: SC, ) -> impl Future where - B: Block, + B: BlockT, C: SelectChain + Clone, W: SlotWorker, SO: SyncOracle + Send + Clone, @@ -86,8 +90,11 @@ where let SlotDuration(slot_duration) = slot_duration; // rather than use a timer interval, we schedule our waits ourselves - let mut authorship = Slots::::new(slot_duration.slot_duration(), inherent_data_providers) - .map_err(|e| debug!(target: "slots", "Faulty timer: {:?}", e)) + let mut authorship = Slots::::new( + slot_duration.slot_duration(), + inherent_data_providers, + timestamp_extractor, + ).map_err(|e| debug!(target: "slots", "Faulty timer: {:?}", e)) .for_each(move |slot_info| { // only propose when we are not syncing. if sync_oracle.is_major_syncing() { @@ -188,7 +195,7 @@ impl SlotDuration { /// /// `slot_key` is marked as `'static`, as it should really be a /// compile-time constant. - pub fn get_or_compute(client: &C, cb: CB) -> ::client::error::Result where + pub fn get_or_compute(client: &C, cb: CB) -> ::client::error::Result where C: client::backend::AuxStore, C: ProvideRuntimeApi, CB: FnOnce(ApiRef, &BlockId) -> ::client::error::Result, diff --git a/core/consensus/slots/src/slots.rs b/core/consensus/slots/src/slots.rs index 1bce98487ac94f6b9a93bc123bee6457cbeff1b2..0142f32b82a2c9f904f39b459b2a3faa8a3fb225 100644 --- a/core/consensus/slots/src/slots.rs +++ b/core/consensus/slots/src/slots.rs @@ -24,7 +24,6 @@ use futures::prelude::*; use futures::try_ready; use inherents::{InherentData, InherentDataProviders}; -use std::marker::PhantomData; use std::time::{Duration, Instant}; use tokio_timer::Delay; @@ -55,11 +54,11 @@ impl SignedDuration { /// Get the slot for now. Panics if `slot_duration` is 0. pub fn slot_now(&self, slot_duration: u64) -> u64 { - if self.is_positive { + (if self.is_positive { duration_now() + self.offset } else { duration_now() - self.offset - }.as_secs() / slot_duration + }.as_secs()) / slot_duration } } @@ -97,23 +96,27 @@ impl SlotInfo { } /// A stream that returns every time there is a new slot. -pub struct Slots { +pub(crate) struct Slots { last_slot: u64, slot_duration: u64, inner_delay: Option, inherent_data_providers: InherentDataProviders, - _marker: PhantomData, + timestamp_extractor: SC, } impl Slots { /// Create a new `Slots` stream. - pub fn new(slot_duration: u64, inherent_data_providers: InherentDataProviders) -> Self { + pub fn new( + slot_duration: u64, + inherent_data_providers: InherentDataProviders, + timestamp_extractor: SC, + ) -> Self { Slots { last_slot: 0, slot_duration, inner_delay: None, inherent_data_providers, - _marker: PhantomData, + timestamp_extractor, } } } @@ -123,49 +126,49 @@ impl Stream for Slots { type Error = Error; fn poll(&mut self) -> Poll, Self::Error> { - let slot_duration = self.slot_duration; - self.inner_delay = match self.inner_delay.take() { - None => { - // schedule wait. - let wait_until = Instant::now() + time_until_next(duration_now(), slot_duration); - Some(Delay::new(wait_until)) + loop { + let slot_duration = self.slot_duration; + self.inner_delay = match self.inner_delay.take() { + None => { + // schedule wait. + let wait_until = Instant::now() + time_until_next(duration_now(), slot_duration); + Some(Delay::new(wait_until)) + } + Some(d) => Some(d), + }; + + if let Some(ref mut inner_delay) = self.inner_delay { + try_ready!(inner_delay + .poll() + .map_err(Error::FaultyTimer)); } - Some(d) => Some(d), - }; - if let Some(ref mut inner_delay) = self.inner_delay { - try_ready!(inner_delay - .poll() - .map_err(Error::FaultyTimer)); - } - - // timeout has fired. - - let inherent_data = self - .inherent_data_providers - .create_inherent_data() - .map_err(|s| consensus_common::Error::InherentData(s.into_owned()))?; - let (timestamp, slot_num) = SC::extract_timestamp_and_slot(&inherent_data)?; - - // reschedule delay for next slot. - let ends_at = - Instant::now() + time_until_next(Duration::from_secs(timestamp), slot_duration); - self.inner_delay = Some(Delay::new(ends_at)); - - // never yield the same slot twice. - if slot_num > self.last_slot { - self.last_slot = slot_num; - - Ok(Async::Ready(Some(SlotInfo { - number: slot_num, - duration: self.slot_duration, - timestamp, - ends_at, - inherent_data, - }))) - } else { - // re-poll until we get a new slot. - self.poll() + // timeout has fired. + + let inherent_data = self + .inherent_data_providers + .create_inherent_data() + .map_err(|s| consensus_common::Error::InherentData(s.into_owned()))?; + let (timestamp, slot_num, offset) = self + .timestamp_extractor + .extract_timestamp_and_slot(&inherent_data)?; + // reschedule delay for next slot. + let ends_at = Instant::now() + offset + + time_until_next(Duration::from_secs(timestamp), slot_duration); + self.inner_delay = Some(Delay::new(ends_at)); + + // never yield the same slot twice. + if slot_num > self.last_slot { + self.last_slot = slot_num; + + break Ok(Async::Ready(Some(SlotInfo { + number: slot_num, + duration: self.slot_duration, + timestamp, + ends_at, + inherent_data, + }))) + } } } } diff --git a/core/executor/Cargo.toml b/core/executor/Cargo.toml index 9cc198557da0f1c57657e9a31f051fabca05c42d..50afe391db958ff096bfc10cbd1c0d9d4e940532 100644 --- a/core/executor/Cargo.toml +++ b/core/executor/Cargo.toml @@ -5,8 +5,8 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -derive_more = "0.14.0" -parity-codec = "3.3" +derive_more = "0.15.0" +parity-codec = "4.1.1" runtime_io = { package = "sr-io", path = "../sr-io" } primitives = { package = "substrate-primitives", path = "../primitives" } trie = { package = "substrate-trie", path = "../trie" } @@ -15,8 +15,8 @@ state_machine = { package = "substrate-state-machine", path = "../state-machine" runtime_version = { package = "sr-version", path = "../sr-version" } panic-handler = { package = "substrate-panic-handler", path = "../panic-handler" } wasmi = { version = "0.4.3" } -byteorder = "1.1" -lazy_static = "1.0" +byteorder = "1.3" +lazy_static = "1.3" parking_lot = "0.8.0" log = "0.4" libsecp256k1 = "0.2.1" @@ -26,6 +26,9 @@ tiny-keccak = "1.4.2" assert_matches = "1.1" wabt = "~0.7.4" hex-literal = "0.2.0" +runtime-test = { package = "substrate-runtime-test", path = "runtime-test" } +substrate-client = { path = "../client" } +substrate-offchain = { path = "../offchain/" } [features] default = [] diff --git a/core/executor/wasm/Cargo.toml b/core/executor/runtime-test/Cargo.toml similarity index 52% rename from core/executor/wasm/Cargo.toml rename to core/executor/runtime-test/Cargo.toml index e9f829746d49f0952c7da8d73e1f90bfbe17c516..26bf2f71bdcf2d25a9ef5169e73be3ced15fe5bc 100644 --- a/core/executor/wasm/Cargo.toml +++ b/core/executor/runtime-test/Cargo.toml @@ -1,20 +1,20 @@ [package] -name = "runtime-test" +name = "substrate-runtime-test" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" - -[lib] -crate-type = ["cdylib"] +build = "build.rs" [dependencies] +rstd = { package = "sr-std", path = "../../sr-std", default-features = false } runtime_io = { package = "sr-io", path = "../../sr-io", default-features = false } sandbox = { package = "sr-sandbox", path = "../../sr-sandbox", default-features = false } substrate-primitives = { path = "../../primitives", default-features = false } -[profile.release] -panic = "abort" -lto = true +[build-dependencies] +wasm-builder-runner = { package = "substrate-wasm-builder-runner", version = "1.0.2", path = "../../utils/wasm-builder-runner" } -[workspace] -members = [] +[features] +default = [ "std" ] +std = [] +no_std = [] diff --git a/core/executor/runtime-test/build.rs b/core/executor/runtime-test/build.rs new file mode 100644 index 0000000000000000000000000000000000000000..a8a5e1cba54f6a2e6836220a7462a42c4a5c1038 --- /dev/null +++ b/core/executor/runtime-test/build.rs @@ -0,0 +1,27 @@ +// 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 wasm_builder_runner::{build_current_project, WasmBuilderSource}; + +fn main() { + build_current_project( + "wasm_binary.rs", + WasmBuilderSource::CratesOrPath { + path: "../../utils/wasm-builder", + version: "1.0.4", + }, + ); +} diff --git a/core/executor/wasm/src/lib.rs b/core/executor/runtime-test/src/lib.rs similarity index 72% rename from core/executor/wasm/src/lib.rs rename to core/executor/runtime-test/src/lib.rs index 41f071ca9fef4be361c0ca66eda83cd004021b46..bca220264642851069852722486f248dc727d4f7 100644 --- a/core/executor/wasm/src/lib.rs +++ b/core/executor/runtime-test/src/lib.rs @@ -1,9 +1,11 @@ -#![no_std] +#![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(feature = "strict", deny(warnings))] -extern crate alloc; -use alloc::vec::Vec; -use alloc::slice; +// Make the WASM binary available. +#[cfg(feature = "std")] +include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); + +use rstd::{vec::Vec, slice, vec}; use runtime_io::{ set_storage, storage, clear_prefix, print, blake2_128, blake2_256, @@ -11,7 +13,7 @@ use runtime_io::{ }; macro_rules! impl_stubs { - ( $( $new_name:ident => $invoke:expr ),* ) => { + ( $( $new_name:ident => $invoke:expr, )* ) => { $( impl_stubs!(@METHOD $new_name => $invoke); )* @@ -33,7 +35,7 @@ macro_rules! impl_stubs { // 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. - ::core::mem::forget(output); + rstd::mem::forget(output); res } }; @@ -134,7 +136,42 @@ impl_stubs!( Err(sandbox::Error::OutOfBounds) => 3, }; [code].to_vec() - } + }, + test_offchain_local_storage => |_| { + let kind = substrate_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", b"asd", b""); + assert_eq!(res, true); + assert_eq!(runtime_io::local_storage_get(kind, b"test"), Some(b"".to_vec())); + + [0].to_vec() + }, + test_offchain_http => |_| { + use substrate_primitives::offchain::HttpRequestStatus; + let run = || -> Option<()> { + let id = runtime_io::http_request_start("POST", "http://localhost:12345", &[]).ok()?; + runtime_io::http_request_add_header(id, "X-Auth", "test").ok()?; + runtime_io::http_request_write_body(id, &[1, 2, 3, 4], None).ok()?; + runtime_io::http_request_write_body(id, &[], None).ok()?; + let status = runtime_io::http_response_wait(&[id], None); + assert!(status == vec![HttpRequestStatus::Finished(200)], "Expected Finished(200) status."); + let headers = runtime_io::http_response_headers(id); + assert_eq!(headers, vec![(b"X-Auth".to_vec(), b"hello".to_vec())]); + let mut buffer = vec![0; 64]; + let read = runtime_io::http_response_read_body(id, &mut buffer, None).ok()?; + assert_eq!(read, 3); + assert_eq!(&buffer[0..read], &[1, 2, 3]); + let read = runtime_io::http_response_read_body(id, &mut buffer, None).ok()?; + assert_eq!(read, 0); + + Some(()) + }; + + [if run().is_some() { 0 } else { 1 }].to_vec() + }, ); fn execute_sandboxed(code: &[u8], args: &[sandbox::TypedValue]) -> Result { diff --git a/core/executor/src/sandbox.rs b/core/executor/src/sandbox.rs index ceb5f44a260bb3bdf2a4077e549ee249c5268154..1ce300c38ad8168df3c401d8df4ee41032ab0823 100644 --- a/core/executor/src/sandbox.rs +++ b/core/executor/src/sandbox.rs @@ -341,11 +341,11 @@ impl SandboxInstance { } } -/// Error occured during instantiation of a sandboxed module. +/// Error occurred during instantiation of a sandboxed module. pub enum InstantiationError { /// Something wrong with the environment definition. It either can't /// be decoded, have a reference to a non-existent or torn down memory instance. - EnvironmentDefintionCorrupted, + EnvironmentDefinitionCorrupted, /// Provided module isn't recognized as a valid webassembly binary. ModuleDecoding, /// Module is a well-formed webassembly binary but could not be instantiated. This could @@ -361,7 +361,7 @@ fn decode_environment_definition( memories: &[Option], ) -> std::result::Result<(Imports, GuestToSupervisorFunctionMapping), InstantiationError> { let env_def = sandbox_primitives::EnvironmentDefinition::decode(&mut &raw_env_def[..]) - .ok_or_else(|| InstantiationError::EnvironmentDefintionCorrupted)?; + .ok_or_else(|| InstantiationError::EnvironmentDefinitionCorrupted)?; let mut func_map = HashMap::new(); let mut memories_map = HashMap::new(); @@ -381,8 +381,8 @@ fn decode_environment_definition( let memory_ref = memories .get(memory_idx as usize) .cloned() - .ok_or_else(|| InstantiationError::EnvironmentDefintionCorrupted)? - .ok_or_else(|| InstantiationError::EnvironmentDefintionCorrupted)?; + .ok_or_else(|| InstantiationError::EnvironmentDefinitionCorrupted)? + .ok_or_else(|| InstantiationError::EnvironmentDefinitionCorrupted)?; memories_map.insert((module, field), memory_ref); } } @@ -566,13 +566,14 @@ mod tests { 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 = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + let test_code = WASM_BINARY; let code = wabt::wat2wasm(r#" (module @@ -604,7 +605,7 @@ mod tests { #[test] fn sandbox_trap() { let mut ext = TestExternalities::::default(); - let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + let test_code = WASM_BINARY; let code = wabt::wat2wasm(r#" (module @@ -625,7 +626,7 @@ mod tests { #[test] fn sandbox_should_trap_when_heap_exhausted() { let mut ext = TestExternalities::::default(); - let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + let test_code = WASM_BINARY; let code = wabt::wat2wasm(r#" (module @@ -650,7 +651,7 @@ mod tests { #[test] fn start_called() { let mut ext = TestExternalities::::default(); - let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + let test_code = WASM_BINARY; let code = wabt::wat2wasm(r#" (module @@ -688,7 +689,7 @@ mod tests { #[test] fn invoke_args() { let mut ext = TestExternalities::::default(); - let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + let test_code = WASM_BINARY; let code = wabt::wat2wasm(r#" (module @@ -722,7 +723,7 @@ mod tests { #[test] fn return_val() { let mut ext = TestExternalities::::default(); - let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + let test_code = WASM_BINARY; let code = wabt::wat2wasm(r#" (module @@ -744,7 +745,7 @@ mod tests { #[test] fn unlinkable_module() { let mut ext = TestExternalities::::default(); - let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + let test_code = WASM_BINARY; let code = wabt::wat2wasm(r#" (module @@ -764,7 +765,7 @@ mod tests { #[test] fn corrupted_module() { let mut ext = TestExternalities::::default(); - let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + let test_code = WASM_BINARY; // Corrupted wasm file let code = &[0, 0, 0, 0, 1, 0, 0, 0]; @@ -778,7 +779,7 @@ mod tests { #[test] fn start_fn_ok() { let mut ext = TestExternalities::::default(); - let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + let test_code = WASM_BINARY; let code = wabt::wat2wasm(r#" (module @@ -801,7 +802,7 @@ mod tests { #[test] fn start_fn_traps() { let mut ext = TestExternalities::::default(); - let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + let test_code = WASM_BINARY; let code = wabt::wat2wasm(r#" (module diff --git a/core/executor/src/wasm_executor.rs b/core/executor/src/wasm_executor.rs index cbb47195de87baa5684aaa12e06a0709dc5bda33..30d5ccd54258190c955166303c9390da1d322d47 100644 --- a/core/executor/src/wasm_executor.rs +++ b/core/executor/src/wasm_executor.rs @@ -26,6 +26,7 @@ use wasmi::{ }; use state_machine::{Externalities, ChildStorageKey}; use crate::error::{Error, Result}; +use parity_codec::Encode; use primitives::{blake2_128, blake2_256, twox_64, twox_128, twox_256, ed25519, sr25519, Pair}; use primitives::offchain; use primitives::hexdisplay::HexDisplay; @@ -121,16 +122,6 @@ fn deadline_to_timestamp(deadline: u64) -> Option { } } -fn u32_to_key(key: u32) -> std::result::Result, ()> { - if key > u16::max_value() as u32 { - Err(()) - } else if key == 0 { - Ok(None) - } else { - Ok(Some(offchain::CryptoKeyId(key as u16))) - } -} - impl_function_executor!(this: FunctionExecutor<'e, E>, ext_print_utf8(utf8_data: *const u8, utf8_len: u32) => { if let Ok(utf8) = this.memory.get(utf8_data, utf8_len as usize) { @@ -720,7 +711,7 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, Ok(if res.is_ok() { 0 } else { 1 }) }, - ext_new_crypto_key(crypto: u32) -> u32 => { + ext_new_crypto_key(crypto: u32) -> u64 => { let kind = offchain::CryptoKind::try_from(crypto) .map_err(|_| "crypto kind OOB while ext_new_crypto_key: wasm")?; @@ -729,12 +720,17 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, .ok_or_else(|| "Calling unavailable API ext_new_crypto_key: wasm")?; match res { - Ok(key_id) => Ok(key_id.0 as u32), - Err(()) => Ok(u32::max_value()), + Ok(key) => Ok(key.into()), + Err(()) => Ok(u64::max_value()), } }, - ext_encrypt(key: u32, data: *const u8, data_len: u32, msg_len: *mut u32) -> *mut u8 => { - let key = u32_to_key(key) + ext_encrypt( + key: u64, + data: *const u8, + data_len: u32, + msg_len: *mut u32 + ) -> *mut u8 => { + let key = offchain::CryptoKey::try_from(key) .map_err(|_| "Key OOB while ext_encrypt: wasm")?; let message = this.memory.get(data, data_len as usize) .map_err(|_| "OOB while ext_encrypt: wasm")?; @@ -759,8 +755,48 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, Ok(offset) }, - ext_decrypt(key: u32, data: *const u8, data_len: u32, msg_len: *mut u32) -> *mut u8 => { - let key = u32_to_key(key) + ext_network_state(written_out: *mut u32) -> *mut u8 => { + let res = this.ext.offchain() + .map(|api| api.network_state()) + .ok_or_else(|| "Calling unavailable API ext_network_state: wasm")?; + + let encoded = res.encode(); + let len = encoded.len() as u32; + let offset = this.heap.allocate(len)? as u32; + this.memory.set(offset, &encoded) + .map_err(|_| "Invalid attempt to set memory in ext_network_state")?; + + this.memory.write_primitive(written_out, len) + .map_err(|_| "Invalid attempt to write written_out in ext_network_state")?; + + Ok(offset) + }, + ext_pubkey( + key: u64, + written_out: *mut u32 + ) -> *mut u8 => { + let key = offchain::CryptoKey::try_from(key) + .map_err(|_| "Key OOB while ext_decrypt: wasm")?; + let res = this.ext.offchain() + .map(|api| api.pubkey(key)) + .ok_or_else(|| "Calling unavailable API ext_authority_pubkey: wasm")?; + + let encoded = res.encode(); + let len = encoded.len() as u32; + let offset = this.heap.allocate(len)? as u32; + this.memory.set(offset, &encoded) + .map_err(|_| "Invalid attempt to set memory in ext_authority_pubkey")?; + this.memory.write_primitive(written_out, len) + .map_err(|_| "Invalid attempt to write written_out in ext_authority_pubkey")?; + Ok(offset) + }, + ext_decrypt( + key: u64, + data: *const u8, + data_len: u32, + msg_len: *mut u32 + ) -> *mut u8 => { + let key = offchain::CryptoKey::try_from(key) .map_err(|_| "Key OOB while ext_decrypt: wasm")?; let message = this.memory.get(data, data_len as usize) .map_err(|_| "OOB while ext_decrypt: wasm")?; @@ -785,8 +821,13 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, Ok(offset) }, - ext_sign(key: u32, data: *const u8, data_len: u32, sig_data_len: *mut u32) -> *mut u8 => { - let key = u32_to_key(key) + ext_sign( + key: u64, + data: *const u8, + data_len: u32, + sig_data_len: *mut u32 + ) -> *mut u8 => { + let key = offchain::CryptoKey::try_from(key) .map_err(|_| "Key OOB while ext_sign: wasm")?; let message = this.memory.get(data, data_len as usize) .map_err(|_| "OOB while ext_sign: wasm")?; @@ -812,13 +853,13 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, Ok(offset) }, ext_verify( - key: u32, + key: u64, msg: *const u8, msg_len: u32, signature: *const u8, signature_len: u32 ) -> u32 => { - let key = u32_to_key(key) + let key = offchain::CryptoKey::try_from(key) .map_err(|_| "Key OOB while ext_verify: wasm")?; let message = this.memory.get(msg, msg_len as usize) .map_err(|_| "OOB while ext_verify: wasm")?; @@ -857,24 +898,28 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, .map_err(|_| "Invalid attempt to set value in ext_random_seed")?; Ok(()) }, - ext_local_storage_set(key: *const u8, key_len: u32, value: *const u8, value_len: u32) => { + ext_local_storage_set(kind: u32, key: *const u8, key_len: u32, value: *const u8, value_len: u32) => { + let kind = offchain::StorageKind::try_from(kind) + .map_err(|_| "storage kind OOB while ext_local_storage_set: wasm")?; let key = this.memory.get(key, key_len as usize) .map_err(|_| "OOB while ext_local_storage_set: wasm")?; let value = this.memory.get(value, value_len as usize) .map_err(|_| "OOB while ext_local_storage_set: wasm")?; this.ext.offchain() - .map(|api| api.local_storage_set(&key, &value)) + .map(|api| api.local_storage_set(kind, &key, &value)) .ok_or_else(|| "Calling unavailable API ext_local_storage_set: wasm")?; Ok(()) }, - ext_local_storage_get(key: *const u8, key_len: u32, value_len: *mut u32) -> *mut u8 => { + ext_local_storage_get(kind: u32, key: *const u8, key_len: u32, value_len: *mut u32) -> *mut u8 => { + let kind = offchain::StorageKind::try_from(kind) + .map_err(|_| "storage kind OOB while ext_local_storage_get: wasm")?; let key = this.memory.get(key, key_len as usize) .map_err(|_| "OOB while ext_local_storage_get: wasm")?; let maybe_value = this.ext.offchain() - .map(|api| api.local_storage_get(&key)) + .map(|api| api.local_storage_get(kind, &key)) .ok_or_else(|| "Calling unavailable API ext_local_storage_get: wasm")?; let (offset, len) = if let Some(value) = maybe_value { @@ -891,6 +936,31 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, Ok(offset) }, + ext_local_storage_compare_and_set( + kind: u32, + key: *const u8, + key_len: u32, + old_value: *const u8, + old_value_len: u32, + new_value: *const u8, + new_value_len: u32 + ) -> u32 => { + let kind = offchain::StorageKind::try_from(kind) + .map_err(|_| "storage kind OOB while ext_local_storage_compare_and_set: wasm")?; + let key = this.memory.get(key, key_len as usize) + .map_err(|_| "OOB while ext_local_storage_compare_and_set: wasm")?; + let old_value = this.memory.get(old_value, old_value_len as usize) + .map_err(|_| "OOB while ext_local_storage_compare_and_set: wasm")?; + let new_value = this.memory.get(new_value, new_value_len as usize) + .map_err(|_| "OOB while ext_local_storage_compare_and_set: wasm")?; + + let res = this.ext.offchain() + .map(|api| api.local_storage_compare_and_set(kind, &key, &old_value, &new_value)) + .ok_or_else(|| "Calling unavailable API ext_local_storage_compare_andset: wasm")?; + + Ok(if res { 0 } else { 1 }) + + }, ext_http_request_start( method: *const u8, method_len: u32, @@ -916,7 +986,7 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, .ok_or_else(|| "Calling unavailable API ext_http_request_start: wasm")?; if let Ok(id) = id { - Ok(id.0 as u32) + Ok(id.into()) } else { Ok(u32::max_value()) } @@ -967,7 +1037,7 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, Ok(match res { Ok(()) => 0, - Err(e) => e as u8 as u32, + Err(e) => e.into(), }) }, ext_http_response_wait( @@ -1045,7 +1115,7 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, read as u32 }, Err(err) => { - u32::max_value() - err as u8 as u32 + 1 + u32::max_value() - u32::from(err) + 1 } }) }, @@ -1365,13 +1435,15 @@ mod tests { 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 = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + 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]); @@ -1380,7 +1452,7 @@ mod tests { #[test] fn panicking_should_work() { let mut ext = TestExternalities::default(); - let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + let test_code = WASM_BINARY; let output = WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_panic", &[]); assert!(output.is_err()); @@ -1396,7 +1468,7 @@ mod tests { fn storage_should_work() { let mut ext = TestExternalities::default(); ext.set_storage(b"foo".to_vec(), b"bar".to_vec()); - let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + let test_code = WASM_BINARY; let output = WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_data_in", b"Hello world").unwrap(); @@ -1418,7 +1490,7 @@ mod tests { 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 = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + 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(); @@ -1436,7 +1508,7 @@ mod tests { #[test] fn blake2_256_should_work() { let mut ext = TestExternalities::default(); - let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + let test_code = WASM_BINARY; assert_eq!( WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_blake2_256", &[]).unwrap(), blake2_256(&b""[..]).encode() @@ -1450,7 +1522,7 @@ mod tests { #[test] fn blake2_128_should_work() { let mut ext = TestExternalities::default(); - let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + let test_code = WASM_BINARY; assert_eq!( WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_blake2_128", &[]).unwrap(), blake2_128(&b""[..]).encode() @@ -1464,7 +1536,7 @@ mod tests { #[test] fn twox_256_should_work() { let mut ext = TestExternalities::default(); - let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + let test_code = WASM_BINARY; assert_eq!( WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_twox_256", &[]).unwrap(), hex!("99e9d85137db46ef4bbea33613baafd56f963c64b1f3685a4eb4abd67ff6203a") @@ -1478,7 +1550,7 @@ mod tests { #[test] fn twox_128_should_work() { let mut ext = TestExternalities::default(); - let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + let test_code = WASM_BINARY; assert_eq!( WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_twox_128", &[]).unwrap(), hex!("99e9d85137db46ef4bbea33613baafd5") @@ -1492,7 +1564,7 @@ mod tests { #[test] fn ed25519_verify_should_work() { let mut ext = TestExternalities::::default(); - let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + 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![]; @@ -1518,7 +1590,7 @@ mod tests { #[test] fn sr25519_verify_should_work() { let mut ext = TestExternalities::::default(); - let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + 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![]; @@ -1544,10 +1616,51 @@ mod tests { #[test] fn enumerated_trie_root_should_work() { let mut ext = TestExternalities::::default(); - let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + let test_code = WASM_BINARY; assert_eq!( WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_enumerated_trie_root", &[]).unwrap(), ordered_trie_root::(vec![b"zero".to_vec(), b"one".to_vec(), b"two".to_vec()].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![])); + } + + #[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: 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] + ); + } } diff --git a/core/executor/wasm/Cargo.lock b/core/executor/wasm/Cargo.lock deleted file mode 100644 index 34cd17116c3c1708e76d2923a21fc47b15d598ee..0000000000000000000000000000000000000000 --- a/core/executor/wasm/Cargo.lock +++ /dev/null @@ -1,268 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "arrayvec" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "autocfg" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "byteorder" -version = "1.3.1" -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.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "static_assertions 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hash-db" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "hash256-std-hasher" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "impl-codec" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "nodrop" -version = "0.1.13" -source = "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.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parity-codec" -version = "3.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec-derive 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parity-codec-derive" -version = "3.3.0" -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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "primitive-types" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "fixed-hash 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "impl-codec 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "uint 0.7.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.1 (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 = "quote" -version = "0.6.12" -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 = "runtime-test" -version = "2.0.0" -dependencies = [ - "sr-io 2.0.0", - "sr-sandbox 2.0.0", - "substrate-primitives 2.0.0", -] - -[[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.91" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "sr-io" -version = "2.0.0" -dependencies = [ - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (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-primitives 2.0.0", -] - -[[package]] -name = "sr-sandbox" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.1 (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-primitives 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 = "substrate-primitives" -version = "2.0.0" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hash256-std-hasher 0.12.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-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "primitive-types 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-std 2.0.0", -] - -[[package]] -name = "syn" -version = "0.15.34" -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.12 (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 = "toml" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "uint" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (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" - -[metadata] -"checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" -"checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf" -"checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" -"checksum crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" -"checksum fixed-hash 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d1a683d1234507e4f3bf2736eeddf0de1dc65996dc0164d57eba0a74bcf29489" -"checksum hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ba7fb417e5c470acdd61068c79767d0e65962e70836cf6c9dfd2409f06345ce0" -"checksum hash256-std-hasher 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f8b2027c19ec91eb304999abae7307d225cf93be42af53b0039f76e98ed5af86" -"checksum impl-codec 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d2050d823639fbeae26b2b5ba09aca8907793117324858070ade0673c49f793b" -"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" -"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" -"checksum parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dcb43c05fb71c03b4ea7327bf15694da1e0f23f19d5b1e95bab6c6d74097e336" -"checksum parity-codec-derive 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "00a486fd383382ddcb2de928364b1f82571c1e48274fc43b7667a4738ee4056c" -"checksum primitive-types 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6e8612a8dc70f26276fed6131c153ca277cf275ee0a5e2a50cd8a69c697beb8f" -"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 quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db" -"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.91 (registry+https://github.com/rust-lang/crates.io-index)" = "a72e9b96fa45ce22a4bc23da3858dfccfd60acd28a25bcd328a98fdd6bea43fd" -"checksum static_assertions 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c19be23126415861cb3a23e501d34a708f7f9b2183c5252d690941c2e69199d5" -"checksum syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)" = "a1393e4a97a19c01e900df2aec855a29f71cf02c402e2f443b8d2747c25c5dbe" -"checksum toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b8c96d7873fa7ef8bdeb3a9cda3ac48389b4154f32b9803b4bc26220b677b039" -"checksum uint 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2143cded94692b156c356508d92888acc824db5bffc0b4089732264c6fcf86d4" -"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" diff --git a/core/executor/wasm/build.sh b/core/executor/wasm/build.sh deleted file mode 100755 index 9414c8037b57933d2da64d1f562ffbdfdab32e1f..0000000000000000000000000000000000000000 --- a/core/executor/wasm/build.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash -set -e - -if cargo --version | grep -q "nightly"; then - CARGO_CMD="cargo" -else - CARGO_CMD="cargo +nightly" -fi -CARGO_INCREMENTAL=0 RUSTFLAGS="-C link-arg=--export-table" $CARGO_CMD build --target=wasm32-unknown-unknown --release "$@" -for i in test -do - wasm-gc target/wasm32-unknown-unknown/release/runtime_$i.wasm target/wasm32-unknown-unknown/release/runtime_$i.compact.wasm -done diff --git a/core/finality-grandpa/Cargo.toml b/core/finality-grandpa/Cargo.toml index bbd6297d3576e7048c6fcdabcf01f3ce2f2a51cb..3cbb56df81c11df9aacc6759282e6b593723277b 100644 --- a/core/finality-grandpa/Cargo.toml +++ b/core/finality-grandpa/Cargo.toml @@ -5,13 +5,15 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -fork-tree = { path = "../../core/util/fork-tree" } +fork-tree = { path = "../../core/utils/fork-tree" } futures = "0.1" +futures03 = { package = "futures-preview", version = "0.3.0-alpha.17", features = ["compat"] } log = "0.4" parking_lot = "0.8.0" -tokio = "0.1.7" +tokio-executor = "0.1.7" +tokio-timer = "0.2.11" rand = "0.6" -parity-codec = { version = "3.3", features = ["derive"] } +parity-codec = { version = "4.1.1", features = ["derive"] } runtime_primitives = { package = "sr-primitives", path = "../sr-primitives" } consensus_common = { package = "substrate-consensus-common", path = "../consensus/common" } substrate-primitives = { path = "../primitives" } @@ -23,14 +25,15 @@ network = { package = "substrate-network", path = "../network" } service = { package = "substrate-service", path = "../service", optional = true } srml-finality-tracker = { path = "../../srml/finality-tracker" } fg_primitives = { package = "substrate-finality-grandpa-primitives", path = "primitives" } -grandpa = { package = "finality-grandpa", version = "0.7.2", features = ["derive-codec"] } +grandpa = { package = "finality-grandpa", version = "0.8.1", features = ["derive-codec"] } [dev-dependencies] -consensus_common = { package = "substrate-consensus-common", path = "../consensus/common", features = ["test-helpers"] } +grandpa = { package = "finality-grandpa", version = "0.8.1", features = ["derive-codec", "test-helpers"] } network = { package = "substrate-network", path = "../network", features = ["test-helpers"] } keyring = { package = "substrate-keyring", path = "../keyring" } test-client = { package = "substrate-test-runtime-client", path = "../test-runtime/client"} env_logger = "0.6" +tokio = "0.1.17" [features] default = ["service-integration"] diff --git a/core/finality-grandpa/primitives/Cargo.toml b/core/finality-grandpa/primitives/Cargo.toml index 6a36d74b01788489e82407ebf6b62e96c4ac3cb5..4f3b469ca8f179ca27e5ee447727fd6df8947616 100644 --- a/core/finality-grandpa/primitives/Cargo.toml +++ b/core/finality-grandpa/primitives/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" [dependencies] client = { package = "substrate-client", path = "../../client", default-features = false } substrate-primitives = { path = "../../primitives", default-features = false } -parity-codec = { version = "3.3", default-features = false, features = ["derive"] } +parity-codec = { version = "4.1.1", default-features = false, features = ["derive"] } sr-primitives = { path = "../../sr-primitives", default-features = false } rstd = { package = "sr-std", path = "../../sr-std", default-features = false } serde = { version = "1.0", optional = true, features = ["derive"] } diff --git a/core/finality-grandpa/primitives/src/lib.rs b/core/finality-grandpa/primitives/src/lib.rs index aded32efa36e188a3bf9838523bb06f5830bf59f..77527820251837b49b996dfbf2928ee4ef7867da 100644 --- a/core/finality-grandpa/primitives/src/lib.rs +++ b/core/finality-grandpa/primitives/src/lib.rs @@ -23,7 +23,7 @@ extern crate alloc; #[cfg(feature = "std")] use serde::Serialize; -use parity_codec::{Encode, Decode}; +use parity_codec::{Encode, Decode, Codec}; use sr_primitives::{ConsensusEngineId, traits::{DigestFor, NumberFor}}; use client::decl_runtime_apis; use rstd::vec::Vec; @@ -44,16 +44,99 @@ pub const GRANDPA_ENGINE_ID: ConsensusEngineId = *b"FRNK"; /// The weight of an authority. pub type AuthorityWeight = u64; +/// The index of an authority. +pub type AuthorityIndex = u64; + /// A scheduled change of authority set. #[cfg_attr(feature = "std", derive(Debug, Serialize))] #[derive(Clone, Eq, PartialEq, Encode, Decode)] pub struct ScheduledChange { /// The new authorities after the change, along with their respective weights. - pub next_authorities: Vec<(AuthorityId, u64)>, + pub next_authorities: Vec<(AuthorityId, AuthorityWeight)>, /// The number of blocks to delay. pub delay: N, } +/// An consensus log item for GRANDPA. +#[cfg_attr(feature = "std", derive(Serialize, Debug))] +#[derive(Decode, Encode, PartialEq, Eq, Clone)] +pub enum ConsensusLog { + /// Schedule an authority set change. + /// + /// Precedence towards earlier or later digest items can be given + /// based on the rules of the chain. + /// + /// No change should be scheduled if one is already and the delay has not + /// passed completely. + /// + /// This should be a pure function: i.e. as long as the runtime can interpret + /// the digest type it should return the same result regardless of the current + /// state. + #[codec(index = "1")] + ScheduledChange(ScheduledChange), + /// Force an authority set change. + /// + /// Forced changes are applied after a delay of _imported_ blocks, + /// while pending changes are applied after a delay of _finalized_ blocks. + /// + /// Precedence towards earlier or later digest items can be given + /// based on the rules of the chain. + /// + /// No change should be scheduled if one is already and the delay has not + /// passed completely. + /// + /// This should be a pure function: i.e. as long as the runtime can interpret + /// the digest type it should return the same result regardless of the current + /// state. + #[codec(index = "2")] + ForcedChange(N, ScheduledChange), + /// Note that the authority with given index is disabled until the next change. + #[codec(index = "3")] + OnDisabled(AuthorityIndex), + /// A signal to pause the current authority set after the given delay. + /// After finalizing the block at _delay_ the authorities should stop voting. + #[codec(index = "4")] + Pause(N), + /// A signal to resume the current authority set after the given delay. + /// After authoring the block at _delay_ the authorities should resume voting. + #[codec(index = "5")] + Resume(N), +} + +impl ConsensusLog { + /// Try to cast the log entry as a contained signal. + pub fn try_into_change(self) -> Option> { + match self { + ConsensusLog::ScheduledChange(change) => Some(change), + _ => None, + } + } + + /// Try to cast the log entry as a contained forced signal. + pub fn try_into_forced_change(self) -> Option<(N, ScheduledChange)> { + match self { + ConsensusLog::ForcedChange(median, change) => Some((median, change)), + _ => None, + } + } + + /// Try to cast the log entry as a contained pause signal. + pub fn try_into_pause(self) -> Option { + match self { + ConsensusLog::Pause(delay) => Some(delay), + _ => None, + } + } + + /// Try to cast the log entry as a contained resume signal. + pub fn try_into_resume(self) -> Option { + match self { + ConsensusLog::Resume(delay) => Some(delay), + _ => None, + } + } +} + /// WASM function call to check for pending changes. pub const PENDING_CHANGE_CALL: &str = "grandpa_pending_change"; /// WASM function call to get current GRANDPA authorities. diff --git a/core/finality-grandpa/src/communication/gossip.rs b/core/finality-grandpa/src/communication/gossip.rs index ca529125ec172bd2c5ed08a51fd904fe8f304fd2..dfaa96628f2dc43699bd7297247353e50112fdae 100644 --- a/core/finality-grandpa/src/communication/gossip.rs +++ b/core/finality-grandpa/src/communication/gossip.rs @@ -46,7 +46,7 @@ //! #### Propose //! //! This is a broadcast by a known voter of the last-round estimate. - +//! //! #### Commit //! //! These are used to announce past agreement of finality. @@ -58,6 +58,21 @@ //! Sending a commit is polite when it may finalize something that the receiving peer //! was not aware of. //! +//! #### Catch Up +//! +//! These allow a peer to request another peer, which they perceive to be in a +//! later round, to provide all the votes necessary to complete a given round +//! `R`. +//! +//! It is impolite to send a catch up request for a round `R` to a peer whose +//! announced view is behind `R`. It is also impolite to send a catch up request +//! to a peer in a new different Set ID. +//! +//! The logic for issuing and tracking pending catch up requests is implemented +//! in the `GossipValidator`. A catch up request is issued anytime we see a +//! neighbor packet from a peer at a round `CATCH_UP_THRESHOLD` higher than at +//! we are. +//! //! ## Expiration //! //! We keep some amount of recent rounds' messages, but do not accept new ones from rounds @@ -78,13 +93,20 @@ use log::{trace, debug, warn}; use futures::prelude::*; use futures::sync::mpsc; -use crate::{CompactCommit, SignedMessage}; +use crate::{environment, CatchUp, CompactCommit, SignedMessage}; use super::{cost, benefit, Round, SetId}; use std::collections::{HashMap, VecDeque}; use std::time::{Duration, Instant}; const REBROADCAST_AFTER: Duration = Duration::from_secs(60 * 5); +const CATCH_UP_REQUEST_TIMEOUT: Duration = Duration::from_secs(5); +const CATCH_UP_PROCESS_TIMEOUT: Duration = Duration::from_secs(15); +/// Maximum number of rounds we are behind a peer before issuing a +/// catch up request. +const CATCH_UP_THRESHOLD: u64 = 2; + +type Report = (PeerId, i32); /// An outcome of examining a message. #[derive(Debug, PartialEq, Clone, Copy)] @@ -230,6 +252,10 @@ pub(super) enum GossipMessage { Commit(FullCommitMessage), /// A neighbor packet. Not repropagated. Neighbor(VersionedNeighborPacket>), + /// Grandpa catch up request message with round and set info. Not repropagated. + CatchUpRequest(CatchUpRequestMessage), + /// Grandpa catch up message with round and set info. Not repropagated. + CatchUp(FullCatchUpMessage), } impl From>> for GossipMessage { @@ -264,9 +290,12 @@ pub(super) struct FullCommitMessage { /// and are not repropagated. These contain information about the node's state. #[derive(Debug, Encode, Decode, Clone)] pub(super) struct NeighborPacket { - round: Round, - set_id: SetId, - commit_finalized_height: N, + /// The round the node is currently at. + pub(super) round: Round, + /// The set ID the node is currently at. + pub(super) set_id: SetId, + /// The highest finalizing commit observed. + pub(super) commit_finalized_height: N, } /// A versioned neighbor packet. @@ -284,6 +313,24 @@ impl VersionedNeighborPacket { } } +/// A catch up request for a given round (or any further round) localized by set id. +#[derive(Clone, Debug, Encode, Decode)] +pub(super) struct CatchUpRequestMessage { + /// The round that we want to catch up to. + pub(super) round: Round, + /// The voter set ID this message is from. + pub(super) set_id: SetId, +} + +/// Network level catch up message with topic information. +#[derive(Debug, Encode, Decode)] +pub(super) struct FullCatchUpMessage { + /// The voter set ID this message is from. + pub(super) set_id: SetId, + /// The compact commit message. + pub(super) message: CatchUp, +} + /// Misbehavior that peers can perform. /// /// `cost` gives a cost that can be used to perform cost/benefit analysis of a @@ -294,6 +341,10 @@ pub(super) enum Misbehavior { InvalidViewChange, // could not decode neighbor message. bytes-length of the packet. UndecodablePacket(i32), + // Bad catch up message (invalid signatures). + BadCatchUpMessage { + signatures_checked: i32, + }, // Bad commit message BadCommitMessage { signatures_checked: i32, @@ -315,7 +366,9 @@ impl Misbehavior { match *self { InvalidViewChange => cost::INVALID_VIEW_CHANGE, - UndecodablePacket(bytes) => bytes.saturating_mul(cost::PER_UNDECODABLE_BYTE), + UndecodablePacket(bytes) => bytes.saturating_mul(cost::PER_UNDECODABLE_BYTE), + BadCatchUpMessage { signatures_checked } => + cost::PER_SIGNATURE_CHECKED.saturating_mul(signatures_checked), BadCommitMessage { signatures_checked, blocks_loaded, equivocations_caught } => { let cost = cost::PER_SIGNATURE_CHECKED .saturating_mul(signatures_checked) @@ -425,6 +478,23 @@ pub(super) enum Action { Discard(i32), } +/// State of catch up request handling. +#[derive(Debug)] +enum PendingCatchUp { + /// No pending catch up requests. + None, + /// Pending catch up request which has not been answered yet. + Requesting { + who: PeerId, + request: CatchUpRequestMessage, + instant: Instant, + }, + /// Pending catch up request that was answered and is being processed. + Processing { + instant: Instant, + }, +} + struct Inner { local_view: Option>>, peers: Peers>, @@ -432,6 +502,7 @@ struct Inner { authorities: Vec, config: crate::Config, next_rebroadcast: Instant, + pending_catch_up: PendingCatchUp, } type MaybeMessage = Option<(Vec, NeighborPacket>)>; @@ -444,6 +515,7 @@ impl Inner { live_topics: KeepTopics::new(), next_rebroadcast: Instant::now() + REBROADCAST_AFTER, authorities: Vec::new(), + pending_catch_up: PendingCatchUp::None, config, } } @@ -593,18 +665,201 @@ impl Inner { Action::ProcessAndDiscard(topic, benefit::BASIC_VALIDATED_COMMIT) } + fn validate_catch_up_message(&mut self, who: &PeerId, full: &FullCatchUpMessage) + -> Action + { + match &self.pending_catch_up { + PendingCatchUp::Requesting { who: peer, request, instant } => { + if peer != who { + return Action::Discard(Misbehavior::OutOfScopeMessage.cost()); + } + + if request.set_id != full.set_id { + return Action::Discard(cost::MALFORMED_CATCH_UP); + } + + if request.round.0 > full.message.round_number { + return Action::Discard(cost::MALFORMED_CATCH_UP); + } + + if full.message.prevotes.is_empty() || full.message.precommits.is_empty() { + return Action::Discard(cost::MALFORMED_CATCH_UP); + } + + // move request to pending processing state, we won't push out + // any catch up requests until we import this one (either with a + // success or failure). + self.pending_catch_up = PendingCatchUp::Processing { + instant: instant.clone(), + }; + + // always discard catch up messages, they're point-to-point + let topic = super::global_topic::(full.set_id.0); + Action::ProcessAndDiscard(topic, benefit::BASIC_VALIDATED_CATCH_UP) + }, + _ => Action::Discard(Misbehavior::OutOfScopeMessage.cost()), + } + } + + fn note_catch_up_message_processed(&mut self) { + match &self.pending_catch_up { + PendingCatchUp::Processing { .. } => { + self.pending_catch_up = PendingCatchUp::None; + }, + state => trace!(target: "afg", + "Noted processed catch up message when state was: {:?}", + state, + ), + } + } + + fn handle_catch_up_request( + &mut self, + who: &PeerId, + request: CatchUpRequestMessage, + set_state: &environment::SharedVoterSetState, + ) -> (Option>, Action) { + let local_view = match self.local_view { + None => return (None, Action::Discard(Misbehavior::OutOfScopeMessage.cost())), + Some(ref view) => view, + }; + + if request.set_id != local_view.set_id { + // NOTE: When we're close to a set change there is potentially a + // race where the peer sent us the request before it observed that + // we had transitioned to a new set. In this case we charge a lower + // cost. + if local_view.round.0.saturating_sub(CATCH_UP_THRESHOLD) == 0 { + return (None, Action::Discard(cost::HONEST_OUT_OF_SCOPE_CATCH_UP)); + } + + return (None, Action::Discard(Misbehavior::OutOfScopeMessage.cost())); + } + + match self.peers.peer(who) { + None => + return (None, Action::Discard(Misbehavior::OutOfScopeMessage.cost())), + Some(peer) if peer.view.round >= request.round => + return (None, Action::Discard(Misbehavior::OutOfScopeMessage.cost())), + _ => {}, + } + + let last_completed_round = set_state.read().last_completed_round(); + if last_completed_round.number < request.round.0 { + return (None, Action::Discard(Misbehavior::OutOfScopeMessage.cost())); + } + + trace!(target: "afg", "Replying to catch-up request for round {} from {} with round {}", + request.round.0, + who, + last_completed_round.number, + ); + + let mut prevotes = Vec::new(); + let mut precommits = Vec::new(); + + // NOTE: the set of votes stored in `LastCompletedRound` is a minimal + // set of votes, i.e. at most one equivocation is stored per voter. The + // code below assumes this invariant is maintained when creating the + // catch up reply since peers won't accept catch-up messages that have + // too many equivocations (we exceed the fault-tolerance bound). + for vote in last_completed_round.votes { + match vote.message { + grandpa::Message::Prevote(prevote) => { + prevotes.push(grandpa::SignedPrevote { + prevote, + signature: vote.signature, + id: vote.id, + }); + }, + grandpa::Message::Precommit(precommit) => { + precommits.push(grandpa::SignedPrecommit { + precommit, + signature: vote.signature, + id: vote.id, + }); + }, + _ => {}, + } + } + + let (base_hash, base_number) = last_completed_round.base; + + let catch_up = CatchUp:: { + round_number: last_completed_round.number, + prevotes, + precommits, + base_hash, + base_number, + }; + + let full_catch_up = GossipMessage::CatchUp::(FullCatchUpMessage { + set_id: request.set_id, + message: catch_up, + }); + + (Some(full_catch_up), Action::Discard(cost::CATCH_UP_REPLY)) + } + + fn try_catch_up(&mut self, who: &PeerId) -> (Option>, Option) { + let mut catch_up = None; + let mut report = None; + + // if the peer is on the same set and ahead of us by a margin bigger + // than `CATCH_UP_THRESHOLD` then we should ask it for a catch up + // message. + if let (Some(peer), Some(local_view)) = (self.peers.peer(who), &self.local_view) { + if peer.view.set_id == local_view.set_id && + peer.view.round.0.saturating_sub(CATCH_UP_THRESHOLD) > local_view.round.0 + { + // send catch up request if allowed + let round = peer.view.round.0 - 1; // peer.view.round is > 0 + let request = CatchUpRequestMessage { + set_id: peer.view.set_id, + round: Round(round), + }; + + let (catch_up_allowed, catch_up_report) = self.note_catch_up_request(who, &request); + + if catch_up_allowed { + trace!(target: "afg", "Sending catch-up request for round {} to {}", + round, + who, + ); + + catch_up = Some(GossipMessage::::CatchUpRequest(request)); + } + + report = catch_up_report; + } + } + + (catch_up, report) + } + fn import_neighbor_message(&mut self, who: &PeerId, update: NeighborPacket>) - -> (Vec, Action) + -> (Vec, Action, Option>, Option) { - let (cb, topics) = match self.peers.update_peer_state(who, update) { - Ok(view) => (100i32, view.map(|view| neighbor_topics::(view))), - Err(misbehavior) => (misbehavior.cost(), None) + let update_res = self.peers.update_peer_state(who, update); + + let (cost_benefit, topics) = match update_res { + Ok(view) => + (benefit::NEIGHBOR_MESSAGE, view.map(|view| neighbor_topics::(view))), + Err(misbehavior) => + (misbehavior.cost(), None), + }; + + let (catch_up, report) = match update_res { + Ok(_) => self.try_catch_up(who), + _ => (None, None), }; let neighbor_topics = topics.unwrap_or_default(); - // always discard, it's valid for one hop. - (neighbor_topics, Action::Discard(cb)) + // always discard neighbor messages, it's only valid for one hop. + let action = Action::Discard(cost_benefit); + + (neighbor_topics, action, catch_up, report) } fn multicast_neighbor_packet(&self) -> MaybeMessage { @@ -619,20 +874,55 @@ impl Inner { (peers, packet) }) } + + fn note_catch_up_request( + &mut self, + who: &PeerId, + catch_up_request: &CatchUpRequestMessage, + ) -> (bool, Option) { + let report = match &self.pending_catch_up { + PendingCatchUp::Requesting { who: peer, instant, .. } => + if instant.elapsed() <= CATCH_UP_REQUEST_TIMEOUT { + return (false, None); + } else { + // report peer for timeout + Some((peer.clone(), cost::CATCH_UP_REQUEST_TIMEOUT)) + }, + PendingCatchUp::Processing { instant, .. } => + if instant.elapsed() < CATCH_UP_PROCESS_TIMEOUT { + return (false, None); + } else { + None + }, + _ => None, + }; + + self.pending_catch_up = PendingCatchUp::Requesting { + who: who.clone(), + request: catch_up_request.clone(), + instant: Instant::now(), + }; + + (true, report) + } } /// A validator for GRANDPA gossip messages. pub(super) struct GossipValidator { inner: parking_lot::RwLock>, + set_state: environment::SharedVoterSetState, report_sender: mpsc::UnboundedSender, } impl GossipValidator { /// Create a new gossip-validator. This initialized the current set to 0. - pub(super) fn new(config: crate::Config) -> (GossipValidator, ReportStream) { + pub(super) fn new(config: crate::Config, set_state: environment::SharedVoterSetState) + -> (GossipValidator, ReportStream) + { let (tx, rx) = mpsc::unbounded(); let val = GossipValidator { inner: parking_lot::RwLock::new(Inner::new(config)), + set_state, report_sender: tx, }; @@ -670,26 +960,50 @@ impl GossipValidator { } } + /// Note that we've processed a catch up message. + pub(super) fn note_catch_up_message_processed(&self) { + self.inner.write().note_catch_up_message_processed(); + } + fn report(&self, who: PeerId, cost_benefit: i32) { let _ = self.report_sender.unbounded_send(PeerReport { who, cost_benefit }); } pub(super) fn do_validate(&self, who: &PeerId, mut data: &[u8]) - -> (Action, Vec) + -> (Action, Vec, Option>) { let mut broadcast_topics = Vec::new(); + let mut peer_reply = None; + let action = { match GossipMessage::::decode(&mut data) { Some(GossipMessage::VoteOrPrecommit(ref message)) => self.inner.write().validate_round_message(who, message), Some(GossipMessage::Commit(ref message)) => self.inner.write().validate_commit_message(who, message), Some(GossipMessage::Neighbor(update)) => { - let (topics, action) = self.inner.write().import_neighbor_message( + let (topics, action, catch_up, report) = self.inner.write().import_neighbor_message( who, update.into_neighbor_packet(), ); + if let Some((peer, cost_benefit)) = report { + self.report(peer, cost_benefit); + } + broadcast_topics = topics; + peer_reply = catch_up; + action + } + Some(GossipMessage::CatchUp(ref message)) + => self.inner.write().validate_catch_up_message(who, message), + Some(GossipMessage::CatchUpRequest(request)) => { + let (reply, action) = self.inner.write().handle_catch_up_request( + who, + request, + &self.set_state, + ); + + peer_reply = reply; action } None => { @@ -702,7 +1016,7 @@ impl GossipValidator { } }; - (action, broadcast_topics) + (action, broadcast_topics, peer_reply) } } @@ -734,9 +1048,13 @@ impl network_gossip::Validator for GossipValidator fn validate(&self, context: &mut dyn ValidatorContext, who: &PeerId, data: &[u8]) -> network_gossip::ValidationResult { - let (action, broadcast_topics) = self.do_validate(who, data); + let (action, broadcast_topics, peer_reply) = self.do_validate(who, data); // not with lock held! + if let Some(msg) = peer_reply { + context.send_message(who, msg.encode()); + } + for topic in broadcast_topics { context.send_topic(who, topic, false); } @@ -817,6 +1135,8 @@ impl network_gossip::Validator for GossipValidator && Some(full.message.target_number) > peer_best_commit } Some(GossipMessage::Neighbor(_)) => false, + Some(GossipMessage::CatchUpRequest(_)) => false, + Some(GossipMessage::CatchUp(_)) => false, Some(GossipMessage::VoteOrPrecommit(_)) => false, // should not be the case. } }) @@ -910,6 +1230,7 @@ impl> Future for ReportingTask { #[cfg(test)] mod tests { use super::*; + use super::environment::SharedVoterSetState; use network_gossip::Validator as GossipValidatorT; use network::test::Block; @@ -923,6 +1244,33 @@ mod tests { } } + // dummy voter set state + fn voter_set_state() -> SharedVoterSetState { + use crate::authorities::AuthoritySet; + use crate::environment::{CompletedRound, CompletedRounds, HasVoted, VoterSetState}; + use grandpa::round::State as RoundState; + use substrate_primitives::H256; + + let state = RoundState::genesis((H256::zero(), 0)); + let base = state.prevote_ghost.unwrap(); + let voters = AuthoritySet::genesis(Vec::new()); + let set_state = VoterSetState::Live { + completed_rounds: CompletedRounds::new( + CompletedRound { + state, + number: 0, + votes: Vec::new(), + base, + }, + 0, + &voters, + ), + current_round: HasVoted::No, + }; + + set_state.into() + } + #[test] fn view_vote_rules() { let view = View { round: Round(100), set_id: SetId(1), last_commit: Some(1000u64) }; @@ -1064,7 +1412,10 @@ mod tests { #[test] fn messages_not_expired_immediately() { - let (val, _) = GossipValidator::::new(config()); + let (val, _) = GossipValidator::::new( + config(), + voter_set_state(), + ); let set_id = 1; @@ -1096,7 +1447,10 @@ mod tests { fn message_from_unknown_authority_discarded() { assert!(cost::UNKNOWN_VOTER != cost::BAD_SIGNATURE); - let (val, _) = GossipValidator::::new(config()); + let (val, _) = GossipValidator::::new( + config(), + voter_set_state(), + ); let set_id = 1; let auth = AuthorityId::from_raw([1u8; 32]); let peer = PeerId::random(); @@ -1134,4 +1488,122 @@ mod tests { assert_eq!(unknown_voter, Action::Discard(cost::UNKNOWN_VOTER)); assert_eq!(bad_sig, Action::Discard(cost::BAD_SIGNATURE)); } + + #[test] + fn unsolicited_catch_up_messages_discarded() { + let (val, _) = GossipValidator::::new( + config(), + voter_set_state(), + ); + + let set_id = 1; + let auth = AuthorityId::from_raw([1u8; 32]); + let peer = PeerId::random(); + + val.note_set(SetId(set_id), vec![auth.clone()], |_, _| {}); + val.note_round(Round(0), |_, _| {}); + + let validate_catch_up = || { + let mut inner = val.inner.write(); + inner.validate_catch_up_message(&peer, &FullCatchUpMessage { + set_id: SetId(set_id), + message: grandpa::CatchUp { + round_number: 10, + prevotes: Default::default(), + precommits: Default::default(), + base_hash: Default::default(), + base_number: Default::default(), + } + }) + }; + + // the catch up is discarded because we have no pending request + assert_eq!(validate_catch_up(), Action::Discard(cost::OUT_OF_SCOPE_MESSAGE)); + + let noted = val.inner.write().note_catch_up_request( + &peer, + &CatchUpRequestMessage { + set_id: SetId(set_id), + round: Round(10), + } + ); + + assert!(noted.0); + + // catch up is allowed because we have requested it, but it's rejected + // because it's malformed (empty prevotes and precommits) + assert_eq!(validate_catch_up(), Action::Discard(cost::MALFORMED_CATCH_UP)); + } + + #[test] + fn unanswerable_catch_up_requests_discarded() { + // create voter set state with round 1 completed + let set_state: SharedVoterSetState = { + let mut completed_rounds = voter_set_state().read().completed_rounds(); + + assert!(completed_rounds.push(environment::CompletedRound { + number: 1, + state: grandpa::round::State::genesis(Default::default()), + base: Default::default(), + votes: Default::default(), + })); + + let set_state = environment::VoterSetState::::Live { + completed_rounds, + current_round: environment::HasVoted::No, + }; + + set_state.into() + }; + + let (val, _) = GossipValidator::::new( + config(), + set_state.clone(), + ); + + let set_id = 1; + let auth = AuthorityId::from_raw([1u8; 32]); + let peer = PeerId::random(); + + val.note_set(SetId(set_id), vec![auth.clone()], |_, _| {}); + val.note_round(Round(2), |_, _| {}); + + // add the peer making the request to the validator, + // otherwise it is discarded + let mut inner = val.inner.write(); + inner.peers.new_peer(peer.clone()); + + let res = inner.handle_catch_up_request( + &peer, + CatchUpRequestMessage { + set_id: SetId(set_id), + round: Round(10), + }, + &set_state, + ); + + // we're at round 2, 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)); + + let res = inner.handle_catch_up_request( + &peer, + CatchUpRequestMessage { + set_id: SetId(set_id), + round: Round(1), + }, + &set_state, + ); + + // a catch up request for round 1 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!(res.1, Action::Discard(cost::CATCH_UP_REPLY)); + }, + _ => panic!("expected catch up message"), + }; + } } diff --git a/core/finality-grandpa/src/communication/mod.rs b/core/finality-grandpa/src/communication/mod.rs index cbcfef0d41a2ebd65f9e5a29471d760874d5a088..4707dede78d65cd6b3e618d367479c985f847c43 100644 --- a/core/finality-grandpa/src/communication/mod.rs +++ b/core/finality-grandpa/src/communication/mod.rs @@ -29,11 +29,12 @@ use std::sync::Arc; -use grandpa::voter_set::VoterSet; +use grandpa::{voter, voter_set::VoterSet}; use grandpa::Message::{Prevote, Precommit, PrimaryPropose}; use futures::prelude::*; use futures::sync::{oneshot, mpsc}; use log::{debug, trace}; +use tokio_executor::Executor; use parity_codec::{Encode, Decode}; use substrate_primitives::{ed25519, Pair}; use substrate_telemetry::{telemetry, CONSENSUS_DEBUG, CONSENSUS_INFO}; @@ -41,10 +42,13 @@ use runtime_primitives::traits::{Block as BlockT, Hash as HashT, Header as Heade use network::{consensus_gossip as network_gossip, NetworkService}; use network_gossip::ConsensusMessage; -use crate::{Error, Message, SignedMessage, Commit, CompactCommit}; +use crate::{ + CatchUp, Commit, CommunicationIn, CommunicationOut, CompactCommit, Error, + Message, SignedMessage, +}; use crate::environment::HasVoted; use gossip::{ - GossipMessage, FullCommitMessage, VoteOrPrecommitMessage, GossipValidator + GossipMessage, FullCatchUpMessage, FullCommitMessage, VoteOrPrecommitMessage, GossipValidator }; use substrate_primitives::ed25519::{Public as AuthorityId, Signature as AuthoritySignature}; @@ -60,6 +64,7 @@ pub use fg_primitives::GRANDPA_ENGINE_ID; mod cost { pub(super) const PAST_REJECTION: i32 = -50; pub(super) const BAD_SIGNATURE: i32 = -100; + pub(super) const MALFORMED_CATCH_UP: i32 = -1000; pub(super) const MALFORMED_COMMIT: i32 = -1000; pub(super) const FUTURE_MESSAGE: i32 = -500; pub(super) const UNKNOWN_VOTER: i32 = -150; @@ -68,13 +73,21 @@ mod cost { pub(super) const PER_UNDECODABLE_BYTE: i32 = -5; pub(super) const PER_SIGNATURE_CHECKED: i32 = -25; pub(super) const PER_BLOCK_LOADED: i32 = -10; + pub(super) const INVALID_CATCH_UP: i32 = -5000; pub(super) const INVALID_COMMIT: i32 = -5000; pub(super) const OUT_OF_SCOPE_MESSAGE: i32 = -500; + pub(super) const CATCH_UP_REQUEST_TIMEOUT: i32 = -200; + + // cost of answering a catch up request + pub(super) const CATCH_UP_REPLY: i32 = -200; + pub(super) const HONEST_OUT_OF_SCOPE_CATCH_UP: i32 = -200; } // benefit scalars for reporting peers. mod benefit { + pub(super) const NEIGHBOR_MESSAGE: i32 = 100; pub(super) const ROUND_MESSAGE: i32 = 100; + pub(super) const BASIC_VALIDATED_CATCH_UP: i32 = 200; pub(super) const BASIC_VALIDATED_COMMIT: i32 = 100; pub(super) const PER_EQUIVOCATION: i32 = 10; } @@ -213,12 +226,6 @@ impl Stream for NetworkStream { } } -/// The result of processing a commit. -pub(crate) enum CommitProcessingOutcome { - Good, - Bad, -} - /// Bridge between the underlying network service, gossiping consensus messages and Grandpa pub(crate) struct NetworkBridge> { service: N, @@ -234,21 +241,21 @@ impl> NetworkBridge { pub(crate) fn new( service: N, config: crate::Config, - set_state: Option<&crate::environment::VoterSetState>, + set_state: crate::environment::SharedVoterSetState, on_exit: impl Future + Clone + Send + 'static, ) -> ( Self, impl futures::Future + Send + 'static, ) { - let (validator, report_stream) = GossipValidator::new(config); + let (validator, report_stream) = GossipValidator::new(config, set_state.clone()); let validator = Arc::new(validator); service.register_validator(validator.clone()); - if let Some(set_state) = set_state { + { // register all previous votes with the gossip service so that they're // available to peers potentially stuck on a previous round. - let completed = set_state.completed_rounds(); + let completed = set_state.read().completed_rounds(); let (set_id, voters) = completed.set_info(); validator.note_set(SetId(set_id), voters.to_vec(), |_, _| {}); for round in completed.iter() { @@ -291,25 +298,23 @@ impl> NetworkBridge { let startup_work = futures::future::lazy(move || { // lazily spawn these jobs onto their own tasks. the lazy future has access // to tokio globals, which aren't available outside. - tokio::spawn(rebroadcast_job.select(on_exit.clone()).then(|_| Ok(()))); - tokio::spawn(reporting_job.select(on_exit.clone()).then(|_| Ok(()))); + let mut executor = tokio_executor::DefaultExecutor::current(); + executor.spawn(Box::new(rebroadcast_job.select(on_exit.clone()).then(|_| Ok(())))) + .expect("failed to spawn grandpa rebroadcast job task"); + executor.spawn(Box::new(reporting_job.select(on_exit.clone()).then(|_| Ok(())))) + .expect("failed to spawn grandpa reporting job task"); Ok(()) }); (bridge, startup_work) } - /// Get the round messages for a round in the current set ID. These are signature-checked. - pub(crate) fn round_communication( + /// Note the beginning of a new round to the `GossipValidator`. + pub(crate) fn note_round( &self, round: Round, set_id: SetId, - voters: Arc>, - local_key: Option>, - has_voted: HasVoted, - ) -> ( - impl Stream,Error=Error>, - impl Sink,SinkError=Error>, + voters: &VoterSet, ) { // is a no-op if currently in that set. self.validator.note_set( @@ -328,6 +333,25 @@ impl> NetworkBridge { GossipMessage::::from(neighbor).encode() ), ); + } + + /// Get the round messages for a round in the current set ID. These are signature-checked. + pub(crate) fn round_communication( + &self, + round: Round, + set_id: SetId, + voters: Arc>, + local_key: Option>, + has_voted: HasVoted, + ) -> ( + impl Stream,Error=Error>, + impl Sink,SinkError=Error>, + ) { + self.note_round( + round, + set_id, + &*voters, + ); let locals = local_key.and_then(|pair| { let public = pair.public(); @@ -418,8 +442,8 @@ impl> NetworkBridge { voters: Arc>, is_voter: bool, ) -> ( - impl Stream, impl FnMut(CommitProcessingOutcome)), Error = Error>, - impl Sink), SinkError = Error>, + impl Stream, Error = Error>, + impl Sink, SinkError = Error>, ) { self.validator.note_set( set_id, @@ -438,16 +462,123 @@ impl> NetworkBridge { self.validator.clone(), ); + let outgoing = outgoing.with(|out| { + let voter::CommunicationOut::Commit(round, commit) = out; + Ok((round, commit)) + }); + (incoming, outgoing) } } fn incoming_global>( - service: N, + mut service: N, topic: B::Hash, voters: Arc>, gossip_validator: Arc>, -) -> impl Stream, impl FnMut(CommitProcessingOutcome)), Error = Error> { +) -> impl Stream, Error = Error> { + let process_commit = move | + msg: FullCommitMessage, + mut notification: network_gossip::TopicNotification, + service: &mut N, + gossip_validator: &Arc>, + voters: &VoterSet, + | { + let precommits_signed_by: Vec = + msg.message.auth_data.iter().map(move |(_, a)| { + format!("{}", a) + }).collect(); + + telemetry!(CONSENSUS_INFO; "afg.received_commit"; + "contains_precommits_signed_by" => ?precommits_signed_by, + "target_number" => ?msg.message.target_number.clone(), + "target_hash" => ?msg.message.target_hash.clone(), + ); + + if let Err(cost) = check_compact_commit::( + &msg.message, + voters, + msg.round, + msg.set_id, + ) { + if let Some(who) = notification.sender { + service.report(who, cost); + } + + return None; + } + + let round = msg.round.0; + let commit = msg.message; + let finalized_number = commit.target_number; + let gossip_validator = gossip_validator.clone(); + let service = service.clone(); + let cb = move |outcome| match outcome { + voter::CommitProcessingOutcome::Good(_) => { + // if it checks out, gossip it. not accounting for + // any discrepancy between the actual ghost and the claimed + // finalized number. + gossip_validator.note_commit_finalized( + finalized_number, + |to, neighbor_msg| service.send_message( + to, + GossipMessage::::from(neighbor_msg).encode(), + ), + ); + + service.gossip_message(topic, notification.message.clone(), false); + } + voter::CommitProcessingOutcome::Bad(_) => { + // report peer and do not gossip. + if let Some(who) = notification.sender.take() { + service.report(who, cost::INVALID_COMMIT); + } + } + }; + + let cb = voter::Callback::Work(Box::new(cb)); + + Some(voter::CommunicationIn::Commit(round, commit, cb)) + }; + + let process_catch_up = move | + msg: FullCatchUpMessage, + mut notification: network_gossip::TopicNotification, + service: &mut N, + gossip_validator: &Arc>, + voters: &VoterSet, + | { + let gossip_validator = gossip_validator.clone(); + let service = service.clone(); + + if let Err(cost) = check_catch_up::( + &msg.message, + voters, + msg.set_id, + ) { + if let Some(who) = notification.sender { + service.report(who, cost); + } + + return None; + } + + let cb = move |outcome| { + if let voter::CatchUpProcessingOutcome::Bad(_) = outcome { + // report peer + if let Some(who) = notification.sender.take() { + service.report(who, cost::INVALID_CATCH_UP); + } + } + + gossip_validator.note_catch_up_message_processed(); + }; + + let cb = voter::Callback::Work(Box::new(cb)); + + Some(voter::CommunicationIn::CatchUp(msg.message, cb)) + }; + service.messages_for(topic) .filter_map(|notification| { // this could be optimized by decoding piecewise. @@ -459,66 +590,16 @@ fn incoming_global>( }) .filter_map(move |(notification, msg)| { match msg { - GossipMessage::Commit(msg) => { - let precommits_signed_by: Vec = - msg.message.auth_data.iter().map(move |(_, a)| { - format!("{}", a) - }).collect(); - telemetry!(CONSENSUS_INFO; "afg.received_commit"; - "contains_precommits_signed_by" => ?precommits_signed_by, - "target_number" => ?msg.message.target_number.clone(), - "target_hash" => ?msg.message.target_hash.clone(), - ); - if let Err(cost) = check_compact_commit::( - &msg.message, - &*voters, - msg.round, - msg.set_id, - ) { - if let Some(who) = notification.sender { - service.report(who, cost); - } - None - } else { - Some((msg, notification, service.clone())) - } - }, + GossipMessage::Commit(msg) => + process_commit(msg, notification, &mut service, &gossip_validator, &*voters), + GossipMessage::CatchUp(msg) => + process_catch_up(msg, notification, &mut service, &gossip_validator, &*voters), _ => { debug!(target: "afg", "Skipping unknown message type"); return None; } } }) - .map(move |(msg, mut notification, service)| { - let round = msg.round.0; - let commit = msg.message; - let finalized_number = commit.target_number; - let gossip_validator = gossip_validator.clone(); - let cb = move |outcome| match outcome { - CommitProcessingOutcome::Good => { - // if it checks out, gossip it. not accounting for - // any discrepancy between the actual ghost and the claimed - // finalized number. - gossip_validator.note_commit_finalized( - finalized_number, - |to, neighbor_msg| service.send_message( - to, - GossipMessage::::from(neighbor_msg).encode(), - ), - ); - - service.gossip_message(topic, notification.message.clone(), false); - } - CommitProcessingOutcome::Bad => { - // report peer and do not gossip. - if let Some(who) = notification.sender.take() { - service.report(who, cost::INVALID_COMMIT); - } - } - }; - - (round, commit, cb) - }) .map_err(|()| Error::Network(format!("Failed to receive message on unbounded stream"))) } @@ -653,7 +734,8 @@ impl> Sink for OutgoingMessages } } -// checks a compact commit. returns `None` if it was bad and +// checks a compact commit. returns the cost associated with processing it if +// the commit was bad. fn check_compact_commit( msg: &CompactCommit, voters: &VoterSet, @@ -712,6 +794,114 @@ fn check_compact_commit( Ok(()) } +// checks a catch up. returns the cost associated with processing it if +// the catch up was bad. +fn check_catch_up( + msg: &CatchUp, + voters: &VoterSet, + set_id: SetId, +) -> Result<(), i32> { + // 4f + 1 = equivocations from f voters. + let f = voters.total_weight() - voters.threshold(); + let full_threshold = voters.total_weight() + f; + + // check total weight is not out of range for a set of votes. + fn check_weight<'a>( + voters: &'a VoterSet, + votes: impl Iterator, + full_threshold: u64, + ) -> Result<(), i32> { + let mut total_weight = 0; + + for id in votes { + if let Some(weight) = voters.info(&id).map(|info| info.weight()) { + total_weight += weight; + if total_weight > full_threshold { + return Err(cost::MALFORMED_CATCH_UP); + } + } else { + debug!(target: "afg", "Skipping catch up message containing unknown voter {}", id); + return Err(cost::MALFORMED_CATCH_UP); + } + } + + if total_weight < voters.threshold() { + return Err(cost::MALFORMED_CATCH_UP); + } + + Ok(()) + }; + + check_weight( + voters, + msg.prevotes.iter().map(|vote| &vote.id), + full_threshold, + )?; + + check_weight( + voters, + msg.precommits.iter().map(|vote| &vote.id), + full_threshold, + )?; + + fn check_signatures<'a, B, I>( + messages: I, + round: u64, + set_id: u64, + mut signatures_checked: usize, + ) -> Result where + B: BlockT, + I: Iterator, &'a AuthorityId, &'a AuthoritySignature)>, + { + use crate::communication::gossip::Misbehavior; + + for (msg, id, sig) in messages { + signatures_checked += 1; + + if let Err(()) = check_message_sig::( + &msg, + id, + sig, + round, + set_id, + ) { + debug!(target: "afg", "Bad catch up message signature {}", id); + telemetry!(CONSENSUS_DEBUG; "afg.bad_catch_up_msg_signature"; "id" => ?id); + + let cost = Misbehavior::BadCatchUpMessage { + signatures_checked: signatures_checked as i32, + }.cost(); + + return Err(cost); + } + } + + Ok(signatures_checked) + } + + // check signatures on all contained prevotes. + let signatures_checked = check_signatures::( + msg.prevotes.iter().map(|vote| { + (grandpa::Message::Prevote(vote.prevote.clone()), &vote.id, &vote.signature) + }), + msg.round_number, + set_id.0, + 0, + )?; + + // check signatures on all contained precommits. + let _ = check_signatures::( + msg.precommits.iter().map(|vote| { + (grandpa::Message::Precommit(vote.precommit.clone()), &vote.id, &vote.signature) + }), + msg.round_number, + set_id.0, + signatures_checked, + )?; + + Ok(()) +} + /// An output sink for commit messages. struct CommitsOut> { network: N, diff --git a/core/finality-grandpa/src/communication/periodic.rs b/core/finality-grandpa/src/communication/periodic.rs index c6121370421bccefffb524e5afb3c9929a36e6f6..8490ff2f794ebcd789b8cb05ec46c029b7771838 100644 --- a/core/finality-grandpa/src/communication/periodic.rs +++ b/core/finality-grandpa/src/communication/periodic.rs @@ -21,7 +21,7 @@ use futures::prelude::*; use futures::sync::mpsc; use runtime_primitives::traits::{NumberFor, Block as BlockT}; use network::PeerId; -use tokio::timer::Delay; +use tokio_timer::Delay; use log::warn; use parity_codec::Encode; diff --git a/core/finality-grandpa/src/communication/tests.rs b/core/finality-grandpa/src/communication/tests.rs index f2b50ab80c2b6288db97a6416da27612b67f9a74..5760b3936cd987ea79c65cc07b781be2205d5a93 100644 --- a/core/finality-grandpa/src/communication/tests.rs +++ b/core/finality-grandpa/src/communication/tests.rs @@ -26,6 +26,7 @@ use std::sync::Arc; use keyring::AuthorityKeyring; use parity_codec::Encode; +use crate::environment::SharedVoterSetState; use super::gossip::{self, GossipValidator}; use super::{AuthorityId, VoterSet, Round, SetId}; @@ -92,6 +93,18 @@ impl super::Network for TestNetwork { } } +impl network_gossip::ValidatorContext for TestNetwork { + fn broadcast_topic(&mut self, _: Hash, _: bool) { } + + fn broadcast_message(&mut self, _: Hash, _: Vec, _: bool) { } + + fn send_message(&mut self, who: &network::PeerId, data: Vec) { + >::send_message(self, vec![who.clone()], data); + } + + fn send_topic(&mut self, _: &network::PeerId, _: Hash, _: bool) { } +} + struct Tester { net_handle: super::NetworkBridge, gossip_validator: Arc>, @@ -125,8 +138,38 @@ fn config() -> crate::Config { } } +// dummy voter set state +fn voter_set_state() -> SharedVoterSetState { + use crate::authorities::AuthoritySet; + use crate::environment::{CompletedRound, CompletedRounds, HasVoted, VoterSetState}; + use grandpa::round::State as RoundState; + use substrate_primitives::H256; + + let state = RoundState::genesis((H256::zero(), 0)); + let base = state.prevote_ghost.unwrap(); + let voters = AuthoritySet::genesis(Vec::new()); + let set_state = VoterSetState::Live { + completed_rounds: CompletedRounds::new( + CompletedRound { + state, + number: 0, + votes: Vec::new(), + base, + }, + 0, + &voters, + ), + current_round: HasVoted::No, + }; + + set_state.into() +} + // needs to run in a tokio runtime. -fn make_test_network() -> impl Future { +fn make_test_network() -> ( + impl Future, + TestNetwork, +) { let (tx, rx) = mpsc::unbounded(); let net = TestNetwork { sender: tx }; @@ -145,15 +188,18 @@ fn make_test_network() -> impl Future { let (bridge, startup_work) = super::NetworkBridge::new( net.clone(), config(), - None, + voter_set_state(), Exit, ); - startup_work.map(move |()| Tester { - gossip_validator: bridge.validator.clone(), - net_handle: bridge, - events: rx, - }) + ( + startup_work.map(move |()| Tester { + gossip_validator: bridge.validator.clone(), + net_handle: bridge, + events: rx, + }), + net, + ) } fn make_ids(keys: &[AuthorityKeyring]) -> Vec<(AuthorityId, u64)> { @@ -217,7 +263,7 @@ fn good_commit_leads_to_relay() { let id = network::PeerId::random(); let global_topic = super::global_topic::(set_id); - let test = make_test_network() + let test = make_test_network().0 .and_then(move |tester| { // register a peer. tester.gossip_validator.new_peer(&mut NoopContext, &id, network::config::Roles::FULL); @@ -228,7 +274,7 @@ fn good_commit_leads_to_relay() { let (commits_in, _) = tester.net_handle.global_communication(SetId(1), voter_set, false); { - let (action, _) = tester.gossip_validator.do_validate(&id, &encoded_commit[..]); + let (action, ..) = tester.gossip_validator.do_validate(&id, &encoded_commit[..]); match action { gossip::Action::ProcessAndDiscard(t, _) => assert_eq!(t, global_topic), _ => panic!("wrong expected outcome from initial commit validation"), @@ -257,8 +303,12 @@ fn good_commit_leads_to_relay() { // when the commit comes in, we'll tell the callback it was good. let handle_commit = commits_in.into_future() .map(|(item, _)| { - let (_, _, mut callback) = item.unwrap(); - (callback)(super::CommitProcessingOutcome::Good); + match item.unwrap() { + grandpa::voter::CommunicationIn::Commit(_, _, mut callback) => { + callback.run(grandpa::voter::CommitProcessingOutcome::good()); + }, + _ => panic!("commit expected"), + } }) .map_err(|_| panic!("could not process commit")); @@ -328,7 +378,7 @@ fn bad_commit_leads_to_report() { let id = network::PeerId::random(); let global_topic = super::global_topic::(set_id); - let test = make_test_network() + let test = make_test_network().0 .and_then(move |tester| { // register a peer. tester.gossip_validator.new_peer(&mut NoopContext, &id, network::config::Roles::FULL); @@ -339,7 +389,7 @@ fn bad_commit_leads_to_report() { let (commits_in, _) = tester.net_handle.global_communication(SetId(1), voter_set, false); { - let (action, _) = tester.gossip_validator.do_validate(&id, &encoded_commit[..]); + let (action, ..) = tester.gossip_validator.do_validate(&id, &encoded_commit[..]); match action { gossip::Action::ProcessAndDiscard(t, _) => assert_eq!(t, global_topic), _ => panic!("wrong expected outcome from initial commit validation"), @@ -368,8 +418,12 @@ fn bad_commit_leads_to_report() { // when the commit comes in, we'll tell the callback it was good. let handle_commit = commits_in.into_future() .map(|(item, _)| { - let (_, _, mut callback) = item.unwrap(); - (callback)(super::CommitProcessingOutcome::Bad); + match item.unwrap() { + grandpa::voter::CommunicationIn::Commit(_, _, mut callback) => { + callback.run(grandpa::voter::CommitProcessingOutcome::bad()); + }, + _ => panic!("commit expected"), + } }) .map_err(|_| panic!("could not process commit")); @@ -393,3 +447,61 @@ fn bad_commit_leads_to_report() { current_thread::block_on_all(test).unwrap(); } + +#[test] +fn peer_with_higher_view_leads_to_catch_up_request() { + let id = network::PeerId::random(); + + let (tester, mut net) = make_test_network(); + let test = tester + .and_then(move |tester| { + // register a peer. + tester.gossip_validator.new_peer(&mut NoopContext, &id, network::config::Roles::FULL); + Ok((tester, id)) + }) + .and_then(move |(tester, id)| { + // send neighbor message at round 10 and height 50 + let result = tester.gossip_validator.validate( + &mut net, + &id, + &gossip::GossipMessage::::from(gossip::NeighborPacket { + set_id: SetId(0), + round: Round(10), + commit_finalized_height: 50, + }).encode(), + ); + + // neighbor packets are always discard + match result { + network_gossip::ValidationResult::Discard => {}, + _ => panic!("wrong expected outcome from neighbor validation"), + } + + // a catch up request should be sent to the peer for round - 1 + tester.filter_network_events(move |event| match event { + Event::SendMessage(peers, message) => { + assert_eq!( + peers, + vec![id.clone()], + ); + + assert_eq!( + message, + gossip::GossipMessage::::CatchUpRequest( + gossip::CatchUpRequestMessage { + set_id: SetId(0), + round: Round(9), + } + ).encode(), + ); + + true + }, + _ => false, + }) + .map_err(|_| panic!("could not watch for peer send message")) + .map(|_| ()) + }); + + current_thread::block_on_all(test).unwrap(); +} diff --git a/core/finality-grandpa/src/environment.rs b/core/finality-grandpa/src/environment.rs index 763301dc977dc5282da48b53222be116b3763450..e3189ecd92c951354d1e470ad3e2d99dc9177235 100644 --- a/core/finality-grandpa/src/environment.rs +++ b/core/finality-grandpa/src/environment.rs @@ -22,11 +22,12 @@ use std::time::{Duration, Instant}; use log::{debug, warn, info}; use parity_codec::{Decode, Encode}; use futures::prelude::*; -use tokio::timer::Delay; +use tokio_timer::Delay; use parking_lot::RwLock; use client::{ - backend::Backend, BlockchainEvents, CallExecutor, Client, error::Error as ClientError + backend::Backend, BlockchainEvents, CallExecutor, Client, error::Error as ClientError, + utils::is_descendent_of, }; use grandpa::{ BlockNumberOps, Equivocation, Error as GrandpaError, round::State as RoundState, @@ -50,9 +51,17 @@ use crate::authorities::{AuthoritySet, SharedAuthoritySet}; use crate::consensus_changes::SharedConsensusChanges; use crate::justification::GrandpaJustification; use crate::until_imported::UntilVoteTargetImported; -use fg_primitives::AuthorityId; +use fg_primitives::{AuthorityId, AuthoritySignature}; -/// Data about a completed round. +type HistoricalVotes = grandpa::HistoricalVotes< + ::Hash, + NumberFor, + AuthoritySignature, + AuthorityId, +>; + +/// Data about a completed round. The set of votes that is stored must be +/// minimal, i.e. at most one equivocation is stored per voter. #[derive(Debug, Clone, Decode, Encode, PartialEq)] pub struct CompletedRound { /// The round number. @@ -177,6 +186,16 @@ impl VoterSetState { completed_rounds.clone(), } } + + /// Returns the last completed round. + pub(crate) fn last_completed_round(&self) -> CompletedRound { + match self { + VoterSetState::Live { completed_rounds, .. } => + completed_rounds.last().clone(), + VoterSetState::Paused { completed_rounds } => + completed_rounds.last().clone(), + } + } } /// Whether we've voted already during a prior run of the program. @@ -636,7 +655,7 @@ where round: u64, state: RoundState>, base: (Block::Hash, NumberFor), - votes: Vec>, + historical_votes: &HistoricalVotes, ) -> Result<(), Self::Error> { debug!( target: "afg", "Voter {} completed round {} in set {}. Estimate = {:?}, Finalized in round = {:?}", @@ -650,6 +669,9 @@ where self.update_voter_set_state(|voter_set_state| { let mut completed_rounds = voter_set_state.completed_rounds(); + // TODO: Future integration will store the prevote and precommit index. See #2611. + let votes = historical_votes.seen().clone(); + // NOTE: the Environment assumes that rounds are *always* completed in-order. if !completed_rounds.push(CompletedRound { number: round, @@ -969,42 +991,3 @@ pub(crate) fn canonical_at_height, RA>( Ok(Some(current.hash())) } - -/// Returns a function for checking block ancestry, the returned function will -/// return `true` if the given hash (second parameter) is a descendent of the -/// base (first parameter). If the `current` parameter is defined, it should -/// represent the current block `hash` and its `parent hash`, if given the -/// function that's returned will assume that `hash` isn't part of the local DB -/// yet, and all searches in the DB will instead reference the parent. -pub fn is_descendent_of<'a, B, E, Block: BlockT, RA>( - client: &'a Client, - current: Option<(&'a H256, &'a H256)>, -) -> impl Fn(&H256, &H256) -> Result + 'a -where B: Backend, - E: CallExecutor + Send + Sync, -{ - move |base, hash| { - if base == hash { return Ok(false); } - - let mut hash = hash; - if let Some((current_hash, current_parent_hash)) = current { - if base == current_hash { return Ok(false); } - if hash == current_hash { - if base == current_parent_hash { - return Ok(true); - } else { - hash = current_parent_hash; - } - } - } - - let tree_route = client::blockchain::tree_route( - #[allow(deprecated)] - client.backend().blockchain(), - BlockId::Hash(*hash), - BlockId::Hash(*base), - )?; - - Ok(tree_route.common_block().hash == *base) - } -} diff --git a/core/finality-grandpa/src/import.rs b/core/finality-grandpa/src/import.rs index 7feec92be893a0bc756aadc26006d70dbf3ae5b1..c7300232c2abcac2401fd6ff62c3158007fd52f7 100644 --- a/core/finality-grandpa/src/import.rs +++ b/core/finality-grandpa/src/import.rs @@ -25,9 +25,10 @@ use client::{blockchain, CallExecutor, Client}; use client::blockchain::HeaderBackend; use client::backend::Backend; use client::runtime_api::ApiExt; +use client::utils::is_descendent_of; use consensus_common::{ BlockImport, Error as ConsensusError, - ImportBlock, ImportResult, JustificationImport, + BlockImportParams, ImportResult, JustificationImport, SelectChain, import_queue::CacheKeyId, }; use fg_primitives::GrandpaApi; @@ -42,7 +43,7 @@ use substrate_primitives::{H256, Blake2Hasher}; use crate::{Error, CommandOrError, NewAuthoritySet, VoterCommand}; use crate::authorities::{AuthoritySet, SharedAuthoritySet, DelayKind, PendingChange}; use crate::consensus_changes::SharedConsensusChanges; -use crate::environment::{finalize_block, is_descendent_of}; +use crate::environment::finalize_block; use crate::justification::GrandpaJustification; /// A block-import handler for GRANDPA. @@ -63,6 +64,21 @@ pub struct GrandpaBlockImport, RA, PRA, SC> { api: Arc, } +impl, RA, PRA, SC: Clone> Clone for + GrandpaBlockImport +{ + fn clone(&self) -> Self { + GrandpaBlockImport { + inner: self.inner.clone(), + select_chain: self.select_chain.clone(), + authority_set: self.authority_set.clone(), + send_voter_commands: self.send_voter_commands.clone(), + consensus_changes: self.consensus_changes.clone(), + api: self.api.clone(), + } + } +} + impl, RA, PRA, SC> JustificationImport for GrandpaBlockImport where NumberFor: grandpa::BlockNumberOps, @@ -76,7 +92,8 @@ impl, RA, PRA, SC> JustificationImport { type Error = ConsensusError; - fn on_start(&self, link: &mut dyn consensus_common::import_queue::Link) { + fn on_start(&mut self) -> Vec<(Block::Hash, NumberFor)> { + let mut out = Vec::new(); let chain_info = self.inner.info().chain; // request justifications for all pending changes for which change blocks have already been imported @@ -94,16 +111,18 @@ impl, RA, PRA, SC> JustificationImport if let Ok(Some(hash)) = effective_block_hash { if let Ok(Some(header)) = self.inner.header(&BlockId::Hash(hash)) { if *header.number() == pending_change.effective_number() { - link.request_justification(&header.hash(), *header.number()); + out.push((header.hash(), *header.number())); } } } } } + + out } fn import_justification( - &self, + &mut self, hash: Block::Hash, number: NumberFor, justification: Justification, @@ -226,7 +245,7 @@ where } } - fn make_authorities_changes<'a>(&'a self, block: &mut ImportBlock, hash: Block::Hash) + fn make_authorities_changes<'a>(&'a self, block: &mut BlockImportParams, hash: Block::Hash) -> Result, ConsensusError> { // when we update the authorities, we need to hold the lock @@ -387,7 +406,7 @@ impl, RA, PRA, SC> BlockImport { type Error = ConsensusError; - fn import_block(&self, mut block: ImportBlock, new_cache: HashMap>) + fn import_block(&mut self, mut block: BlockImportParams, new_cache: HashMap>) -> Result { let hash = block.post_header().hash(); @@ -407,7 +426,7 @@ impl, RA, PRA, SC> BlockImport // we don't want to finalize on `inner.import_block` let mut justification = block.justification.take(); let enacts_consensus_change = !new_cache.is_empty(); - let import_result = self.inner.import_block(block, new_cache); + let import_result = (&*self.inner).import_block(block, new_cache); let mut imported_aux = { match import_result { @@ -501,7 +520,7 @@ impl, RA, PRA, SC> BlockImport } fn check_block( - &self, + &mut self, hash: Block::Hash, parent_hash: Block::Hash, ) -> Result { @@ -545,7 +564,7 @@ where /// If `enacts_change` is set to true, then finalizing this block *must* /// enact an authority set change, the function will panic otherwise. fn import_justification( - &self, + &mut self, hash: Block::Hash, number: NumberFor, justification: Justification, diff --git a/core/finality-grandpa/src/lib.rs b/core/finality-grandpa/src/lib.rs index e7410ca44a1ebdbe43b55e9e5f33246dbc75797a..534a5d9256036273a846e2637b1b341d598bed60 100644 --- a/core/finality-grandpa/src/lib.rs +++ b/core/finality-grandpa/src/lib.rs @@ -28,9 +28,9 @@ //! 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`. This requires a -//! `Network` implementation. The returned future should be driven to completion and -//! will finalize blocks in the background. +//! Next, use the `LinkHalf` and a local configuration to `run_grandpa_voter`. +//! This requires a `Network` implementation. The returned future should be +//! driven to completion and will finalize blocks in the background. //! //! # Changing authority sets //! @@ -102,7 +102,7 @@ pub use observer::run_grandpa_observer; use aux_schema::PersistentData; use environment::{CompletedRound, CompletedRounds, Environment, HasVoted, SharedVoterSetState, VoterSetState}; use import::GrandpaBlockImport; -use until_imported::UntilCommitBlocksImported; +use until_imported::UntilGlobalMessageBlocksImported; use communication::NetworkBridge; use service::TelemetryOnConnect; use fg_primitives::AuthoritySignature; @@ -129,19 +129,64 @@ pub type PrimaryPropose = grandpa::PrimaryPropose<::Hash pub type Prevote = grandpa::Prevote<::Hash, NumberFor>; /// A precommit message for this chain's block type. pub type Precommit = grandpa::Precommit<::Hash, NumberFor>; +/// A catch up message for this chain's block type. +pub type CatchUp = grandpa::CatchUp< + ::Hash, + NumberFor, + AuthoritySignature, + AuthorityId, +>; /// A commit message for this chain's block type. pub type Commit = grandpa::Commit< ::Hash, NumberFor, AuthoritySignature, - AuthorityId + AuthorityId, >; /// A compact commit message for this chain's block type. pub type CompactCommit = grandpa::CompactCommit< ::Hash, NumberFor, AuthoritySignature, - AuthorityId + AuthorityId, +>; +/// A global communication input stream for commits and catch up messages. Not +/// exposed publicly, used internally to simplify types in the communication +/// layer. +type CommunicationIn = grandpa::voter::CommunicationIn< + ::Hash, + NumberFor, + AuthoritySignature, + AuthorityId, +>; + +/// Global communication input stream for commits and catch up messages, with +/// the hash type not being derived from the block, useful for forcing the hash +/// to some type (e.g. `H256`) when the compiler can't do the inference. +type CommunicationInH = grandpa::voter::CommunicationIn< + H, + NumberFor, + AuthoritySignature, + AuthorityId, +>; + +/// A global communication sink for commits. Not exposed publicly, used +/// internally to simplify types in the communication layer. +type CommunicationOut = grandpa::voter::CommunicationOut< + ::Hash, + NumberFor, + AuthoritySignature, + AuthorityId, +>; + +/// Global communication sink for commits with the hash type not being derived +/// from the block, useful for forcing the hash to some type (e.g. `H256`) when +/// the compiler can't do the inference. +type CommunicationOutH = grandpa::voter::CommunicationOut< + H, + NumberFor, + AuthoritySignature, + AuthorityId, >; /// Configuration for the GRANDPA service. @@ -179,7 +224,7 @@ pub enum Error { /// An invariant has been violated (e.g. not finalizing pending change blocks in-order) Safety(String), /// A timer failed to fire. - Timer(::tokio::timer::Error), + Timer(tokio_timer::Error), } impl From for Error { @@ -358,11 +403,11 @@ fn global_communication, B, E, N, RA>( network: &NetworkBridge, ) -> ( impl Stream< - Item = voter::CommunicationIn, AuthoritySignature, AuthorityId>, + Item = CommunicationInH, Error = CommandOrError>, >, impl Sink< - SinkItem = voter::CommunicationOut, AuthoritySignature, AuthorityId>, + SinkItem = CommunicationOutH, SinkError = CommandOrError>, >, ) where @@ -378,37 +423,21 @@ fn global_communication, B, E, N, RA>( .unwrap_or(false); // verification stream - let (commit_in, commit_out) = network.global_communication( + let (global_in, global_out) = network.global_communication( communication::SetId(set_id), voters.clone(), is_voter, ); - // block commit messages until relevant blocks are imported. - let commit_in = UntilCommitBlocksImported::new( + // block commit and catch up messages until relevant blocks are imported. + let global_in = UntilGlobalMessageBlocksImported::new( client.import_notification_stream(), client.clone(), - commit_in, + global_in, ); - let commits_in = commit_in.map_err(CommandOrError::from); - let commits_out = commit_out.sink_map_err(CommandOrError::from); - - let global_in = commits_in.map(|(round, commit, mut callback)| { - let callback = voter::Callback::Work(Box::new(move |outcome| match outcome { - voter::CommitProcessingOutcome::Good(_) => - callback(communication::CommitProcessingOutcome::Good), - voter::CommitProcessingOutcome::Bad(_) => - callback(communication::CommitProcessingOutcome::Bad), - })); - voter::CommunicationIn::Commit(round, commit, callback) - }); - - // NOTE: eventually this will also handle catch-up requests - let global_out = commits_out.with(|global| match global { - voter::CommunicationOut::Commit(round, commit) => Ok((round, commit)), - _ => unimplemented!(), - }); + let global_in = global_in.map_err(CommandOrError::from); + let global_out = global_out.sink_map_err(CommandOrError::from); (global_in, global_out) } @@ -443,7 +472,7 @@ fn register_finality_tracker_inherent_data_provider, N, RA, SC, X> { +pub struct GrandpaParams, N, RA, SC, X> { /// Configuration for the GRANDPA service. pub config: Config, /// A link to the block import worker. @@ -455,7 +484,7 @@ pub struct GrandpaParams<'a, B, E, Block: BlockT, N, RA, SC, X> { /// Handle to a future that will resolve on exit. pub on_exit: X, /// If supplied, can be used to hook on telemetry connection established events. - pub telemetry_on_connect: Option>, + pub telemetry_on_connect: Option, } /// Run a GRANDPA voter as a task. Provide configuration and a link to a @@ -497,13 +526,13 @@ pub fn run_grandpa_voter, N, RA, SC, X>( let (network, network_startup) = NetworkBridge::new( network, config.clone(), - Some(&set_state.read()), + set_state.clone(), on_exit.clone(), ); register_finality_tracker_inherent_data_provider(client.clone(), &inherent_data_providers)?; - if let Some(telemetry_on_connect) = telemetry_on_connect { + let telemetry_task = if let Some(telemetry_on_connect) = telemetry_on_connect { let authorities = authority_set.clone(); let events = telemetry_on_connect.telemetry_connection_sinks .for_each(move |_| { @@ -520,10 +549,11 @@ pub fn run_grandpa_voter, N, RA, SC, X>( ); Ok(()) }) - .then(|_| Ok(())); - let events = events.select(telemetry_on_connect.on_exit).then(|_| Ok(())); - telemetry_on_connect.executor.spawn(events); - } + .then(|_| -> Result<(), ()> { Ok(()) }); + futures::future::Either::A(events) + } else { + futures::future::Either::B(futures::future::empty()) + }; let voters = authority_set.current_authorities(); let initial_environment = Arc::new(Environment { @@ -723,7 +753,11 @@ pub fn run_grandpa_voter, N, RA, SC, X>( let voter_work = network_startup.and_then(move |()| voter_work); - Ok(voter_work.select(on_exit).then(|_| Ok(()))) + // Make sure that `telemetry_task` doesn't accidentally finish and kill grandpa. + let telemetry_task = telemetry_task + .then(|_| futures::future::empty::<(), ()>()); + + Ok(voter_work.select(on_exit).select2(telemetry_task).then(|_| Ok(()))) } #[deprecated(since = "1.1", note = "Please switch to run_grandpa_voter.")] diff --git a/core/finality-grandpa/src/light_import.rs b/core/finality-grandpa/src/light_import.rs index ec1f638b480e1e5f9d77cfef02e5670b3818e36b..842f70fff2b5d886549bd099f5d0213a409022ec 100644 --- a/core/finality-grandpa/src/light_import.rs +++ b/core/finality-grandpa/src/light_import.rs @@ -28,10 +28,11 @@ use client::{ }; use parity_codec::{Encode, Decode}; use consensus_common::{ - import_queue::{Verifier, SharedFinalityProofRequestBuilder}, - BlockOrigin, BlockImport, FinalityProofImport, ImportBlock, ImportResult, ImportedAux, - Error as ConsensusError, FinalityProofRequestBuilder, + import_queue::Verifier, + BlockOrigin, BlockImport, FinalityProofImport, BlockImportParams, ImportResult, ImportedAux, + Error as ConsensusError, }; +use network::config::{BoxFinalityProofRequestBuilder, FinalityProofRequestBuilder}; use runtime_primitives::Justification; use runtime_primitives::traits::{ NumberFor, Block as BlockT, Header as HeaderT, ProvideRuntimeApi, DigestFor, @@ -85,6 +86,16 @@ pub struct GrandpaLightBlockImport, RA> { data: Arc>>, } +impl, RA> Clone for GrandpaLightBlockImport { + fn clone(&self) -> Self { + GrandpaLightBlockImport { + client: self.client.clone(), + authority_set_provider: self.authority_set_provider.clone(), + data: self.data.clone(), + } + } +} + /// Mutable data of light block importer. struct LightImportData> { last_finalized: Block::Hash, @@ -101,8 +112,8 @@ struct LightAuthoritySet { impl, RA> GrandpaLightBlockImport { /// Create finality proof request builder. - pub fn create_finality_proof_request_builder(&self) -> SharedFinalityProofRequestBuilder { - Arc::new(GrandpaFinalityProofRequestBuilder(self.data.clone())) as _ + pub fn create_finality_proof_request_builder(&self) -> BoxFinalityProofRequestBuilder { + Box::new(GrandpaFinalityProofRequestBuilder(self.data.clone())) as _ } } @@ -117,8 +128,8 @@ impl, RA> BlockImport type Error = ConsensusError; fn import_block( - &self, - block: ImportBlock, + &mut self, + block: BlockImportParams, new_cache: HashMap>, ) -> Result { do_import_block::<_, _, _, _, GrandpaJustification>( @@ -127,7 +138,7 @@ impl, RA> BlockImport } fn check_block( - &self, + &mut self, hash: Block::Hash, parent_hash: Block::Hash, ) -> Result { @@ -145,19 +156,22 @@ impl, RA> FinalityProofImport { type Error = ConsensusError; - fn on_start(&self, link: &mut dyn consensus_common::import_queue::Link) { + fn on_start(&mut self) -> Vec<(Block::Hash, NumberFor)> { + let mut out = Vec::new(); let chain_info = self.client.info().chain; let data = self.data.read(); for (pending_number, pending_hash) in data.consensus_changes.pending_changes() { if *pending_number > chain_info.finalized_number && *pending_number <= chain_info.best_number { - link.request_finality_proof(pending_hash, *pending_number); + out.push((pending_hash.clone(), *pending_number)); } } + + out } fn import_finality_proof( - &self, + &mut self, hash: Block::Hash, number: NumberFor, finality_proof: Vec, @@ -204,7 +218,7 @@ impl LightAuthoritySet { struct GrandpaFinalityProofRequestBuilder>(Arc>>); impl> FinalityProofRequestBuilder for GrandpaFinalityProofRequestBuilder { - fn build_request_data(&self, _hash: &B::Hash) -> Vec { + fn build_request_data(&mut self, _hash: &B::Hash) -> Vec { let data = self.0.read(); make_finality_proof_request( data.last_finalized, @@ -215,9 +229,9 @@ impl> FinalityProofRequestBuilder for GrandpaFinalityPro /// Try to import new block. fn do_import_block, RA, J>( - client: &Client, + mut client: &Client, data: &mut LightImportData, - mut block: ImportBlock, + mut block: BlockImportParams, new_cache: HashMap>, ) -> Result where @@ -234,7 +248,7 @@ fn do_import_block, RA, J>( // we don't want to finalize on `inner.import_block` let justification = block.justification.take(); let enacts_consensus_change = !new_cache.is_empty(); - let import_result = client.import_block(block, new_cache); + let import_result = BlockImport::import_block(&mut client, block, new_cache); let mut imported_aux = match import_result { Ok(ImportResult::Imported(aux)) => aux, @@ -535,6 +549,19 @@ pub mod tests { pub GrandpaLightBlockImport ); + impl, RA> Clone + for NoJustificationsImport where + NumberFor: grandpa::BlockNumberOps, + B: Backend + 'static, + E: CallExecutor + 'static + Clone + Send + Sync, + DigestFor: Encode, + RA: Send + Sync, + { + fn clone(&self) -> Self { + NoJustificationsImport(self.0.clone()) + } + } + impl, RA> BlockImport for NoJustificationsImport where NumberFor: grandpa::BlockNumberOps, @@ -546,8 +573,8 @@ pub mod tests { type Error = ConsensusError; fn import_block( - &self, - mut block: ImportBlock, + &mut self, + mut block: BlockImportParams, new_cache: HashMap>, ) -> Result { block.justification.take(); @@ -555,7 +582,7 @@ pub mod tests { } fn check_block( - &self, + &mut self, hash: Block::Hash, parent_hash: Block::Hash, ) -> Result { @@ -573,12 +600,12 @@ pub mod tests { { type Error = ConsensusError; - fn on_start(&self, link: &mut dyn consensus_common::import_queue::Link) { - self.0.on_start(link) + fn on_start(&mut self) -> Vec<(Block::Hash, NumberFor)> { + self.0.on_start() } fn import_finality_proof( - &self, + &mut self, hash: Block::Hash, number: NumberFor, finality_proof: Vec, @@ -614,7 +641,7 @@ pub mod tests { authority_set: LightAuthoritySet::genesis(vec![(AuthorityId::from_raw([1; 32]), 1)]), consensus_changes: ConsensusChanges::empty(), }; - let block = ImportBlock { + let block = BlockImportParams { origin: BlockOrigin::Own, header: Header { number: 1, diff --git a/core/finality-grandpa/src/observer.rs b/core/finality-grandpa/src/observer.rs index 4738fd3ed6330729b73a1f5b55f0a7eeee923188..2c0818c2d70957def110832da26ee3020aeadf65 100644 --- a/core/finality-grandpa/src/observer.rs +++ b/core/finality-grandpa/src/observer.rs @@ -30,7 +30,7 @@ use runtime_primitives::traits::{NumberFor, Block as BlockT}; use substrate_primitives::{H256, Blake2Hasher}; use crate::{ - AuthoritySignature, global_communication, CommandOrError, Config, environment, + global_communication, CommandOrError, CommunicationIn, Config, environment, LinkHalf, Network, aux_schema::PersistentData, VoterCommand, VoterSetState, }; use crate::authorities::SharedAuthoritySet; @@ -57,22 +57,24 @@ impl<'a, Block: BlockT, B, E, RA> grandpa::Chain, RA, S>( +fn grandpa_observer, RA, S, F>( client: &Arc>, authority_set: &SharedAuthoritySet>, consensus_changes: &SharedConsensusChanges>, voters: &Arc>, last_finalized_number: NumberFor, commits: S, + note_round: F, ) -> impl Future>> where NumberFor: BlockNumberOps, B: Backend, E: CallExecutor + Send + Sync, RA: Send + Sync, S: Stream< - Item = voter::CommunicationIn, AuthoritySignature, AuthorityId>, + Item = CommunicationIn, Error = CommandOrError>, >, + F: Fn(u64), { let authority_set = authority_set.clone(); let consensus_changes = consensus_changes.clone(); @@ -85,8 +87,8 @@ fn grandpa_observer, RA, S>( let commit = grandpa::Commit::from(commit); (round, commit, callback) }, - voter::CommunicationIn::Auxiliary(_) => { - // ignore aux messages + voter::CommunicationIn::CatchUp(..) => { + // ignore catch up messages return future::ok(last_finalized_number); }, }; @@ -124,6 +126,10 @@ fn grandpa_observer, RA, S>( Err(e) => return future::err(e), }; + // note that we've observed completion of this round through the commit, + // and that implies that the next round has started. + note_round(round + 1); + grandpa::process_commit_validation_result(validation_result, callback); // proceed processing with new finalized block number @@ -167,15 +173,9 @@ pub fn run_grandpa_observer, N, RA, SC>( } = link; let PersistentData { authority_set, consensus_changes, set_state } = persistent_data; + let initial_state = (authority_set, consensus_changes, set_state.clone(), voter_commands_rx.into_future()); - let (network, network_startup) = NetworkBridge::new( - network, - config.clone(), - None, - on_exit.clone(), - ); - - let initial_state = (authority_set, consensus_changes, set_state, voter_commands_rx.into_future()); + let (network, network_startup) = NetworkBridge::new(network, config.clone(), set_state, on_exit.clone()); let observer_work = future::loop_fn(initial_state, move |state| { let (authority_set, consensus_changes, set_state, voter_commands_rx) = state; @@ -194,6 +194,21 @@ pub fn run_grandpa_observer, N, RA, SC>( let last_finalized_number = client.info().chain.finalized_number; + // NOTE: since we are not using `round_communication` we have to + // manually note the round with the gossip validator, otherwise we won't + // relay round messages. we want all full nodes to contribute to vote + // availability. + let note_round = { + let network = network.clone(); + let voters = voters.clone(); + + move |round| network.note_round( + crate::communication::Round(round), + crate::communication::SetId(set_id), + &*voters, + ) + }; + // create observer for the current set let observer = grandpa_observer( &client, @@ -202,6 +217,7 @@ pub fn run_grandpa_observer, N, RA, SC>( &voters, last_finalized_number, global_in, + note_round, ); let handle_voter_command = move |command, voter_commands_rx| { diff --git a/core/finality-grandpa/src/tests.rs b/core/finality-grandpa/src/tests.rs index 8afa495334397894492edaefd9688a5b67e8fb09..698996995f450551f50fc86be39f2deb8c39a60d 100644 --- a/core/finality-grandpa/src/tests.rs +++ b/core/finality-grandpa/src/tests.rs @@ -19,9 +19,9 @@ use super::*; use network::test::{Block, DummySpecialization, Hash, TestNetFactory, Peer, PeersClient}; use network::test::{PassThroughVerifier}; -use network::config::{ProtocolConfig, Roles}; -use network::consensus_gossip as network_gossip; +use network::config::{ProtocolConfig, Roles, BoxFinalityProofRequestBuilder}; use parking_lot::Mutex; +use futures03::{StreamExt as _, TryStreamExt as _}; use tokio::runtime::current_thread; use keyring::ed25519::{Keyring as AuthorityKeyring}; use client::{ @@ -30,10 +30,8 @@ use client::{ LongestChain, }; use test_client::{self, runtime::BlockNumber}; -use consensus_common::{BlockOrigin, ForkChoiceStrategy, ImportedAux, ImportBlock, ImportResult}; -use consensus_common::import_queue::{SharedBlockImport, SharedJustificationImport, SharedFinalityProofImport, - SharedFinalityProofRequestBuilder, -}; +use consensus_common::{BlockOrigin, ForkChoiceStrategy, ImportedAux, BlockImportParams, ImportResult}; +use consensus_common::import_queue::{BoxBlockImport, BoxJustificationImport, BoxFinalityProofImport}; use std::collections::{HashMap, HashSet}; use std::result; use parity_codec::Decode; @@ -44,7 +42,6 @@ use fg_primitives::AuthorityId; use authorities::AuthoritySet; use finality_proof::{FinalityProofProvider, AuthoritySetForFinalityProver, AuthoritySetForFinalityChecker}; -use communication::GRANDPA_ENGINE_ID; use consensus_changes::ConsensusChanges; type PeerData = @@ -62,16 +59,14 @@ type PeerData = type GrandpaPeer = Peer; struct GrandpaTestNet { - peers: Vec>, + peers: Vec, test_config: TestApi, - started: bool, } impl GrandpaTestNet { fn new(test_config: TestApi, n_peers: usize) -> Self { let mut net = GrandpaTestNet { peers: Vec::with_capacity(n_peers), - started: false, test_config, }; let config = Self::default_config(); @@ -92,7 +87,6 @@ impl TestNetFactory for GrandpaTestNet { GrandpaTestNet { peers: Vec::new(), test_config: Default::default(), - started: false, } } @@ -111,10 +105,10 @@ impl TestNetFactory for GrandpaTestNet { fn make_block_import(&self, client: PeersClient) -> ( - SharedBlockImport, - Option>, - Option>, - Option>, + BoxBlockImport, + Option>, + Option>, + Option>, PeerData, ) { @@ -129,8 +123,9 @@ impl TestNetFactory for GrandpaTestNet { Arc::new(self.test_config.clone()), select_chain, ).expect("Could not create block import for fresh peer."); - let shared_import = Arc::new(import); - (shared_import.clone(), Some(shared_import), None, None, Mutex::new(Some(link))) + let justification_import = Box::new(import.clone()); + let block_import = Box::new(import); + (block_import, Some(justification_import), None, None, Mutex::new(Some(link))) }, PeersClient::Light(ref client) => { use crate::light_import::tests::light_block_import_without_justifications; @@ -144,8 +139,9 @@ impl TestNetFactory for GrandpaTestNet { Arc::new(self.test_config.clone()) ).expect("Could not create block import for fresh peer."); let finality_proof_req_builder = import.0.create_finality_proof_request_builder(); - let shared_import = Arc::new(import); - (shared_import.clone(), None, Some(shared_import), Some(finality_proof_req_builder), Mutex::new(None)) + let proof_import = Box::new(import.clone()); + let block_import = Box::new(import); + (block_import, None, Some(proof_import), Some(finality_proof_req_builder), Mutex::new(None)) }, } } @@ -163,112 +159,17 @@ impl TestNetFactory for GrandpaTestNet { } } - fn uses_tokio(&self) -> bool { - true - } - - fn peer(&self, i: usize) -> &GrandpaPeer { - &self.peers[i] + fn peer(&mut self, i: usize) -> &mut GrandpaPeer { + &mut self.peers[i] } - fn peers(&self) -> &Vec> { + fn peers(&self) -> &Vec { &self.peers } - fn mut_peers>)>(&mut self, closure: F) { + fn mut_peers)>(&mut self, closure: F) { closure(&mut self.peers); } - - fn started(&self) -> bool { - self.started - } - - fn set_started(&mut self, new: bool) { - self.started = new; - } -} - -#[derive(Clone)] -struct MessageRouting { - inner: Arc>, - peer_id: usize, -} - -impl MessageRouting { - fn new(inner: Arc>, peer_id: usize,) -> Self { - MessageRouting { - inner, - peer_id, - } - } -} - -impl Network for MessageRouting { - type In = Box + Send>; - - /// Get a stream of messages for a specific gossip topic. - fn messages_for(&self, topic: Hash) -> Self::In { - let inner = self.inner.lock(); - let peer = inner.peer(self.peer_id); - - let messages = peer.consensus_gossip_messages_for( - GRANDPA_ENGINE_ID, - topic, - ); - - let messages = messages.map_err( - move |_| panic!("Messages for topic {} dropped too early", topic) - ); - - Box::new(messages) - } - - fn register_validator(&self, v: Arc>) { - let inner = self.inner.lock(); - let peer = inner.peer(self.peer_id); - peer.with_gossip(move |gossip, context| { - gossip.register_validator(context, GRANDPA_ENGINE_ID, v); - }); - } - - fn gossip_message(&self, topic: Hash, data: Vec, force: bool) { - let inner = self.inner.lock(); - inner.peer(self.peer_id).gossip_message( - topic, - GRANDPA_ENGINE_ID, - data, - force, - ); - } - - fn send_message(&self, who: Vec, data: Vec) { - let inner = self.inner.lock(); - let peer = inner.peer(self.peer_id); - - peer.with_gossip(move |gossip, ctx| for who in &who { - gossip.send_message( - ctx, - who, - network_gossip::ConsensusMessage { - engine_id: GRANDPA_ENGINE_ID, - data: data.clone(), - } - ) - }) - } - - fn register_gossip_message(&self, _topic: Hash, _data: Vec) { - // NOTE: only required to restore previous state on startup - // not required for tests currently - } - - fn report(&self, _who: network::PeerId, _cost_benefit: i32) { - - } - - fn announce(&self, _block: Hash) { - - } } #[derive(Clone)] @@ -440,7 +341,6 @@ impl AuthoritySetForFinalityChecker for TestApi { } const TEST_GOSSIP_DURATION: Duration = Duration::from_millis(500); -const TEST_ROUTING_INTERVAL: Duration = Duration::from_millis(50); fn make_ids(keys: &[AuthorityKeyring]) -> Vec<(substrate_primitives::ed25519::Public, u64)> { keys.iter() @@ -452,6 +352,7 @@ fn make_ids(keys: &[AuthorityKeyring]) -> Vec<(substrate_primitives::ed25519::Pu // run the voters to completion. provide a closure to be invoked after // the voters are spawned but before blocking on them. fn run_to_completion_with( + runtime: &mut current_thread::Runtime, blocks: u64, net: Arc>, peers: &[AuthorityKeyring], @@ -462,7 +363,6 @@ fn run_to_completion_with( use parking_lot::RwLock; let mut wait_for = Vec::new(); - let mut runtime = current_thread::Runtime::new().unwrap(); let highest_finalized = Arc::new(RwLock::new(0)); @@ -472,12 +372,13 @@ fn run_to_completion_with( for (peer_id, key) in peers.iter().enumerate() { let highest_finalized = highest_finalized.clone(); - let (client, link) = { + let (client, net_service, link) = { let net = net.lock(); // temporary needed for some reason let link = net.peers[peer_id].data.lock().take().expect("link initialized at startup; qed"); ( net.peers[peer_id].client().clone(), + net.peers[peer_id].network_service().clone(), link, ) }; @@ -485,6 +386,7 @@ fn run_to_completion_with( wait_for.push( Box::new( client.finality_notification_stream() + .map(|v| Ok::<_, ()>(v)).compat() .take_while(move |n| { let mut highest_finalized = highest_finalized.write(); if *n.header.number() > *highest_finalized { @@ -507,7 +409,7 @@ fn run_to_completion_with( name: Some(format!("peer#{}", peer_id)), }, link: link, - network: MessageRouting::new(net.clone(), peer_id), + network: net_service, inherent_data_providers: InherentDataProviders::new(), on_exit: Exit, telemetry_on_connect: None, @@ -524,35 +426,32 @@ fn run_to_completion_with( .map(|_| ()) .map_err(|_| ()); - let drive_to_completion = ::tokio::timer::Interval::new_interval(TEST_ROUTING_INTERVAL) - .for_each(move |_| { - net.lock().send_import_notifications(); - net.lock().send_finality_notifications(); - net.lock().sync_without_disconnects(); - Ok(()) - }) - .map(|_| ()) - .map_err(|_| ()); - + let drive_to_completion = futures::future::poll_fn(|| { net.lock().poll(); Ok(Async::NotReady) }); let _ = runtime.block_on(wait_for.select(drive_to_completion).map_err(|_| ())).unwrap(); let highest_finalized = *highest_finalized.read(); highest_finalized } -fn run_to_completion(blocks: u64, net: Arc>, peers: &[AuthorityKeyring]) -> u64 { - run_to_completion_with(blocks, net, peers, |_| None) +fn run_to_completion( + runtime: &mut current_thread::Runtime, + blocks: u64, + net: Arc>, + peers: &[AuthorityKeyring] +) -> u64 { + run_to_completion_with(runtime, blocks, net, peers, |_| None) } #[test] fn finalize_3_voters_no_observers() { let _ = env_logger::try_init(); + let mut runtime = current_thread::Runtime::new().unwrap(); let peers = &[AuthorityKeyring::Alice, AuthorityKeyring::Bob, AuthorityKeyring::Charlie]; let voters = make_ids(peers); let mut net = GrandpaTestNet::new(TestApi::new(voters), 3); net.peer(0).push_blocks(20, false); - net.sync(); + net.block_until_sync(&mut runtime); for i in 0..3 { assert_eq!(net.peer(i).client().info().chain.best_number, 20, @@ -560,7 +459,7 @@ fn finalize_3_voters_no_observers() { } let net = Arc::new(Mutex::new(net)); - run_to_completion(20, net.clone(), peers); + run_to_completion(&mut runtime, 20, net.clone(), peers); // normally there's no justification for finalized blocks assert!(net.lock().peer(0).client().justification(&BlockId::Number(20)).unwrap().is_none(), @@ -569,33 +468,36 @@ fn finalize_3_voters_no_observers() { #[test] fn finalize_3_voters_1_full_observer() { + let mut runtime = current_thread::Runtime::new().unwrap(); + let peers = &[AuthorityKeyring::Alice, AuthorityKeyring::Bob, AuthorityKeyring::Charlie]; let voters = make_ids(peers); let mut net = GrandpaTestNet::new(TestApi::new(voters), 4); net.peer(0).push_blocks(20, false); - net.sync(); + net.block_until_sync(&mut runtime); let net = Arc::new(Mutex::new(net)); let mut finality_notifications = Vec::new(); - let mut runtime = current_thread::Runtime::new().unwrap(); let all_peers = peers.iter() .cloned() .map(|key| Some(Arc::new(key.into()))) .chain(::std::iter::once(None)); for (peer_id, local_key) in all_peers.enumerate() { - let (client, link) = { + let (client, net_service, link) = { let net = net.lock(); let link = net.peers[peer_id].data.lock().take().expect("link initialized at startup; qed"); ( net.peers[peer_id].client().clone(), + net.peers[peer_id].network_service().clone(), link, ) }; finality_notifications.push( client.finality_notification_stream() + .map(|v| Ok::<_, ()>(v)).compat() .take_while(|n| Ok(n.header.number() < &20)) .for_each(move |_| Ok(())) ); @@ -608,7 +510,7 @@ fn finalize_3_voters_1_full_observer() { name: Some(format!("peer#{}", peer_id)), }, link: link, - network: MessageRouting::new(net.clone(), peer_id), + network: net_service, inherent_data_providers: InherentDataProviders::new(), on_exit: Exit, telemetry_on_connect: None, @@ -623,11 +525,7 @@ fn finalize_3_voters_1_full_observer() { .map(|_| ()) .map_err(|_| ()); - let drive_to_completion = ::tokio::timer::Interval::new_interval(TEST_ROUTING_INTERVAL) - .for_each(move |_| { net.lock().sync_without_disconnects(); Ok(()) }) - .map(|_| ()) - .map_err(|_| ()); - + let drive_to_completion = futures::future::poll_fn(|| { net.lock().poll(); Ok(Async::NotReady) }); let _ = runtime.block_on(wait_for.select(drive_to_completion).map_err(|_| ())).unwrap(); } @@ -663,7 +561,7 @@ fn transition_3_voters_twice_1_full_observer() { let mut runtime = current_thread::Runtime::new().unwrap(); net.lock().peer(0).push_blocks(1, false); - net.lock().sync(); + net.lock().block_until_sync(&mut runtime); for (i, peer) in net.lock().peers().iter().enumerate() { let full_client = peer.client().as_full().expect("only full clients are used in test"); @@ -690,6 +588,7 @@ fn transition_3_voters_twice_1_full_observer() { // wait for blocks to be finalized before generating new ones let block_production = client.finality_notification_stream() + .map(|v| Ok::<_, ()>(v)).compat() .take_while(|n| Ok(n.header.number() < &30)) .for_each(move |n| { match n.header.number() { @@ -745,17 +644,19 @@ fn transition_3_voters_twice_1_full_observer() { .enumerate(); for (peer_id, local_key) in all_peers { - let (client, link) = { + let (client, net_service, link) = { let net = net.lock(); let link = net.peers[peer_id].data.lock().take().expect("link initialized at startup; qed"); ( net.peers[peer_id].client().clone(), + net.peers[peer_id].network_service().clone(), link, ) }; finality_notifications.push( client.finality_notification_stream() + .map(|v| Ok::<_, ()>(v)).compat() .take_while(|n| Ok(n.header.number() < &30)) .for_each(move |_| Ok(())) .map(move |()| { @@ -777,7 +678,7 @@ fn transition_3_voters_twice_1_full_observer() { name: Some(format!("peer#{}", peer_id)), }, link: link, - network: MessageRouting::new(net.clone(), peer_id), + network: net_service, inherent_data_providers: InherentDataProviders::new(), on_exit: Exit, telemetry_on_connect: None, @@ -792,30 +693,22 @@ fn transition_3_voters_twice_1_full_observer() { .map(|_| ()) .map_err(|_| ()); - let drive_to_completion = ::tokio::timer::Interval::new_interval(TEST_ROUTING_INTERVAL) - .for_each(move |_| { - net.lock().send_import_notifications(); - net.lock().send_finality_notifications(); - net.lock().sync_without_disconnects(); - Ok(()) - }) - .map(|_| ()) - .map_err(|_| ()); - + let drive_to_completion = futures::future::poll_fn(|| { net.lock().poll(); Ok(Async::NotReady) }); let _ = runtime.block_on(wait_for.select(drive_to_completion).map_err(|_| ())).unwrap(); } #[test] fn justification_is_emitted_when_consensus_data_changes() { + let mut runtime = current_thread::Runtime::new().unwrap(); let peers = &[AuthorityKeyring::Alice, AuthorityKeyring::Bob, AuthorityKeyring::Charlie]; let mut net = GrandpaTestNet::new(TestApi::new(make_ids(peers)), 3); // import block#1 WITH consensus data change let new_authorities = vec![substrate_primitives::sr25519::Public::from_raw([42; 32])]; net.peer(0).push_authorities_change_block(new_authorities); - net.sync(); + net.block_until_sync(&mut runtime); let net = Arc::new(Mutex::new(net)); - run_to_completion(1, net.clone(), peers); + run_to_completion(&mut runtime, 1, net.clone(), peers); // ... and check that there's justification for block#1 assert!(net.lock().peer(0).client().justification(&BlockId::Number(1)).unwrap().is_some(), @@ -824,15 +717,16 @@ fn justification_is_emitted_when_consensus_data_changes() { #[test] fn justification_is_generated_periodically() { + let mut runtime = current_thread::Runtime::new().unwrap(); let peers = &[AuthorityKeyring::Alice, AuthorityKeyring::Bob, AuthorityKeyring::Charlie]; let voters = make_ids(peers); let mut net = GrandpaTestNet::new(TestApi::new(voters), 3); net.peer(0).push_blocks(32, false); - net.sync(); + net.block_until_sync(&mut runtime); let net = Arc::new(Mutex::new(net)); - run_to_completion(32, net.clone(), peers); + run_to_completion(&mut runtime, 32, net.clone(), peers); // when block#32 (justification_period) is finalized, justification // is required => generated @@ -862,6 +756,7 @@ fn consensus_changes_works() { #[test] fn sync_justifications_on_change_blocks() { + let mut runtime = current_thread::Runtime::new().unwrap(); let peers_a = &[AuthorityKeyring::Alice, AuthorityKeyring::Bob, AuthorityKeyring::Charlie]; let peers_b = &[AuthorityKeyring::Alice, AuthorityKeyring::Bob]; let voters = make_ids(peers_b); @@ -886,7 +781,7 @@ fn sync_justifications_on_change_blocks() { // add more blocks on top of it (until we have 25) net.peer(0).push_blocks(4, false); - net.sync(); + net.block_until_sync(&mut runtime); for i in 0..4 { assert_eq!(net.peer(i).client().info().chain.best_number, 25, @@ -894,7 +789,7 @@ fn sync_justifications_on_change_blocks() { } let net = Arc::new(Mutex::new(net)); - run_to_completion(25, net.clone(), peers_a); + run_to_completion(&mut runtime, 25, net.clone(), peers_a); // the first 3 peers are grandpa voters and therefore have already finalized // block 21 and stored a justification @@ -903,14 +798,20 @@ fn sync_justifications_on_change_blocks() { } // the last peer should get the justification by syncing from other peers - while net.lock().peer(3).client().justification(&BlockId::Number(21)).unwrap().is_none() { - net.lock().sync_without_disconnects(); - } + runtime.block_on(futures::future::poll_fn(move || -> std::result::Result<_, ()> { + if net.lock().peer(3).client().justification(&BlockId::Number(21)).unwrap().is_none() { + net.lock().poll(); + Ok(Async::NotReady) + } else { + Ok(Async::Ready(())) + } + })).unwrap() } #[test] fn finalizes_multiple_pending_changes_in_order() { let _ = env_logger::try_init(); + let mut runtime = current_thread::Runtime::new().unwrap(); let peers_a = &[AuthorityKeyring::Alice, AuthorityKeyring::Bob, AuthorityKeyring::Charlie]; let peers_b = &[AuthorityKeyring::Dave, AuthorityKeyring::Eve, AuthorityKeyring::Ferdie]; @@ -956,7 +857,7 @@ fn finalizes_multiple_pending_changes_in_order() { // add more blocks on top of it (until we have 30) net.peer(0).push_blocks(4, false); - net.sync(); + net.block_until_sync(&mut runtime); // all peers imported both change blocks for i in 0..6 { @@ -965,11 +866,12 @@ fn finalizes_multiple_pending_changes_in_order() { } let net = Arc::new(Mutex::new(net)); - run_to_completion(30, net.clone(), all_peers); + 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 = &[AuthorityKeyring::Alice, AuthorityKeyring::Bob, AuthorityKeyring::Charlie]; let voters = make_ids(peers_a); let api = TestApi::new(voters); @@ -977,7 +879,7 @@ fn doesnt_vote_on_the_tip_of_the_chain() { // add 100 blocks net.peer(0).push_blocks(100, false); - net.sync(); + net.block_until_sync(&mut runtime); for i in 0..3 { assert_eq!(net.peer(i).client().info().chain.best_number, 100, @@ -985,7 +887,7 @@ fn doesnt_vote_on_the_tip_of_the_chain() { } let net = Arc::new(Mutex::new(net)); - let highest = run_to_completion(75, net.clone(), peers_a); + 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); @@ -993,6 +895,8 @@ fn doesnt_vote_on_the_tip_of_the_chain() { #[test] fn force_change_to_new_set() { + let _ = env_logger::try_init(); + let mut runtime = current_thread::Runtime::new().unwrap(); // two of these guys are offline. let genesis_authorities = &[AuthorityKeyring::Alice, AuthorityKeyring::Bob, AuthorityKeyring::Charlie, AuthorityKeyring::One, AuthorityKeyring::Two]; let peers_a = &[AuthorityKeyring::Alice, AuthorityKeyring::Bob, AuthorityKeyring::Charlie]; @@ -1004,49 +908,44 @@ fn force_change_to_new_set() { let net = GrandpaTestNet::new(api, 3); let net = Arc::new(Mutex::new(net)); - let runner_net = net.clone(); - let add_blocks = move |_| { - net.lock().peer(0).push_blocks(1, false); - - { - // add a forced transition at block 12. - let parent_hash = net.lock().peer(0).client().info().chain.best_hash; - forced_transitions.lock().insert(parent_hash, (0, ScheduledChange { - next_authorities: voters.clone(), - delay: 10, - })); - - // add a normal transition too to ensure that forced changes take priority. - normal_transitions.lock().insert(parent_hash, ScheduledChange { - next_authorities: make_ids(genesis_authorities), - delay: 5, - }); - } + net.lock().peer(0).push_blocks(1, false); - net.lock().peer(0).push_blocks(25, false); - net.lock().sync(); + { + // add a forced transition at block 12. + let parent_hash = net.lock().peer(0).client().info().chain.best_hash; + forced_transitions.lock().insert(parent_hash, (0, ScheduledChange { + next_authorities: voters.clone(), + delay: 10, + })); + + // add a normal transition too to ensure that forced changes take priority. + normal_transitions.lock().insert(parent_hash, ScheduledChange { + next_authorities: make_ids(genesis_authorities), + delay: 5, + }); + } - for (i, peer) in net.lock().peers().iter().enumerate() { - assert_eq!(peer.client().info().chain.best_number, 26, - "Peer #{} failed to sync", i); + net.lock().peer(0).push_blocks(25, false); + net.lock().block_until_sync(&mut runtime); - let full_client = peer.client().as_full().expect("only full clients are used in test"); - let set: AuthoritySet = crate::aux_schema::load_authorities( - #[allow(deprecated)] - &**full_client.backend() - ).unwrap(); + for (i, peer) in net.lock().peers().iter().enumerate() { + assert_eq!(peer.client().info().chain.best_number, 26, + "Peer #{} failed to sync", i); - assert_eq!(set.current(), (1, voters.as_slice())); - assert_eq!(set.pending_changes().count(), 0); - } + let full_client = peer.client().as_full().expect("only full clients are used in test"); + let set: AuthoritySet = crate::aux_schema::load_authorities( + #[allow(deprecated)] + &**full_client.backend() + ).unwrap(); - None - }; + assert_eq!(set.current(), (1, voters.as_slice())); + assert_eq!(set.pending_changes().count(), 0); + } // it will only finalize if the forced transition happens. // we add_blocks after the voters are spawned because otherwise // the link-halfs have the wrong AuthoritySet - run_to_completion_with(25, runner_net, peers_a, add_blocks); + run_to_completion(&mut runtime, 25, net, peers_a); } #[test] @@ -1055,10 +954,10 @@ fn allows_reimporting_change_blocks() { let peers_b = &[AuthorityKeyring::Alice, AuthorityKeyring::Bob]; let voters = make_ids(peers_a); let api = TestApi::new(voters); - let net = GrandpaTestNet::new(api.clone(), 3); + let mut net = GrandpaTestNet::new(api.clone(), 3); let client = net.peer(0).client().clone(); - let (block_import, ..) = net.make_block_import(client.clone()); + let (mut block_import, ..) = net.make_block_import(client.clone()); let full_client = client.as_full().unwrap(); let builder = full_client.new_block_at(&BlockId::Number(0), Default::default()).unwrap(); @@ -1070,7 +969,7 @@ fn allows_reimporting_change_blocks() { let block = || { let block = block.clone(); - ImportBlock { + BlockImportParams { origin: BlockOrigin::File, header: block.header, justification: None, @@ -1104,10 +1003,10 @@ fn test_bad_justification() { let peers_b = &[AuthorityKeyring::Alice, AuthorityKeyring::Bob]; let voters = make_ids(peers_a); let api = TestApi::new(voters); - let net = GrandpaTestNet::new(api.clone(), 3); + let mut net = GrandpaTestNet::new(api.clone(), 3); let client = net.peer(0).client().clone(); - let (block_import, ..) = net.make_block_import(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(); @@ -1119,7 +1018,7 @@ fn test_bad_justification() { let block = || { let block = block.clone(); - ImportBlock { + BlockImportParams { origin: BlockOrigin::File, header: block.header, justification: Some(Vec::new()), @@ -1155,6 +1054,7 @@ fn voter_persists_its_votes() { use futures::sync::mpsc; let _ = env_logger::try_init(); + let mut runtime = current_thread::Runtime::new().unwrap(); // we have two authorities but we'll only be running the voter for alice // we are going to be listening for the prevotes it casts @@ -1164,13 +1064,11 @@ fn voter_persists_its_votes() { // alice has a chain with 20 blocks let mut net = GrandpaTestNet::new(TestApi::new(voters.clone()), 2); net.peer(0).push_blocks(20, false); - net.sync(); + net.block_until_sync(&mut runtime); assert_eq!(net.peer(0).client().info().chain.best_number, 20, "Peer #{} failed to sync", 0); - let mut runtime = current_thread::Runtime::new().unwrap(); - let client = net.peer(0).client().clone(); let net = Arc::new(Mutex::new(net)); @@ -1181,6 +1079,7 @@ fn voter_persists_its_votes() { // sender is dropped the voter is stopped. { let net = net.clone(); + let client = client.clone(); let voter = future::loop_fn(voter_rx, move |rx| { let (_block_import, _, _, _, link) = net.lock().make_block_import(client.clone()); @@ -1194,20 +1093,21 @@ fn voter_persists_its_votes() { name: Some(format!("peer#{}", 0)), }, link: link, - network: MessageRouting::new(net.clone(), 0), + network: net.lock().peers[0].network_service().clone(), inherent_data_providers: InherentDataProviders::new(), on_exit: Exit, telemetry_on_connect: None, }; - let mut voter = run_grandpa_voter(grandpa_params).expect("all in order with client and network"); - - let voter = future::poll_fn(move || { - // we need to keep the block_import alive since it owns the - // sender for the voter commands channel, if that gets dropped - // then the voter will stop - let _block_import = _block_import.clone(); - voter.poll() - }); + + let voter = run_grandpa_voter(grandpa_params) + .expect("all in order with client and network") + .then(move |r| { + // we need to keep the block_import alive since it owns the + // sender for the voter commands channel, if that gets dropped + // then the voter will stop + drop(_block_import); + r + }); voter.select2(rx.into_future()).then(|res| match res { Ok(future::Either::A(x)) => { @@ -1244,11 +1144,18 @@ fn voter_persists_its_votes() { local_key: Some(Arc::new(peers[1].clone().into())), name: Some(format!("peer#{}", 1)), }; - let routing = MessageRouting::new(net.clone(), 1); + + let set_state = { + let (_, _, _, _, link) = net.lock().make_block_import(client); + let LinkHalf { persistent_data, .. } = link.lock().take().unwrap(); + let PersistentData { set_state, .. } = persistent_data; + set_state + }; + let (network, routing_work) = communication::NetworkBridge::new( - routing, + net.lock().peers[1].network_service().clone(), config.clone(), - None, + set_state, Exit, ); runtime.block_on(routing_work).unwrap(); @@ -1281,25 +1188,33 @@ fn voter_persists_its_votes() { // we push 20 more blocks to alice's chain net.lock().peer(0).push_blocks(20, false); - net.lock().sync(); - - assert_eq!(net.lock().peer(0).client().info().chain.best_number, 40, - "Peer #{} failed to sync", 0); - #[allow(deprecated)] - let block_30_hash = - net.lock().peer(0).client().as_full().unwrap().backend().blockchain().hash(30).unwrap().unwrap(); + let net2 = net.clone(); + let net = net.clone(); + let voter_tx = voter_tx.clone(); + let round_tx = round_tx.clone(); + future::Either::A(tokio_timer::Interval::new_interval(Duration::from_millis(200)) + .take_while(move |_| { + Ok(net2.lock().peer(1).client().info().chain.best_number != 40) + }) + .for_each(|_| Ok(())) + .and_then(move |_| { + #[allow(deprecated)] + let block_30_hash = + net.lock().peer(0).client().as_full().unwrap().backend().blockchain().hash(30).unwrap().unwrap(); - // we restart alice's voter - voter_tx.unbounded_send(()).unwrap(); + // we restart alice's voter + voter_tx.unbounded_send(()).unwrap(); - // and we push our own prevote for block 30 - let prevote = grandpa::Prevote { - target_number: 30, - target_hash: block_30_hash, - }; + // and we push our own prevote for block 30 + let prevote = grandpa::Prevote { + target_number: 30, + target_hash: block_30_hash, + }; - round_tx.lock().start_send(grandpa::Message::Prevote(prevote)).unwrap(); + round_tx.lock().start_send(grandpa::Message::Prevote(prevote)).unwrap(); + Ok(()) + }).map_err(|_| panic!())) } else if state.compare_and_swap(1, 2, Ordering::SeqCst) == 1 { // the next message we receive should be our own prevote @@ -1315,6 +1230,8 @@ fn voter_persists_its_votes() { // therefore we won't ever receive it again since it will be a // known message on the gossip layer + future::Either::B(future::ok(())) + } else if state.compare_and_swap(2, 3, Ordering::SeqCst) == 2 { // we then receive a precommit from alice for block 15 // even though we casted a prevote for block 30 @@ -1327,23 +1244,17 @@ fn voter_persists_its_votes() { // signal exit exit_tx.clone().lock().take().unwrap().send(()).unwrap(); + + future::Either::B(future::ok(())) + + } else { + panic!() } - Ok(()) }).map_err(|_| ())); } - let net = net.clone(); - let drive_to_completion = ::tokio::timer::Interval::new_interval(TEST_ROUTING_INTERVAL) - .for_each(move |_| { - net.lock().send_import_notifications(); - net.lock().send_finality_notifications(); - net.lock().sync_without_disconnects(); - Ok(()) - }) - .map(|_| ()) - .map_err(|_| ()); - + let drive_to_completion = futures::future::poll_fn(|| { net.lock().poll(); Ok(Async::NotReady) }); let exit = exit_rx.into_future().map(|_| ()).map_err(|_| ()); runtime.block_on(drive_to_completion.select(exit).map(|_| ()).map_err(|_| ())).unwrap(); @@ -1352,12 +1263,13 @@ fn voter_persists_its_votes() { #[test] fn finalize_3_voters_1_light_observer() { let _ = env_logger::try_init(); + let mut runtime = current_thread::Runtime::new().unwrap(); let authorities = &[AuthorityKeyring::Alice, AuthorityKeyring::Bob, AuthorityKeyring::Charlie]; let voters = make_ids(authorities); let mut net = GrandpaTestNet::new(TestApi::new(voters), 4); net.peer(0).push_blocks(20, false); - net.sync(); + net.block_until_sync(&mut runtime); for i in 0..4 { assert_eq!(net.peer(i).client().info().chain.best_number, 20, @@ -1368,10 +1280,11 @@ fn finalize_3_voters_1_light_observer() { let link = net.lock().peer(3).data.lock().take().expect("link initialized on startup; qed"); let finality_notifications = net.lock().peer(3).client().finality_notification_stream() + .map(|v| Ok::<_, ()>(v)).compat() .take_while(|n| Ok(n.header.number() < &20)) .collect(); - run_to_completion_with(20, net.clone(), authorities, |executor| { + run_to_completion_with(&mut runtime, 20, net.clone(), authorities, |executor| { executor.spawn( run_grandpa_observer( Config { @@ -1381,7 +1294,7 @@ fn finalize_3_voters_1_light_observer() { name: Some("observer".to_string()), }, link, - MessageRouting::new(net.clone(), 3), + net.lock().peers[3].network_service().clone(), Exit, ).unwrap() ).unwrap(); @@ -1393,6 +1306,7 @@ fn finalize_3_voters_1_light_observer() { #[test] fn finality_proof_is_fetched_by_light_client_when_consensus_data_changes() { let _ = ::env_logger::try_init(); + let mut runtime = current_thread::Runtime::new().unwrap(); let peers = &[AuthorityKeyring::Alice]; let mut net = GrandpaTestNet::new(TestApi::new(make_ids(peers)), 1); @@ -1402,14 +1316,18 @@ fn finality_proof_is_fetched_by_light_client_when_consensus_data_changes() { // && instead fetches finality proof for block #1 net.peer(0).push_authorities_change_block(vec![substrate_primitives::sr25519::Public::from_raw([42; 32])]); let net = Arc::new(Mutex::new(net)); - run_to_completion(1, net.clone(), peers); - net.lock().sync_without_disconnects(); + run_to_completion(&mut runtime, 1, net.clone(), peers); + net.lock().block_until_sync(&mut runtime); // check that the block#1 is finalized on light client - while net.lock().peer(1).client().info().chain.finalized_number != 1 { - net.lock().tick_peer(1); - net.lock().sync_without_disconnects(); - } + runtime.block_on(futures::future::poll_fn(move || -> std::result::Result<_, ()> { + if net.lock().peer(1).client().info().chain.finalized_number == 1 { + Ok(Async::Ready(())) + } else { + net.lock().poll(); + Ok(Async::NotReady) + } + })).unwrap() } #[test] @@ -1418,6 +1336,7 @@ fn empty_finality_proof_is_returned_to_light_client_when_authority_set_is_differ const FORCE_CHANGE: bool = true; let _ = ::env_logger::try_init(); + let mut runtime = current_thread::Runtime::new().unwrap(); // two of these guys are offline. let genesis_authorities = if FORCE_CHANGE { @@ -1443,40 +1362,141 @@ fn empty_finality_proof_is_returned_to_light_client_when_authority_set_is_differ let net = GrandpaTestNet::new(api, 3); let net = Arc::new(Mutex::new(net)); - let runner_net = net.clone(); - let add_blocks = move |_| { - net.lock().peer(0).push_blocks(1, false); // best is #1 - - // add a forced transition at block 5. - if FORCE_CHANGE { - let parent_hash = net.lock().peer(0).client().info().chain.best_hash; - forced_transitions.lock().insert(parent_hash, (0, ScheduledChange { - next_authorities: voters.clone(), - delay: 3, - })); - } + net.lock().peer(0).push_blocks(1, false); // best is #1 - // ensure block#10 enacts authorities set change => justification is generated - // normally it will reach light client, but because of the forced change, it will not - net.lock().peer(0).push_blocks(8, false); // best is #9 - net.lock().peer(0).push_authorities_change_block( - vec![substrate_primitives::sr25519::Public::from_raw([42; 32])] - ); // #10 - net.lock().peer(0).push_blocks(1, false); // best is #11 - net.lock().sync_without_disconnects(); + // add a forced transition at block 5. + if FORCE_CHANGE { + let parent_hash = net.lock().peer(0).client().info().chain.best_hash; + forced_transitions.lock().insert(parent_hash, (0, ScheduledChange { + next_authorities: voters.clone(), + delay: 3, + })); + } - None - }; + // ensure block#10 enacts authorities set change => justification is generated + // normally it will reach light client, but because of the forced change, it will not + net.lock().peer(0).push_blocks(8, false); // best is #9 + net.lock().peer(0).push_authorities_change_block( + vec![substrate_primitives::sr25519::Public::from_raw([42; 32])] + ); // #10 + net.lock().peer(0).push_blocks(1, false); // best is #11 + net.lock().block_until_sync(&mut runtime); // finalize block #11 on full clients - run_to_completion_with(11, runner_net.clone(), peers_a, add_blocks); + run_to_completion(&mut runtime, 11, net.clone(), peers_a); + // request finalization by light client - runner_net.lock().add_light_peer(&GrandpaTestNet::default_config()); - runner_net.lock().sync_without_disconnects(); + net.lock().add_light_peer(&GrandpaTestNet::default_config()); + net.lock().block_until_sync(&mut runtime); // check block, finalized on light client assert_eq!( - runner_net.lock().peer(3).client().info().chain.finalized_number, + net.lock().peer(3).client().info().chain.finalized_number, if FORCE_CHANGE { 0 } else { 10 }, ); } + +#[test] +fn voter_catches_up_to_latest_round_when_behind() { + let _ = env_logger::try_init(); + let mut runtime = current_thread::Runtime::new().unwrap(); + + let peers = &[AuthorityKeyring::Alice, AuthorityKeyring::Bob]; + let voters = make_ids(peers); + + let mut net = GrandpaTestNet::new(TestApi::new(voters), 3); + net.peer(0).push_blocks(50, false); + net.block_until_sync(&mut runtime); + + let net = Arc::new(Mutex::new(net)); + let mut finality_notifications = Vec::new(); + + let voter = |local_key, peer_id, link, net: Arc>| -> Box + Send> { + let grandpa_params = GrandpaParams { + config: Config { + gossip_duration: TEST_GOSSIP_DURATION, + justification_period: 32, + local_key, + name: Some(format!("peer#{}", peer_id)), + }, + link: link, + network: net.lock().peer(peer_id).network_service().clone(), + inherent_data_providers: InherentDataProviders::new(), + on_exit: Exit, + telemetry_on_connect: None, + }; + + Box::new(run_grandpa_voter(grandpa_params).expect("all in order with client and network")) + }; + + // spawn authorities + for (peer_id, key) in peers.iter().enumerate() { + let (client, link) = { + let net = net.lock(); + let link = net.peers[peer_id].data.lock().take().expect("link initialized at startup; qed"); + ( + net.peers[peer_id].client().clone(), + link, + ) + }; + + finality_notifications.push( + client.finality_notification_stream() + .map(|v| Ok::<_, ()>(v)).compat() + .take_while(|n| Ok(n.header.number() < &50)) + .for_each(move |_| Ok(())) + ); + + let voter = voter(Some(Arc::new((*key).into())), peer_id, link, net.clone()); + + runtime.spawn(voter); + } + + // wait for them to finalize block 50. since they'll vote on 3/4 of the + // unfinalized chain it will take at least 4 rounds to do it. + let wait_for_finality = ::futures::future::join_all(finality_notifications) + .map(|_| ()) + .map_err(|_| ()); + + // spawn a new voter, it should be behind by at least 4 rounds and should be + // able to catch up to the latest round + let test = { + let net = net.clone(); + let runtime = runtime.handle(); + + wait_for_finality.and_then(move |_| { + let peer_id = 2; + let (client, link) = { + let net = net.lock(); + let link = net.peers[peer_id].data.lock().take().expect("link initialized at startup; qed"); + ( + net.peers[peer_id].client().clone(), + link, + ) + }; + + let set_state = link.persistent_data.set_state.clone(); + + let wait = client.finality_notification_stream() + .map(|v| Ok::<_, ()>(v)).compat() + .take_while(|n| Ok(n.header.number() < &50)) + .collect() + .map(|_| set_state); + + let voter = voter(None, peer_id, link, net); + + runtime.spawn(voter).unwrap(); + + wait + }) + .and_then(|set_state| { + // the last completed round in the new voter is higher than 4 + // which means it caught up to the voters + assert!(set_state.read().last_completed_round().number >= 4); + Ok(()) + }) + }; + + 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(); +} diff --git a/core/finality-grandpa/src/until_imported.rs b/core/finality-grandpa/src/until_imported.rs index 7c981050dd4aea6a850f05636077fad2bd3c5361..5575a0d2c6009ecdeb8478f7fa3128e7eb2fda7c 100644 --- a/core/finality-grandpa/src/until_imported.rs +++ b/core/finality-grandpa/src/until_imported.rs @@ -20,15 +20,17 @@ //! //! This is used for votes and commit messages currently. -use super::{BlockStatus, Error, SignedMessage, CompactCommit}; +use super::{BlockStatus, CommunicationIn, Error, SignedMessage}; use log::{debug, warn}; -use client::ImportNotifications; +use client::{BlockImportNotification, ImportNotifications}; use futures::prelude::*; use futures::stream::Fuse; +use futures03::{StreamExt as _, TryStreamExt as _}; +use grandpa::voter; use parking_lot::Mutex; use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, NumberFor}; -use tokio::timer::Interval; +use tokio_timer::Interval; use std::collections::{HashMap, VecDeque}; use std::sync::{atomic::{AtomicUsize, Ordering}, Arc}; @@ -63,7 +65,7 @@ pub(crate) trait BlockUntilImported: Sized { /// Buffering imported messages until blocks with given hashes are imported. pub(crate) struct UntilImported> { - import_notifications: Fuse>, + import_notifications: Fuse, Error = ()> + Send>>, status_check: Status, inner: Fuse, ready: VecDeque, @@ -90,7 +92,10 @@ impl UntilImported let check_pending = Interval::new(now + CHECK_PENDING_INTERVAL, CHECK_PENDING_INTERVAL); UntilImported { - import_notifications: import_notifications.fuse(), + import_notifications: { + let stream = import_notifications.map::<_, fn(_) -> _>(|v| Ok::<_, ()>(v)).compat(); + Box::new(stream) as Box + Send> + }.fuse(), status_check, inner: stream.fuse(), ready: VecDeque::new(), @@ -193,7 +198,6 @@ impl Stream for UntilImported if self.import_notifications.is_done() && self.inner.is_done() { Ok(Async::Ready(None)) } else { - Ok(Async::NotReady) } } @@ -253,18 +257,18 @@ impl BlockUntilImported for SignedMessage { /// signed messages are imported. pub(crate) type UntilVoteTargetImported = UntilImported>; -/// This blocks a commit message's import until all blocks -/// referenced in its votes are known. +/// This blocks a global message import, i.e. a commit or catch up messages, +/// until all blocks referenced in its votes are known. /// -/// This is used for compact commits which have already been checked for -/// structural soundness. -pub(crate) struct BlockCommitMessage { - inner: Arc<(AtomicUsize, Mutex, U)>>)>, +/// This is used for compact commits and catch up messages which have already +/// been checked for structural soundness (e.g. valid signatures). +pub(crate) struct BlockGlobalMessage { + inner: Arc<(AtomicUsize, Mutex>>)>, target_number: NumberFor, } -impl BlockUntilImported for BlockCommitMessage { - type Blocked = (u64, CompactCommit, U); +impl BlockUntilImported for BlockGlobalMessage { + type Blocked = CommunicationIn; fn schedule_wait( input: Self::Blocked, @@ -298,7 +302,7 @@ impl BlockUntilImported for BlockCommitMessage Result { - // check integrity: all precommits for same hash have same number. + // check integrity: all votes for same hash have same number. let canon_number = match checked_hashes.entry(target_hash) { Entry::Occupied(entry) => entry.get().number().clone(), Entry::Vacant(entry) => { @@ -315,51 +319,68 @@ impl BlockUntilImported for BlockCommitMessage { + // add known hashes from all precommits. + let precommit_targets = commit.precommits + .iter() + .map(|c| (c.target_number, c.target_hash)); - // add known hashes from the precommits. - for precommit in &commit.precommits { - let target_number = precommit.target_number; - let target_hash = precommit.target_hash; + for (target_number, target_hash) in precommit_targets { + if !query_known(target_hash, target_number)? { + return Ok(()) + } + } + }, + voter::CommunicationIn::CatchUp(ref catch_up, ..) => { + // add known hashes from all prevotes and precommits. + let prevote_targets = catch_up.prevotes + .iter() + .map(|s| (s.prevote.target_number, s.prevote.target_hash)); - if !query_known(target_hash, target_number)? { - return Ok(()) - } - } + let precommit_targets = catch_up.precommits + .iter() + .map(|s| (s.precommit.target_number, s.precommit.target_hash)); - // see if commit target hash is known. - if !query_known(commit.target_hash, commit.target_number)? { - return Ok(()) - } + let targets = prevote_targets.chain(precommit_targets); + + for (target_number, target_hash) in targets { + if !query_known(target_hash, target_number)? { + return Ok(()) + } + } + }, + }; } - // none of the hashes in the commit message were unknown. - // we can just return the commit directly. + // none of the hashes in the global message were unknown. + // we can just return the message directly. if unknown_count == 0 { ready(input); return Ok(()) } - let locked_commit = Arc::new((AtomicUsize::new(unknown_count), Mutex::new(Some(input)))); + let locked_global = Arc::new((AtomicUsize::new(unknown_count), Mutex::new(Some(input)))); // schedule waits for all unknown messages. // when the last one of these has `wait_completed` called on it, - // the commit will be returned. + // the global message will be returned. // // in the future, we may want to issue sync requests to the network // if this is taking a long time. for (hash, is_known) in checked_hashes { if let KnownOrUnknown::Unknown(target_number) = is_known { - wait(hash, BlockCommitMessage { - inner: locked_commit.clone(), + wait(hash, BlockGlobalMessage { + inner: locked_global.clone(), target_number, }) } @@ -398,25 +419,26 @@ impl BlockUntilImported for BlockCommitMessage = UntilImported< +/// 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< Block, Status, I, - BlockCommitMessage, + BlockGlobalMessage, >; #[cfg(test)] mod tests { use super::*; + use crate::{CatchUp, CompactCommit}; use tokio::runtime::current_thread::Runtime; - use tokio::timer::Delay; + use tokio_timer::Delay; use test_client::runtime::{Block, Hash, Header}; use consensus_common::BlockOrigin; use client::BlockImportNotification; use futures::future::Either; - use futures::sync::mpsc; + use futures03::channel::mpsc; use grandpa::Precommit; #[derive(Clone)] @@ -474,41 +496,73 @@ mod tests { ) } - #[test] - fn blocking_commit_message() { - let h1 = make_header(5); - let h2 = make_header(6); - let h3 = make_header(7); + // unwrap the commit from `CommunicationIn` returning its fields in a tuple, + // panics if the given message isn't a commit + fn unapply_commit(msg: CommunicationIn) -> (u64, CompactCommit::) { + match msg { + voter::CommunicationIn::Commit(round, commit, ..) => (round, commit), + _ => panic!("expected commit"), + } + } + + // unwrap the catch up from `CommunicationIn` returning its inner representation, + // panics if the given message isn't a catch up + fn unapply_catch_up(msg: CommunicationIn) -> CatchUp { + match msg { + voter::CommunicationIn::CatchUp(catch_up, ..) => catch_up, + _ => panic!("expected catch up"), + } + } + fn message_all_dependencies_satisfied( + msg: CommunicationIn, + enact_dependencies: F, + ) -> CommunicationIn where + F: FnOnce(&TestChainState), + { let (chain_state, import_notifications) = TestChainState::new(); let block_status = chain_state.block_status(); - 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 - }; + // enact all dependencies before importing the message + enact_dependencies(&chain_state); - let (commit_tx, commit_rx) = mpsc::unbounded(); + let (global_tx, global_rx) = futures::sync::mpsc::unbounded(); - let until_imported = UntilCommitBlocksImported::new( + let until_imported = UntilGlobalMessageBlocksImported::new( import_notifications, block_status, - commit_rx.map_err(|_| panic!("should never error")), + global_rx.map_err(|_| panic!("should never error")), ); - commit_tx.unbounded_send((0, unknown_commit.clone(), ())).unwrap(); + global_tx.unbounded_send(msg).unwrap(); + let work = until_imported.into_future(); + + let mut runtime = Runtime::new().unwrap(); + runtime.block_on(work).map_err(|(e, _)| e).unwrap().0.unwrap() + } + + fn blocking_message_on_dependencies( + msg: CommunicationIn, + enact_dependencies: F, + ) -> CommunicationIn where + F: FnOnce(&TestChainState), + { + let (chain_state, import_notifications) = TestChainState::new(); + let block_status = chain_state.block_status(); + + let (global_tx, global_rx) = futures::sync::mpsc::unbounded(); + + let until_imported = UntilGlobalMessageBlocksImported::new( + import_notifications, + block_status, + global_rx.map_err(|_| panic!("should never error")), + ); + + global_tx.unbounded_send(msg).unwrap(); + + // NOTE: needs to be cloned otherwise it is moved to the stream and + // dropped too early. let inner_chain_state = chain_state.clone(); let work = until_imported .into_future() @@ -518,26 +572,64 @@ mod tests { Ok(Either::A(_)) => panic!("timeout should have fired first"), Ok(Either::B((_, until_imported))) => { // timeout fired. push in the headers. - inner_chain_state.import_header(h1); - inner_chain_state.import_header(h2); - inner_chain_state.import_header(h3); + enact_dependencies(&inner_chain_state); until_imported } }); let mut runtime = Runtime::new().unwrap(); - assert_eq!(runtime.block_on(work).map_err(|(e, _)| e).unwrap().0, Some((0, unknown_commit, ()))); + runtime.block_on(work).map_err(|(e, _)| e).unwrap().0.unwrap() } #[test] - fn commit_message_all_known() { + fn blocking_commit_message() { let h1 = make_header(5); let h2 = make_header(6); let h3 = make_header(7); - let (chain_state, import_notifications) = TestChainState::new(); - let block_status = chain_state.block_status(); + 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, + ); + + let res = blocking_message_on_dependencies( + unknown_commit(), + |chain_state| { + chain_state.import_header(h1); + chain_state.import_header(h2); + chain_state.import_header(h3); + }, + ); + + assert_eq!( + unapply_commit(res), + unapply_commit(unknown_commit()), + ); + } + + #[test] + fn commit_message_all_known() { + let h1 = make_header(5); + let h2 = make_header(6); + let h3 = make_header(7); let known_commit = CompactCommit:: { target_hash: h1.hash(), @@ -555,23 +647,156 @@ mod tests { auth_data: Vec::new(), // not used }; - chain_state.import_header(h1); - chain_state.import_header(h2); - chain_state.import_header(h3); + let known_commit = || voter::CommunicationIn::Commit( + 0, + known_commit.clone(), + voter::Callback::Blank, + ); - let (commit_tx, commit_rx) = mpsc::unbounded(); + let res = message_all_dependencies_satisfied( + known_commit(), + |chain_state| { + chain_state.import_header(h1); + chain_state.import_header(h2); + chain_state.import_header(h3); + }, + ); - let until_imported = UntilCommitBlocksImported::new( - import_notifications, - block_status, - commit_rx.map_err(|_| panic!("should never error")), + assert_eq!( + unapply_commit(res), + unapply_commit(known_commit()), ); + } - commit_tx.unbounded_send((0, known_commit.clone(), ())).unwrap(); + #[test] + fn blocking_catch_up_message() { + let h1 = make_header(5); + let h2 = make_header(6); + let h3 = make_header(7); - let work = until_imported.into_future(); + let signed_prevote = |header: &Header| { + grandpa::SignedPrevote { + id: Default::default(), + signature: Default::default(), + prevote: grandpa::Prevote { + target_hash: header.hash(), + target_number: *header.number(), + }, + } + }; - let mut runtime = Runtime::new().unwrap(); - assert_eq!(runtime.block_on(work).map_err(|(e, _)| e).unwrap().0, Some((0, known_commit, ()))); + let signed_precommit = |header: &Header| { + grandpa::SignedPrecommit { + id: Default::default(), + signature: Default::default(), + precommit: grandpa::Precommit { + target_hash: header.hash(), + target_number: *header.number(), + }, + } + }; + + let prevotes = vec![ + signed_prevote(&h1), + signed_prevote(&h3), + ]; + + let precommits = vec![ + signed_precommit(&h1), + signed_precommit(&h2), + ]; + + let unknown_catch_up = grandpa::CatchUp { + round_number: 1, + prevotes, + precommits, + base_hash: h1.hash(), + base_number: *h1.number(), + }; + + let unknown_catch_up = || voter::CommunicationIn::CatchUp( + unknown_catch_up.clone(), + voter::Callback::Blank, + ); + + let res = blocking_message_on_dependencies( + unknown_catch_up(), + |chain_state| { + chain_state.import_header(h1); + chain_state.import_header(h2); + chain_state.import_header(h3); + }, + ); + + assert_eq!( + unapply_catch_up(res), + unapply_catch_up(unknown_catch_up()), + ); + } + + #[test] + fn catch_up_message_all_known() { + let h1 = make_header(5); + let h2 = make_header(6); + let h3 = make_header(7); + + let signed_prevote = |header: &Header| { + grandpa::SignedPrevote { + id: Default::default(), + signature: Default::default(), + prevote: grandpa::Prevote { + target_hash: header.hash(), + target_number: *header.number(), + }, + } + }; + + let signed_precommit = |header: &Header| { + grandpa::SignedPrecommit { + id: Default::default(), + signature: Default::default(), + precommit: grandpa::Precommit { + target_hash: header.hash(), + target_number: *header.number(), + }, + } + }; + + let prevotes = vec![ + signed_prevote(&h1), + signed_prevote(&h3), + ]; + + let precommits = vec![ + signed_precommit(&h1), + signed_precommit(&h2), + ]; + + let unknown_catch_up = grandpa::CatchUp { + round_number: 1, + prevotes, + precommits, + base_hash: h1.hash(), + base_number: *h1.number(), + }; + + let unknown_catch_up = || voter::CommunicationIn::CatchUp( + unknown_catch_up.clone(), + voter::Callback::Blank, + ); + + let res = message_all_dependencies_satisfied( + unknown_catch_up(), + |chain_state| { + chain_state.import_header(h1); + chain_state.import_header(h2); + chain_state.import_header(h3); + }, + ); + + assert_eq!( + unapply_catch_up(res), + unapply_catch_up(unknown_catch_up()), + ); } } diff --git a/core/inherents/Cargo.toml b/core/inherents/Cargo.toml index 606d9c5ae97ba83ade7a633fd7a0f4bf1e3e08a7..1824eef80f06b7e953ec008e8a261375e56f917b 100644 --- a/core/inherents/Cargo.toml +++ b/core/inherents/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" [dependencies] parking_lot = { version = "0.8.0", optional = true } rstd = { package = "sr-std", path = "../sr-std", default-features = false } -parity-codec = { version = "3.3", default-features = false, features = ["derive"] } +parity-codec = { version = "4.1.1", default-features = false, features = ["derive"] } runtime_primitives = { package = "sr-primitives", path = "../sr-primitives", default-features = false } [features] diff --git a/core/inherents/src/lib.rs b/core/inherents/src/lib.rs index 7b99c7ba526b24d2ba292a64dec2cb37556ffe48..040f289957fc31e5cf98b6cd35cdf9a6e5963e67 100644 --- a/core/inherents/src/lib.rs +++ b/core/inherents/src/lib.rs @@ -16,8 +16,8 @@ //! Provides types and traits for creating and checking inherents. //! -//! Each inherent is added to a produced block. Each runtime decides on which inherents its -//! want to attach to its blocks. All data that is required for the runtime to create the inherents +//! Each inherent is added to a produced block. Each runtime decides on which inherents it +//! wants to attach to its blocks. All data that is required for the runtime to create the inherents //! is stored in the `InherentData`. This `InherentData` is constructed by the node and given to //! the runtime. //! @@ -124,8 +124,8 @@ impl InherentData { /// /// It either returns okay for all checks, stores all occurred errors or just one fatal error. /// -/// When a fatal error occurres, all other errors are removed and the implementation needs to -/// abbort checking inherents. +/// When a fatal error occurs, all other errors are removed and the implementation needs to +/// abort checking inherents. #[derive(Encode, Decode, Clone)] pub struct CheckInherentsResult { /// Did the check succeed? diff --git a/core/keyring/src/ed25519.rs b/core/keyring/src/ed25519.rs index 9c303b62bc58e6728603dcbe1b27be7c85f724d8..bec4c801561f7b35486aeb16e831801c88ded151 100644 --- a/core/keyring/src/ed25519.rs +++ b/core/keyring/src/ed25519.rs @@ -18,7 +18,7 @@ use std::{collections::HashMap, ops::Deref}; use lazy_static::lazy_static; -use substrate_primitives::{ed25519::{Pair, Public, Signature}, Pair as PairT, H256}; +use substrate_primitives::{ed25519::{Pair, Public, Signature}, Pair as PairT, Public as PublicT, H256}; pub use substrate_primitives::ed25519; /// Set of test accounts. diff --git a/core/keyring/src/sr25519.rs b/core/keyring/src/sr25519.rs index 24a83ab798d26442d788b9bc219b1a493bb59496..8db66ab5ddff7e54d3ac80ba229b763858bd7922 100644 --- a/core/keyring/src/sr25519.rs +++ b/core/keyring/src/sr25519.rs @@ -19,7 +19,7 @@ use std::collections::HashMap; use std::ops::Deref; use lazy_static::lazy_static; -use substrate_primitives::{sr25519::{Pair, Public, Signature}, Pair as PairT, H256}; +use substrate_primitives::{sr25519::{Pair, Public, Signature}, Pair as PairT, Public as PublicT, H256}; pub use substrate_primitives::sr25519; /// Set of test accounts. @@ -68,7 +68,7 @@ impl Keyring { } pub fn to_raw_public_vec(self) -> Vec { - Public::from(self).into_raw_vec() + Public::from(self).to_raw_vec() } pub fn sign(self, msg: &[u8]) -> Signature { diff --git a/core/keystore/src/lib.rs b/core/keystore/src/lib.rs index c36c6504c0110163fbf8000a337a368a0e575045..77106059f82bd3cad93d14f26c71c79112d72fa2 100644 --- a/core/keystore/src/lib.rs +++ b/core/keystore/src/lib.rs @@ -23,7 +23,7 @@ use std::path::PathBuf; use std::fs::{self, File}; use std::io::{self, Write}; -use substrate_primitives::{ed25519::{Pair, Public}, Pair as PairT}; +use substrate_primitives::crypto::{KeyTypeId, Pair, Public}; /// Keystore error. #[derive(Debug, derive_more::Display, derive_more::From)] @@ -59,7 +59,7 @@ impl std::error::Error for Error { /// Key store. pub struct Store { path: PathBuf, - additional: HashMap, + additional: HashMap<(KeyTypeId, Vec), Vec>, } impl Store { @@ -69,33 +69,49 @@ impl Store { Ok(Store { path, additional: HashMap::new() }) } + fn get_pair(&self, public: &TPair::Public) -> Result> { + let key = (TPair::KEY_TYPE, public.to_raw_vec()); + if let Some(bytes) = self.additional.get(&key) { + let pair = TPair::from_seed_slice(bytes) + .map_err(|_| Error::InvalidSeed)?; + return Ok(Some(pair)); + } + Ok(None) + } + + fn insert_pair(&mut self, pair: &TPair) { + let key = (TPair::KEY_TYPE, pair.public().to_raw_vec()); + self.additional.insert(key, pair.to_raw_vec()); + } + /// Generate a new key, placing it into the store. - pub fn generate(&self, password: &str) -> Result { - let (pair, phrase, _) = Pair::generate_with_phrase(Some(password)); - let mut file = File::create(self.key_file_path(&pair.public()))?; + pub fn generate(&self, password: &str) -> Result { + let (pair, phrase, _) = TPair::generate_with_phrase(Some(password)); + let mut file = File::create(self.key_file_path::(&pair.public()))?; ::serde_json::to_writer(&file, &phrase)?; file.flush()?; Ok(pair) } /// Create a new key from seed. Do not place it into the store. - pub fn generate_from_seed(&mut self, seed: &str) -> Result { - let pair = Pair::from_string(seed, None) + pub fn generate_from_seed(&mut self, seed: &str) -> Result { + let pair = TPair::from_string(seed, None) .ok().ok_or(Error::InvalidSeed)?; - self.additional.insert(pair.public(), pair.clone()); + self.insert_pair(&pair); Ok(pair) } /// Load a key file with given public key. - pub fn load(&self, public: &Public, password: &str) -> Result { - if let Some(pair) = self.additional.get(public) { - return Ok(pair.clone()); + pub fn load(&self, public: &TPair::Public, password: &str) -> Result { + if let Some(pair) = self.get_pair(public)? { + return Ok(pair) } - let path = self.key_file_path(public); + + let path = self.key_file_path::(public); let file = File::open(path)?; let phrase: String = ::serde_json::from_reader(&file)?; - let (pair, _) = Pair::from_phrase(&phrase, Some(password)) + let (pair, _) = TPair::from_phrase(&phrase, Some(password)) .ok().ok_or(Error::InvalidPhrase)?; if &pair.public() != public { return Err(Error::InvalidPassword); @@ -104,22 +120,28 @@ impl Store { } /// Get public keys of all stored keys. - pub fn contents(&self) -> Result> { - let mut public_keys: Vec = self.additional.keys().cloned().collect(); + pub fn contents(&self) -> Result> { + let mut public_keys: Vec = self.additional.keys() + .filter_map(|(ty, public)| { + if *ty != TPublic::KEY_TYPE { + return None + } + Some(TPublic::from_slice(public)) + }) + .collect(); + + let key_type: [u8; 4] = TPublic::KEY_TYPE.to_le_bytes(); for entry in fs::read_dir(&self.path)? { let entry = entry?; let path = entry.path(); // skip directories and non-unicode file names (hex is unicode) if let Some(name) = path.file_name().and_then(|n| n.to_str()) { - if name.len() != 64 { continue } - match hex::decode(name) { - Ok(ref hex) if hex.len() == 32 => { - let mut buf = [0; 32]; - buf.copy_from_slice(&hex[..]); - - public_keys.push(Public(buf)); + Ok(ref hex) => { + if hex[0..4] != key_type { continue } + let public = TPublic::from_slice(&hex[4..]); + public_keys.push(public); } _ => continue, } @@ -129,9 +151,12 @@ impl Store { Ok(public_keys) } - fn key_file_path(&self, public: &Public) -> PathBuf { + fn key_file_path(&self, public: &TPair::Public) -> PathBuf { let mut buf = self.path.clone(); - buf.push(hex::encode(public.as_slice())); + let bytes: [u8; 4] = TPair::KEY_TYPE.to_le_bytes(); + let key_type = hex::encode(bytes); + let key = hex::encode(public.as_slice()); + buf.push(key_type + key.as_str()); buf } } @@ -140,6 +165,7 @@ impl Store { mod tests { use super::*; use tempdir::TempDir; + use substrate_primitives::ed25519; use substrate_primitives::crypto::Ss58Codec; #[test] @@ -147,16 +173,16 @@ mod tests { let temp_dir = TempDir::new("keystore").unwrap(); let store = Store::open(temp_dir.path().to_owned()).unwrap(); - assert!(store.contents().unwrap().is_empty()); + assert!(store.contents::().unwrap().is_empty()); - let key = store.generate("thepassword").unwrap(); - let key2 = store.load(&key.public(), "thepassword").unwrap(); + let key: ed25519::Pair = store.generate("thepassword").unwrap(); + let key2: ed25519::Pair = store.load(&key.public(), "thepassword").unwrap(); - assert!(store.load(&key.public(), "notthepassword").is_err()); + assert!(store.load::(&key.public(), "notthepassword").is_err()); assert_eq!(key.public(), key2.public()); - assert_eq!(store.contents().unwrap()[0], key.public()); + assert_eq!(store.contents::().unwrap()[0], key.public()); } #[test] @@ -164,7 +190,9 @@ mod tests { let temp_dir = TempDir::new("keystore").unwrap(); let mut store = Store::open(temp_dir.path().to_owned()).unwrap(); - let pair = store.generate_from_seed("0x3d97c819d68f9bafa7d6e79cb991eebcd77d966c5334c0b94d9e1fa7ad0869dc").unwrap(); + let pair: ed25519::Pair = store + .generate_from_seed("0x3d97c819d68f9bafa7d6e79cb991eebcd77d966c5334c0b94d9e1fa7ad0869dc") + .unwrap(); assert_eq!("5DKUrgFqCPV8iAXx9sjy1nyBygQCeiUYRFWurZGhnrn3HJCA", pair.public().to_ss58check()); } } diff --git a/core/network/Cargo.toml b/core/network/Cargo.toml index 56a69039c380898eca6f818ff8728f0c5e0b6161..a94a3a01ba6876485d58d21c34dc03fc3111dd30 100644 --- a/core/network/Cargo.toml +++ b/core/network/Cargo.toml @@ -9,23 +9,26 @@ edition = "2018" [dependencies] bytes = "0.4" derive_more = "0.14.0" +either = "1.5.2" log = "0.4" parking_lot = "0.8.0" bitflags = "1.0" fnv = "1.0" futures = "0.1.17" +futures03 = { package = "futures-preview", version = "0.3.0-alpha.17", features = ["compat"] } +futures-timer = "0.2.1" linked-hash-map = "0.5" linked_hash_set = "0.1.3" lru-cache = "0.1.1" rustc-hex = "2.0" rand = "0.6" -libp2p = { version = "0.9.1", default-features = false, features = ["secp256k1", "libp2p-websocket"] } -fork-tree = { path = "../../core/util/fork-tree" } +libp2p = { version = "0.10.0", default-features = false, features = ["secp256k1", "libp2p-websocket"] } +fork-tree = { path = "../../core/utils/fork-tree" } primitives = { package = "substrate-primitives", path = "../../core/primitives" } consensus = { package = "substrate-consensus-common", path = "../../core/consensus/common" } client = { package = "substrate-client", path = "../../core/client" } runtime_primitives = { package = "sr-primitives", path = "../../core/sr-primitives" } -parity-codec = { version = "3.3", features = ["derive"] } +parity-codec = { version = "4.1.1", features = ["derive"] } peerset = { package = "substrate-peerset", path = "../../core/peerset" } serde = { version = "1.0.70", features = ["derive"] } serde_json = "1.0.24" @@ -33,7 +36,6 @@ slog = { version = "^2", features = ["nested-values"] } slog_derive = "0.1.1" smallvec = "0.6" tokio-io = "0.1" -tokio-timer = "0.2.11" tokio = { version = "0.1.11", optional = true } unsigned-varint = { version = "0.2.1", features = ["codec"] } keyring = { package = "substrate-keyring", path = "../../core/keyring", optional = true } @@ -41,7 +43,7 @@ test_client = { package = "substrate-test-client", path = "../../core/test-clien test-client = { package = "substrate-test-runtime-client", path = "../../core/test-runtime/client", optional = true } erased-serde = "0.3.9" void = "1.0" -zeroize = "0.6.0" +zeroize = "0.9.0" [dev-dependencies] env_logger = { version = "0.6" } @@ -50,10 +52,9 @@ quickcheck = "0.8.5" rand = "0.6.5" test-client = { package = "substrate-test-runtime-client", path = "../../core/test-runtime/client" } test_runtime = { package = "substrate-test-runtime", path = "../../core/test-runtime" } -consensus = { package = "substrate-consensus-common", path = "../../core/consensus/common", features = ["test-helpers"] } tempdir = "0.3" tokio = "0.1.11" [features] default = [] -test-helpers = ["keyring", "test-client", "consensus/test-helpers", "tokio"] +test-helpers = ["keyring", "test-client", "tokio"] diff --git a/core/network/src/behaviour.rs b/core/network/src/behaviour.rs index 35684bc2571edbd22200781835a7536b78c9d07b..2550e906600cb35a3e3dfa837648f01fea87db4e 100644 --- a/core/network/src/behaviour.rs +++ b/core/network/src/behaviour.rs @@ -14,72 +14,59 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use crate::{debug_info, discovery::DiscoveryBehaviour, discovery::DiscoveryOut, DiscoveryNetBehaviour}; +use crate::{ + debug_info, discovery::DiscoveryBehaviour, discovery::DiscoveryOut, DiscoveryNetBehaviour, + protocol::event::DhtEvent +}; +use crate::{ExHashT, specialization::NetworkSpecialization}; +use crate::protocol::{CustomMessageOutcome, Protocol}; use futures::prelude::*; use libp2p::NetworkBehaviour; -use libp2p::core::{Multiaddr, PeerId, ProtocolsHandler, protocols_handler::IntoProtocolsHandler, PublicKey}; -use libp2p::core::swarm::{ConnectedPoint, NetworkBehaviour, NetworkBehaviourAction}; -use libp2p::core::swarm::{NetworkBehaviourEventProcess, PollParameters}; -#[cfg(not(target_os = "unknown"))] -use libp2p::core::swarm::toggle::Toggle; -#[cfg(not(target_os = "unknown"))] -use libp2p::mdns::{Mdns, MdnsEvent}; +use libp2p::core::{Multiaddr, PeerId, PublicKey}; +use libp2p::core::swarm::{NetworkBehaviourAction, NetworkBehaviourEventProcess}; +use libp2p::core::{nodes::Substream, muxing::StreamMuxerBox}; +use libp2p::multihash::Multihash; use log::warn; +use runtime_primitives::traits::Block as BlockT; use std::iter; use void; -/// General behaviour of the network. +/// General behaviour of the network. Combines all protocols together. #[derive(NetworkBehaviour)] -#[behaviour(out_event = "TBehaviourEv", poll_method = "poll")] -pub struct Behaviour { - /// Main protocol that handles everything except the discovery and the technicalities. - user_protocol: UserBehaviourWrap, +#[behaviour(out_event = "BehaviourOut", poll_method = "poll")] +pub struct Behaviour, H: ExHashT> { + /// All the substrate-specific protocols. + substrate: Protocol, /// Periodically pings and identifies the nodes we are connected to, and store information in a /// cache. - debug_info: debug_info::DebugInfoBehaviour, - /// Discovers nodes of the network. Defined below. - discovery: DiscoveryBehaviour, - /// Discovers nodes on the local network. - #[cfg(not(target_os = "unknown"))] - mdns: Toggle>, + debug_info: debug_info::DebugInfoBehaviour>, + /// Discovers nodes of the network. + discovery: DiscoveryBehaviour>, /// Queue of events to produce for the outside. #[behaviour(ignore)] - events: Vec, + events: Vec>, } -impl Behaviour { +/// Event generated by `Behaviour`. +pub enum BehaviourOut { + SubstrateAction(CustomMessageOutcome), + Dht(DhtEvent), +} + +impl, H: ExHashT> Behaviour { /// Builds a new `Behaviour`. pub fn new( - user_protocol: TBehaviour, + substrate: Protocol, user_agent: String, local_public_key: PublicKey, known_addresses: Vec<(PeerId, Multiaddr)>, enable_mdns: bool, ) -> Self { - let debug_info = debug_info::DebugInfoBehaviour::new(user_agent, local_public_key.clone()); - - if enable_mdns { - #[cfg(target_os = "unknown")] - warn!(target: "sub-libp2p", "mDNS is not available on this platform"); - } - Behaviour { - user_protocol: UserBehaviourWrap(user_protocol), - debug_info, - discovery: DiscoveryBehaviour::new(local_public_key, known_addresses), - #[cfg(not(target_os = "unknown"))] - mdns: if enable_mdns { - match Mdns::new() { - Ok(mdns) => Some(mdns).into(), - Err(err) => { - warn!(target: "sub-libp2p", "Failed to initialize mDNS: {:?}", err); - None.into() - } - } - } else { - None.into() - }, + substrate, + debug_info: debug_info::DebugInfoBehaviour::new(user_agent, local_public_key.clone()), + discovery: DiscoveryBehaviour::new(local_public_key, known_addresses, enable_mdns), events: Vec::new(), } } @@ -104,33 +91,42 @@ impl Behaviour &TBehaviour { - &self.user_protocol.0 + pub fn user_protocol(&self) -> &Protocol { + &self.substrate } /// Returns a mutable reference to the user protocol. - pub fn user_protocol_mut(&mut self) -> &mut TBehaviour { - &mut self.user_protocol.0 + pub fn user_protocol_mut(&mut self) -> &mut Protocol { + &mut self.substrate + } + + /// Start querying a record from the DHT. Will later produce either a `ValueFound` or a `ValueNotFound` event. + pub fn get_value(&mut self, key: &Multihash) { + self.discovery.get_value(key); + } + + /// Starts putting a record into DHT. Will later produce either a `ValuePut` or a `ValuePutFailed` event. + pub fn put_value(&mut self, key: Multihash, value: Vec) { + self.discovery.put_value(key, value); } } -impl NetworkBehaviourEventProcess for -Behaviour { +impl, H: ExHashT> NetworkBehaviourEventProcess for +Behaviour { fn inject_event(&mut self, event: void::Void) { void::unreachable(event) } } -impl NetworkBehaviourEventProcess> for -Behaviour { - fn inject_event(&mut self, event: UserEventWrap) { - self.events.push(event.0); +impl, H: ExHashT> NetworkBehaviourEventProcess> for +Behaviour { + fn inject_event(&mut self, event: CustomMessageOutcome) { + self.events.push(BehaviourOut::SubstrateAction(event)); } } -impl NetworkBehaviourEventProcess - for Behaviour - where TBehaviour: DiscoveryNetBehaviour { +impl, H: ExHashT> NetworkBehaviourEventProcess + for Behaviour { fn inject_event(&mut self, event: debug_info::DebugInfoEvent) { let debug_info::DebugInfoEvent::Identified { peer_id, mut info } = event; if !info.protocol_version.contains("substrate") { @@ -146,38 +142,35 @@ impl NetworkBehaviourEventProcess NetworkBehaviourEventProcess - for Behaviour - where TBehaviour: DiscoveryNetBehaviour { +impl, H: ExHashT> NetworkBehaviourEventProcess + for Behaviour { fn inject_event(&mut self, out: DiscoveryOut) { match out { DiscoveryOut::Discovered(peer_id) => { - self.user_protocol.0.add_discovered_nodes(iter::once(peer_id)); + self.substrate.add_discovered_nodes(iter::once(peer_id)); + } + DiscoveryOut::ValueFound(results) => { + self.events.push(BehaviourOut::Dht(DhtEvent::ValueFound(results))); + } + DiscoveryOut::ValueNotFound(key) => { + self.events.push(BehaviourOut::Dht(DhtEvent::ValueNotFound(key))); + } + DiscoveryOut::ValuePut(key) => { + self.events.push(BehaviourOut::Dht(DhtEvent::ValuePut(key))); + } + DiscoveryOut::ValuePutFailed(key) => { + self.events.push(BehaviourOut::Dht(DhtEvent::ValuePutFailed(key))); } } } } -#[cfg(not(target_os = "unknown"))] -impl NetworkBehaviourEventProcess for - Behaviour - where TBehaviour: DiscoveryNetBehaviour { - fn inject_event(&mut self, event: MdnsEvent) { - match event { - MdnsEvent::Discovered(list) => { - self.user_protocol.0.add_discovered_nodes(list.into_iter().map(|(peer_id, _)| peer_id)); - }, - MdnsEvent::Expired(_) => {} - } - } -} - -impl Behaviour { - fn poll(&mut self) -> Async> { +impl, H: ExHashT> Behaviour { + fn poll(&mut self) -> Async>> { if !self.events.is_empty() { return Async::Ready(NetworkBehaviourAction::GenerateEvent(self.events.remove(0))) } @@ -185,71 +178,3 @@ impl Behaviour(TInner); -/// Event produced by `UserBehaviourWrap`. -pub struct UserEventWrap(TInner); -impl NetworkBehaviour for UserBehaviourWrap { - type ProtocolsHandler = TInner::ProtocolsHandler; - type OutEvent = UserEventWrap; - fn new_handler(&mut self) -> Self::ProtocolsHandler { self.0.new_handler() } - fn addresses_of_peer(&mut self, peer_id: &PeerId) -> Vec { - self.0.addresses_of_peer(peer_id) - } - fn inject_connected(&mut self, peer_id: PeerId, endpoint: ConnectedPoint) { - self.0.inject_connected(peer_id, endpoint) - } - fn inject_disconnected(&mut self, peer_id: &PeerId, endpoint: ConnectedPoint) { - self.0.inject_disconnected(peer_id, endpoint) - } - fn inject_node_event( - &mut self, - peer_id: PeerId, - event: <::Handler as ProtocolsHandler>::OutEvent - ) { - self.0.inject_node_event(peer_id, event) - } - fn poll( - &mut self, - params: &mut PollParameters - ) -> Async< - NetworkBehaviourAction< - <::Handler as ProtocolsHandler>::InEvent, - Self::OutEvent - > - > { - match self.0.poll(params) { - Async::NotReady => Async::NotReady, - Async::Ready(NetworkBehaviourAction::GenerateEvent(ev)) => - Async::Ready(NetworkBehaviourAction::GenerateEvent(UserEventWrap(ev))), - Async::Ready(NetworkBehaviourAction::DialAddress { address }) => - Async::Ready(NetworkBehaviourAction::DialAddress { address }), - Async::Ready(NetworkBehaviourAction::DialPeer { peer_id }) => - Async::Ready(NetworkBehaviourAction::DialPeer { peer_id }), - Async::Ready(NetworkBehaviourAction::SendEvent { peer_id, event }) => - Async::Ready(NetworkBehaviourAction::SendEvent { peer_id, event }), - Async::Ready(NetworkBehaviourAction::ReportObservedAddr { address }) => - Async::Ready(NetworkBehaviourAction::ReportObservedAddr { address }), - } - } - fn inject_replaced(&mut self, peer_id: PeerId, closed_endpoint: ConnectedPoint, new_endpoint: ConnectedPoint) { - self.0.inject_replaced(peer_id, closed_endpoint, new_endpoint) - } - fn inject_addr_reach_failure(&mut self, peer_id: Option<&PeerId>, addr: &Multiaddr, error: &dyn std::error::Error) { - self.0.inject_addr_reach_failure(peer_id, addr, error) - } - fn inject_dial_failure(&mut self, peer_id: &PeerId) { - self.0.inject_dial_failure(peer_id) - } - fn inject_new_listen_addr(&mut self, addr: &Multiaddr) { - self.0.inject_new_listen_addr(addr) - } - fn inject_expired_listen_addr(&mut self, addr: &Multiaddr) { - self.0.inject_expired_listen_addr(addr) - } - fn inject_new_external_addr(&mut self, addr: &Multiaddr) { - self.0.inject_new_external_addr(addr) - } -} diff --git a/core/network/src/config.rs b/core/network/src/config.rs index fd0a3a924eef1c6e6af423db14f11b9b974aa0a4..7c7c540d1175c23f783d120f72e239a538dbadf2 100644 --- a/core/network/src/config.rs +++ b/core/network/src/config.rs @@ -14,11 +14,14 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -//! Configuration for the networking layer of Substrate. +//! Configuration of the networking layer. +//! +//! The [`Params`] struct is the struct that must be passed in order to initialize the networking. +//! See the documentation of [`Params`]. pub use crate::protocol::ProtocolConfig; +pub use libp2p::{identity, core::PublicKey, wasm_ext::ExtTransport, build_multiaddr}; -use crate::ProtocolId; use crate::chain::{Client, FinalityProofProvider}; use crate::on_demand_layer::OnDemand; use crate::service::{ExHashT, TransactionPool}; @@ -29,30 +32,54 @@ use runtime_primitives::traits::{Block as BlockT}; use std::sync::Arc; use libp2p::identity::{Keypair, secp256k1, ed25519}; use libp2p::wasm_ext; -use libp2p::{Multiaddr, multiaddr::Protocol}; +use libp2p::{PeerId, Multiaddr, multiaddr}; use std::error::Error; -use std::{io::{self, Write}, iter, fs, net::Ipv4Addr, path::{Path, PathBuf}}; +use std::{io::{self, Write}, iter, fmt, fs, net::Ipv4Addr, path::{Path, PathBuf}}; use zeroize::Zeroize; -/// Service initialization parameters. +/// Network initialization parameters. pub struct Params { - /// Assigned roles for our node. + /// Assigned roles for our node (full, light, ...). pub roles: Roles, + /// Network layer configuration. pub network_config: NetworkConfiguration, - /// Substrate relay chain access point. + + /// Client that contains the blockchain. pub chain: Arc>, + /// Finality proof provider. + /// + /// This object, if `Some`, is used when a node on the network requests a proof of finality + /// from us. pub finality_proof_provider: Option>>, - /// On-demand service reference. + + /// How to build requests for proofs of finality. + /// + /// This object, if `Some`, is used when we need a proof of finality from another node. + pub finality_proof_request_builder: Option>, + + /// The `OnDemand` object acts as a "receiver" for block data requests from the client. + /// If `Some`, the network worker will process these requests and answer them. + /// Normally used only for light clients. pub on_demand: Option>>, - /// Transaction pool. + + /// Pool of transactions. + /// + /// The network worker will fetch transactions from this object in order to propagate them on + /// the network. pub transaction_pool: Arc>, + /// Name of the protocol to use on the wire. Should be different for each chain. pub protocol_id: ProtocolId, + /// Import queue to use. + /// + /// The import queue is the component that verifies that blocks received from other nodes are + /// valid. pub import_queue: Box>, - /// Protocol specialization. + + /// Customization of the network. Use this to plug additional networking capabilities. pub specialization: S, } @@ -94,6 +121,104 @@ impl parity_codec::Decode for Roles { } } +/// Finality proof request builder. +pub trait FinalityProofRequestBuilder: Send { + /// Build data blob, associated with the request. + fn build_request_data(&mut self, hash: &B::Hash) -> Vec; +} + +/// Implementation of `FinalityProofRequestBuilder` that builds a dummy empty request. +#[derive(Debug, Default)] +pub struct DummyFinalityProofRequestBuilder; + +impl FinalityProofRequestBuilder for DummyFinalityProofRequestBuilder { + fn build_request_data(&mut self, _: &B::Hash) -> Vec { + Vec::new() + } +} + +/// Shared finality proof request builder struct used by the queue. +pub type BoxFinalityProofRequestBuilder = Box + Send + Sync>; + +/// Name of a protocol, transmitted on the wire. Should be unique for each chain. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ProtocolId(smallvec::SmallVec<[u8; 6]>); + +impl<'a> From<&'a [u8]> for ProtocolId { + fn from(bytes: &'a [u8]) -> ProtocolId { + ProtocolId(bytes.into()) + } +} + +impl ProtocolId { + /// Exposes the `ProtocolId` as bytes. + pub fn as_bytes(&self) -> &[u8] { + self.0.as_ref() + } +} + +/// Parses a string address and returns the component, if valid. +/// +/// # Example +/// +/// ``` +/// # use substrate_network::{Multiaddr, PeerId, config::parse_str_addr}; +/// let (peer_id, addr) = parse_str_addr( +/// "/ip4/198.51.100.19/tcp/30333/p2p/QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV" +/// ).unwrap(); +/// assert_eq!(peer_id, "QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV".parse::().unwrap()); +/// assert_eq!(addr, "/ip4/198.51.100.19/tcp/30333".parse::().unwrap()); +/// ``` +/// +pub fn parse_str_addr(addr_str: &str) -> Result<(PeerId, Multiaddr), ParseErr> { + let mut addr: Multiaddr = addr_str.parse()?; + + let who = match addr.pop() { + Some(multiaddr::Protocol::P2p(key)) => PeerId::from_multihash(key) + .map_err(|_| ParseErr::InvalidPeerId)?, + _ => return Err(ParseErr::PeerIdMissing), + }; + + Ok((who, addr)) +} + +/// Error that can be generated by `parse_str_addr`. +#[derive(Debug)] +pub enum ParseErr { + /// Error while parsing the multiaddress. + MultiaddrParse(multiaddr::Error), + /// Multihash of the peer ID is invalid. + InvalidPeerId, + /// The peer ID is missing from the address. + PeerIdMissing, +} + +impl fmt::Display for ParseErr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ParseErr::MultiaddrParse(err) => write!(f, "{}", err), + ParseErr::InvalidPeerId => write!(f, "Peer id at the end of the address is invalid"), + ParseErr::PeerIdMissing => write!(f, "Peer id is missing from the address"), + } + } +} + +impl std::error::Error for ParseErr { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + ParseErr::MultiaddrParse(err) => Some(err), + ParseErr::InvalidPeerId => None, + ParseErr::PeerIdMissing => None, + } + } +} + +impl From for ParseErr { + fn from(err: multiaddr::Error) -> ParseErr { + ParseErr::MultiaddrParse(err) + } +} + /// Network service configuration. #[derive(Clone)] pub struct NetworkConfiguration { @@ -121,16 +246,8 @@ pub struct NetworkConfiguration { pub client_version: String, /// Name of the node. Sent over the wire for debugging purposes. pub node_name: String, - /// If true, the network will use mDNS to discover other libp2p nodes on the local network - /// and connect to them if they support the same chain. - pub enable_mdns: bool, - /// Optional external implementation of a libp2p transport. Used in WASM contexts where we need - /// some binding between the networking provided by the operating system or environment and - /// libp2p. - /// - /// This parameter exists whatever the target platform is, but it is expected to be set to - /// `Some` only when compiling for WASM. - pub wasm_external_transport: Option, + /// Configuration for the transport layer. + pub transport: TransportConfig, } impl Default for NetworkConfiguration { @@ -148,8 +265,10 @@ impl Default for NetworkConfiguration { non_reserved_mode: NonReservedPeerMode::Accept, client_version: "unknown".into(), node_name: "unknown".into(), - enable_mdns: false, - wasm_external_transport: None, + transport: TransportConfig::Normal { + enable_mdns: false, + wasm_external_transport: None, + }, } } } @@ -164,14 +283,48 @@ impl NetworkConfiguration { pub fn new_local() -> NetworkConfiguration { let mut config = NetworkConfiguration::new(); config.listen_addresses = vec![ - iter::once(Protocol::Ip4(Ipv4Addr::new(127, 0, 0, 1))) - .chain(iter::once(Protocol::Tcp(0))) + iter::once(multiaddr::Protocol::Ip4(Ipv4Addr::new(127, 0, 0, 1))) + .chain(iter::once(multiaddr::Protocol::Tcp(0))) + .collect() + ]; + config + } + + /// Create new default configuration for localhost-only connection with random port (useful for testing) + pub fn new_memory() -> NetworkConfiguration { + let mut config = NetworkConfiguration::new(); + config.listen_addresses = vec![ + iter::once(multiaddr::Protocol::Ip4(Ipv4Addr::new(127, 0, 0, 1))) + .chain(iter::once(multiaddr::Protocol::Tcp(0))) .collect() ]; config } } +/// Configuration for the transport layer. +#[derive(Clone)] +pub enum TransportConfig { + /// Normal transport mode. + Normal { + /// If true, the network will use mDNS to discover other libp2p nodes on the local network + /// and connect to them if they support the same chain. + enable_mdns: bool, + + /// Optional external implementation of a libp2p transport. Used in WASM contexts where we + /// need some binding between the networking provided by the operating system or environment + /// and libp2p. + /// + /// This parameter exists whatever the target platform is, but it is expected to be set to + /// `Some` only when compiling for WASM. + wasm_external_transport: Option, + }, + + /// Only allow connections within the same process. + /// Only addresses of the form `/memory/...` will be supported. + MemoryOnly, +} + /// The policy for connections to non-reserved peers. #[derive(Clone, Debug, PartialEq, Eq)] pub enum NonReservedPeerMode { diff --git a/core/network/src/custom_proto/behaviour.rs b/core/network/src/custom_proto/behaviour.rs index 975a1d2f3a030783afcfbaff2fef34ded42ae017..50aed1cfca60411598e3cb9339781f71a861424f 100644 --- a/core/network/src/custom_proto/behaviour.rs +++ b/core/network/src/custom_proto/behaviour.rs @@ -14,19 +14,19 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use crate::{DiscoveryNetBehaviour, ProtocolId}; +use crate::{DiscoveryNetBehaviour, config::ProtocolId}; use crate::custom_proto::handler::{CustomProtoHandlerProto, CustomProtoHandlerOut, CustomProtoHandlerIn}; use crate::custom_proto::upgrade::{CustomMessage, RegisteredProtocol}; use fnv::FnvHashMap; use futures::prelude::*; +use futures03::{compat::Compat, TryFutureExt as _, StreamExt as _, TryStreamExt as _}; use libp2p::core::swarm::{ConnectedPoint, NetworkBehaviour, NetworkBehaviourAction, PollParameters}; use libp2p::core::{Multiaddr, PeerId}; use log::{debug, error, trace, warn}; use smallvec::SmallVec; -use std::{borrow::Cow, collections::hash_map::Entry, cmp, error, marker::PhantomData, mem}; +use std::{borrow::Cow, collections::hash_map::Entry, cmp, error, marker::PhantomData, mem, pin::Pin}; use std::time::{Duration, Instant}; use tokio_io::{AsyncRead, AsyncWrite}; -use tokio_timer::clock::Clock; /// Network behaviour that handles opening substreams for custom protocols with other nodes. /// @@ -81,9 +81,6 @@ pub struct CustomProto { /// Marker to pin the generics. marker: PhantomData, - - /// `Clock` instance that uses the current execution context's source of time. - clock: Clock, } /// State of a peer we're connected to. @@ -104,7 +101,9 @@ enum PeerState { /// The peerset requested that we connect to this peer. We are not connected to this node. PendingRequest { /// When to actually start dialing. - timer: tokio_timer::Delay, + timer: Compat, + /// When the `timer` will trigger. + timer_deadline: Instant, }, /// The peerset requested that we connect to this peer. We are currently dialing this peer. @@ -134,7 +133,9 @@ enum PeerState { /// state mismatch. open: bool, /// When to enable this remote. - timer: tokio_timer::Delay, + timer: Compat, + /// When the `timer` will trigger. + timer_deadline: Instant, }, /// We are connected to this peer and the peerset has accepted it. The handler is in the @@ -239,7 +240,6 @@ impl CustomProto { next_incoming_index: peerset::IncomingIndex(0), events: SmallVec::new(), marker: PhantomData, - clock: Clock::new(), } } @@ -276,13 +276,13 @@ impl CustomProto { st @ PeerState::Banned { .. } => *entry.into_mut() = st, // DisabledPendingEnable => Disabled. - PeerState::DisabledPendingEnable { open, connected_point, timer } => { + PeerState::DisabledPendingEnable { open, connected_point, timer_deadline, .. } => { debug!(target: "sub-libp2p", "PSM <= Dropped({:?})", peer_id); self.peerset.dropped(peer_id.clone()); let banned_until = Some(if let Some(ban) = ban { - cmp::max(timer.deadline(), self.clock.now() + ban) + cmp::max(timer_deadline, Instant::now() + ban) } else { - timer.deadline() + timer_deadline }); *entry.into_mut() = PeerState::Disabled { open, connected_point, banned_until } }, @@ -296,8 +296,7 @@ impl CustomProto { peer_id: peer_id.clone(), event: CustomProtoHandlerIn::Disable, }); - let clock = &self.clock; - let banned_until = ban.map(|dur| clock.now() + dur); + let banned_until = ban.map(|dur| Instant::now() + dur); *entry.into_mut() = PeerState::Disabled { open, connected_point, banned_until } }, @@ -318,8 +317,7 @@ impl CustomProto { peer_id: peer_id.clone(), event: CustomProtoHandlerIn::Disable, }); - let clock = &self.clock; - let banned_until = ban.map(|dur| clock.now() + dur); + let banned_until = ban.map(|dur| Instant::now() + dur); *entry.into_mut() = PeerState::Disabled { open: false, connected_point, banned_until } }, @@ -384,11 +382,12 @@ impl CustomProto { }; match mem::replace(occ_entry.get_mut(), PeerState::Poisoned) { - PeerState::Banned { ref until } if *until > self.clock.now() => { + PeerState::Banned { ref until } if *until > Instant::now() => { debug!(target: "sub-libp2p", "PSM => Connect({:?}): Will start to connect at \ until {:?}", occ_entry.key(), until); *occ_entry.into_mut() = PeerState::PendingRequest { - timer: tokio_timer::Delay::new(until.clone()), + timer: futures_timer::Delay::new_at(until.clone()).compat(), + timer_deadline: until.clone(), }; }, @@ -400,13 +399,14 @@ impl CustomProto { }, PeerState::Disabled { open, ref connected_point, banned_until: Some(ref banned) } - if *banned > self.clock.now() => { + if *banned > Instant::now() => { debug!(target: "sub-libp2p", "PSM => Connect({:?}): Has idle connection through \ {:?} but node is banned until {:?}", occ_entry.key(), connected_point, banned); *occ_entry.into_mut() = PeerState::DisabledPendingEnable { connected_point: connected_point.clone(), open, - timer: tokio_timer::Delay::new(banned.clone()), + timer: futures_timer::Delay::new_at(banned.clone()).compat(), + timer_deadline: banned.clone(), }; }, @@ -476,13 +476,13 @@ impl CustomProto { *entry.into_mut() = st; }, - PeerState::DisabledPendingEnable { open, connected_point, timer } => { + PeerState::DisabledPendingEnable { open, connected_point, timer_deadline, .. } => { debug!(target: "sub-libp2p", "PSM => Drop({:?}): Interrupting pending \ enable", entry.key()); *entry.into_mut() = PeerState::Disabled { open, connected_point, - banned_until: Some(timer.deadline()), + banned_until: Some(timer_deadline), }; }, @@ -507,9 +507,9 @@ impl CustomProto { debug!(target: "sub-libp2p", "PSM => Drop({:?}): Was not yet connected", entry.key()); entry.remove(); }, - PeerState::PendingRequest { timer } => { + PeerState::PendingRequest { timer_deadline, .. } => { debug!(target: "sub-libp2p", "PSM => Drop({:?}): Was not yet connected", entry.key()); - *entry.into_mut() = PeerState::Banned { until: timer.deadline() } + *entry.into_mut() = PeerState::Banned { until: timer_deadline } }, PeerState::Poisoned => @@ -720,12 +720,12 @@ where } } - Some(PeerState::DisabledPendingEnable { open, timer, .. }) => { + Some(PeerState::DisabledPendingEnable { open, timer_deadline, .. }) => { debug!(target: "sub-libp2p", "Libp2p => Disconnected({:?}): Was disabled \ (through {:?}) but pending enable", peer_id, endpoint); debug!(target: "sub-libp2p", "PSM <= Dropped({:?})", peer_id); self.peerset.dropped(peer_id.clone()); - self.peers.insert(peer_id.clone(), PeerState::Banned { until: timer.deadline() }); + self.peers.insert(peer_id.clone(), PeerState::Banned { until: timer_deadline }); if open { debug!(target: "sub-libp2p", "External API <= Closed({:?})", peer_id); let event = CustomProtoOut::CustomProtocolClosed { @@ -789,7 +789,7 @@ where PeerState::Requested | PeerState::PendingRequest { .. } => { debug!(target: "sub-libp2p", "Libp2p => Dial failure for {:?}", peer_id); *entry.into_mut() = PeerState::Banned { - until: self.clock.now() + Duration::from_secs(5) + until: Instant::now() + Duration::from_secs(5) }; debug!(target: "sub-libp2p", "PSM <= Dropped({:?})", peer_id); self.peerset.dropped(peer_id.clone()) @@ -859,9 +859,14 @@ where debug_assert!(open); *entry.into_mut() = PeerState::Disabled { open: false, connected_point, banned_until }; }, - PeerState::DisabledPendingEnable { open, connected_point, timer } => { + PeerState::DisabledPendingEnable { open, connected_point, timer, timer_deadline } => { debug_assert!(open); - *entry.into_mut() = PeerState::DisabledPendingEnable { open: false, connected_point, timer }; + *entry.into_mut() = PeerState::DisabledPendingEnable { + open: false, + connected_point, + timer, + timer_deadline + }; }, _ => error!(target: "sub-libp2p", "State mismatch in the custom protos handler"), } @@ -925,6 +930,11 @@ where CustomProtoHandlerOut::ProtocolError { error, .. } => { debug!(target: "sub-libp2p", "Handler({:?}) => Severe protocol error: {:?}", source, error); + // A severe protocol error happens when we detect a "bad" node, such as a node on + // a different chain, or a node that doesn't speak the same protocol(s). We + // decrease the node's reputation, hence lowering the chances we try this node + // again in the short term. + self.peerset.report_peer(source.clone(), i32::min_value()); self.disconnect_peer_inner(&source, Some(Duration::from_secs(5))); } } @@ -932,7 +942,7 @@ where fn poll( &mut self, - _params: &mut PollParameters, + _params: &mut impl PollParameters, ) -> Async< NetworkBehaviourAction< CustomProtoHandlerIn, @@ -942,7 +952,10 @@ where // Poll for instructions from the peerset. // Note that the peerset is a *best effort* crate, and we have to use defensive programming. loop { - match self.peerset.poll() { + let mut peerset01 = futures03::stream::poll_fn(|cx| + futures03::Stream::poll_next(Pin::new(&mut self.peerset), cx) + ).map(|v| Ok::<_, ()>(v)).compat(); + match peerset01.poll() { Ok(Async::Ready(Some(peerset::Message::Accept(index)))) => { self.peerset_report_accept(index); } @@ -969,9 +982,9 @@ where for (peer_id, peer_state) in self.peers.iter_mut() { match mem::replace(peer_state, PeerState::Poisoned) { - PeerState::PendingRequest { mut timer } => { + PeerState::PendingRequest { mut timer, timer_deadline } => { if let Ok(Async::NotReady) = timer.poll() { - *peer_state = PeerState::PendingRequest { timer }; + *peer_state = PeerState::PendingRequest { timer, timer_deadline }; continue; } @@ -980,9 +993,14 @@ where *peer_state = PeerState::Requested; } - PeerState::DisabledPendingEnable { mut timer, connected_point, open } => { + PeerState::DisabledPendingEnable { mut timer, connected_point, open, timer_deadline } => { if let Ok(Async::NotReady) = timer.poll() { - *peer_state = PeerState::DisabledPendingEnable { timer, connected_point, open }; + *peer_state = PeerState::DisabledPendingEnable { + timer, + connected_point, + open, + timer_deadline + }; continue; } diff --git a/core/network/src/custom_proto/handler.rs b/core/network/src/custom_proto/handler.rs index 0ec60e79cd2f87d3e9d52385d0bd8630edecc63b..e4832b64b685686435c55c2656f19c71318eb0a7 100644 --- a/core/network/src/custom_proto/handler.rs +++ b/core/network/src/custom_proto/handler.rs @@ -17,6 +17,8 @@ use crate::custom_proto::upgrade::{CustomMessage, RegisteredProtocol}; use crate::custom_proto::upgrade::{RegisteredProtocolEvent, RegisteredProtocolSubstream}; use futures::prelude::*; +use futures03::{compat::Compat, TryFutureExt as _}; +use futures_timer::Delay; use libp2p::core::{ ConnectedPoint, PeerId, Endpoint, ProtocolsHandler, ProtocolsHandlerEvent, protocols_handler::IntoProtocolsHandler, @@ -29,7 +31,6 @@ use log::{debug, error}; use smallvec::{smallvec, SmallVec}; use std::{borrow::Cow, error, fmt, io, marker::PhantomData, mem, time::Duration}; use tokio_io::{AsyncRead, AsyncWrite}; -use tokio_timer::{Delay, clock::Clock}; /// Implements the `IntoProtocolsHandler` trait of libp2p. /// @@ -119,17 +120,15 @@ where } fn into_handler(self, remote_peer_id: &PeerId, connected_point: &ConnectedPoint) -> Self::Handler { - let clock = Clock::new(); CustomProtoHandler { protocol: self.protocol, endpoint: connected_point.to_endpoint(), remote_peer_id: remote_peer_id.clone(), state: ProtocolState::Init { substreams: SmallVec::new(), - init_deadline: Delay::new(clock.now() + Duration::from_secs(5)) + init_deadline: Delay::new(Duration::from_secs(5)).compat() }, events_queue: SmallVec::new(), - clock, } } } @@ -155,9 +154,6 @@ pub struct CustomProtoHandler { /// This queue must only ever be modified to insert elements at the back, or remove the first /// element. events_queue: SmallVec<[ProtocolsHandlerEvent, (), CustomProtoHandlerOut>; 16]>, - - /// `Clock` instance that uses the current execution context's source of time. - clock: Clock, } /// State of the handler. @@ -167,14 +163,14 @@ enum ProtocolState { /// List of substreams opened by the remote but that haven't been processed yet. substreams: SmallVec<[RegisteredProtocolSubstream; 6]>, /// Deadline after which the initialization is abnormally long. - init_deadline: Delay, + init_deadline: Compat, }, /// Handler is opening a substream in order to activate itself. /// If we are in this state, we haven't sent any `CustomProtocolOpen` yet. Opening { /// Deadline after which the opening is abnormally long. - deadline: Delay, + deadline: Compat, }, /// Normal operating mode. Contains the substreams that are open. @@ -286,7 +282,7 @@ where }); } ProtocolState::Opening { - deadline: Delay::new(self.clock.now() + Duration::from_secs(60)) + deadline: Delay::new(Duration::from_secs(60)).compat() } } else { @@ -356,7 +352,7 @@ where ProtocolState::Init { substreams, mut init_deadline } => { match init_deadline.poll() { Ok(Async::Ready(())) => { - init_deadline.reset(self.clock.now() + Duration::from_secs(60)); + init_deadline = Delay::new(Duration::from_secs(60)).compat(); error!(target: "sub-libp2p", "Handler initialization process is too long \ with {:?}", self.remote_peer_id) }, @@ -371,7 +367,7 @@ where ProtocolState::Opening { mut deadline } => { match deadline.poll() { Ok(Async::Ready(())) => { - deadline.reset(self.clock.now() + Duration::from_secs(60)); + deadline = Delay::new(Duration::from_secs(60)).compat(); let event = CustomProtoHandlerOut::ProtocolError { is_severe: true, error: "Timeout when opening protocol".to_string().into(), @@ -385,7 +381,7 @@ where }, Err(_) => { error!(target: "sub-libp2p", "Tokio timer has errored"); - deadline.reset(self.clock.now() + Duration::from_secs(60)); + deadline = Delay::new(Duration::from_secs(60)).compat(); self.state = ProtocolState::Opening { deadline }; None }, @@ -454,7 +450,7 @@ where // after all the substreams are closed. if reenable && shutdown.is_empty() { self.state = ProtocolState::Opening { - deadline: Delay::new(self.clock.now() + Duration::from_secs(60)) + deadline: Delay::new(Duration::from_secs(60)).compat() }; Some(ProtocolsHandlerEvent::OutboundSubstreamRequest { protocol: SubstreamProtocol::new(self.protocol.clone()), diff --git a/core/network/src/custom_proto/tests.rs b/core/network/src/custom_proto/tests.rs index 37d4db29e6887d84a71983945e22845ca8d095d5..33ff81be4752e553172a07474a68156d00fc7850 100644 --- a/core/network/src/custom_proto/tests.rs +++ b/core/network/src/custom_proto/tests.rs @@ -26,7 +26,7 @@ use libp2p::{PeerId, Multiaddr, Transport}; use rand::seq::SliceRandom; use std::{io, time::Duration, time::Instant}; use test_client::runtime::Block; -use crate::protocol::message::{Message as MessageAlias, generic::Message}; +use crate::message::{Message as MessageAlias, generic::Message}; use crate::custom_proto::{CustomProto, CustomProtoOut, CustomMessage}; /// Builds two nodes that have each other as bootstrap nodes. @@ -157,7 +157,7 @@ impl NetworkBehaviour for CustomProtoWithAddr fn poll( &mut self, - params: &mut PollParameters + params: &mut impl PollParameters ) -> Async< NetworkBehaviourAction< <::Handler as ProtocolsHandler>::InEvent, diff --git a/core/network/src/custom_proto/upgrade.rs b/core/network/src/custom_proto/upgrade.rs index 9ede4753494c6f78312b1e6327ae17137c1bcc26..4cb6cb5dd9042e4333d6982eb31349565b3a5253 100644 --- a/core/network/src/custom_proto/upgrade.rs +++ b/core/network/src/custom_proto/upgrade.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use crate::ProtocolId; +use crate::config::ProtocolId; use bytes::Bytes; use libp2p::core::{Negotiated, Endpoint, UpgradeInfo, InboundUpgrade, OutboundUpgrade, upgrade::ProtocolName}; use libp2p::tokio_codec::Framed; diff --git a/core/network/src/debug_info.rs b/core/network/src/debug_info.rs index f482f13fc201ebef18ab27dda5fc952d97bf3a4b..2ab93b04b0df1501c4f1cc2a725f965d06659b48 100644 --- a/core/network/src/debug_info.rs +++ b/core/network/src/debug_info.rs @@ -16,6 +16,7 @@ use fnv::FnvHashMap; use futures::prelude::*; +use futures03::{StreamExt as _, TryStreamExt as _}; use libp2p::Multiaddr; use libp2p::core::{either::EitherOutput, PeerId, PublicKey}; use libp2p::core::protocols_handler::{IntoProtocolsHandler, IntoProtocolsHandlerSelect, ProtocolsHandler}; @@ -27,7 +28,7 @@ use log::{debug, trace, error}; use std::collections::hash_map::Entry; use std::time::{Duration, Instant}; use tokio_io::{AsyncRead, AsyncWrite}; -use tokio_timer::Interval; +use futures_timer::Interval; /// Time after we disconnect from a node before we purge its information from the cache. const CACHE_EXPIRE: Duration = Duration::from_secs(10 * 60); @@ -44,7 +45,7 @@ pub struct DebugInfoBehaviour { /// Information that we know about all nodes. nodes_info: FnvHashMap, /// Interval at which we perform garbage collection in `nodes_info`. - garbage_collect: Interval, + garbage_collect: Box + Send>, } /// Information about a node we're connected to. @@ -76,7 +77,7 @@ impl DebugInfoBehaviour { ping: Ping::new(PingConfig::new()), identify, nodes_info: FnvHashMap::default(), - garbage_collect: Interval::new_interval(GARBAGE_COLLECT_INTERVAL), + garbage_collect: Box::new(Interval::new(GARBAGE_COLLECT_INTERVAL).map(|()| Ok(())).compat()), } } @@ -253,7 +254,7 @@ where TSubstream: AsyncRead + AsyncWrite { fn poll( &mut self, - params: &mut PollParameters + params: &mut impl PollParameters ) -> Async< NetworkBehaviourAction< <::Handler as ProtocolsHandler>::InEvent, diff --git a/core/network/src/discovery.rs b/core/network/src/discovery.rs index 4e44d9fa9ec732e08eeb4f8bace3515a70194c8f..e256f8490d532e987462573462750eb29d878eca 100644 --- a/core/network/src/discovery.rs +++ b/core/network/src/discovery.rs @@ -14,16 +14,53 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . +//! Discovery mechanisms of Substrate. +//! +//! The `DiscoveryBehaviour` struct implements the `NetworkBehaviour` trait of libp2p and is +//! responsible for discovering other nodes that are part of the network. +//! +//! Substrate uses the following mechanisms in order to discover nodes that are part of the network: +//! +//! - Bootstrap nodes. These are hard-coded node identities and addresses passed in the constructor +//! of the `DiscoveryBehaviour`. You can also call `add_known_address` later to add an entry. +//! +//! - mDNS. Discovers nodes on the local network by broadcasting UDP packets. +//! +//! - Kademlia random walk. Once connected, we perform random Kademlia `FIND_NODE` requests in +//! order for nodes to propagate to us their view of the network. This is performed automatically +//! by the `DiscoveryBehaviour`. +//! +//! Additionally, the `DiscoveryBehaviour` is also capable of storing and loading value in the +//! network-wide DHT. +//! +//! ## Usage +//! +//! The `DiscoveryBehaviour` generates events of type `DiscoveryOut`, most notably +//! `DiscoveryOut::Discovered` that is generated whenever we discover a node. +//! Only the identity of the node is returned. The node's addresses are stored within the +//! `DiscoveryBehaviour` and can be queried through the `NetworkBehaviour` trait. +//! +//! **Important**: In order for the discovery mechanism to work properly, there needs to be an +//! active mechanism that asks nodes for the addresses they are listening on. Whenever we learn +//! of a node's address, you must call `add_self_reported_address`. +//! + use futures::prelude::*; +use futures_timer::Delay; +use futures03::{compat::Compat, TryFutureExt as _}; use libp2p::core::{Multiaddr, PeerId, ProtocolsHandler, PublicKey}; use libp2p::core::swarm::{ConnectedPoint, NetworkBehaviour, NetworkBehaviourAction}; use libp2p::core::swarm::PollParameters; -use libp2p::kad::{Kademlia, KademliaOut}; +#[cfg(not(target_os = "unknown"))] +use libp2p::core::{swarm::toggle::Toggle, nodes::Substream, muxing::StreamMuxerBox}; +use libp2p::kad::{GetValueResult, Kademlia, KademliaOut, PutValueResult}; +#[cfg(not(target_os = "unknown"))] +use libp2p::mdns::{Mdns, MdnsEvent}; +use libp2p::multihash::Multihash; use libp2p::multiaddr::Protocol; use log::{debug, info, trace, warn}; -use std::{cmp, time::Duration}; +use std::{cmp, collections::VecDeque, num::NonZeroU8, time::Duration}; use tokio_io::{AsyncRead, AsyncWrite}; -use tokio_timer::{Delay, clock::Clock}; /// Implementation of `NetworkBehaviour` that discovers the nodes on the network. pub struct DiscoveryBehaviour { @@ -32,34 +69,60 @@ pub struct DiscoveryBehaviour { user_defined: Vec<(PeerId, Multiaddr)>, /// Kademlia requests and answers. kademlia: Kademlia, + /// Discovers nodes on the local network. + #[cfg(not(target_os = "unknown"))] + mdns: Toggle>>, /// Stream that fires when we need to perform the next random Kademlia query. - next_kad_random_query: Delay, + next_kad_random_query: Compat, /// After `next_kad_random_query` triggers, the next one triggers after this duration. duration_to_next_kad: Duration, - /// `Clock` instance that uses the current execution context's source of time. - clock: Clock, + /// Discovered nodes to return. + discoveries: VecDeque, /// Identity of our local node. local_peer_id: PeerId, + /// Number of nodes we're currently connected to. + num_connections: u64, } impl DiscoveryBehaviour { /// Builds a new `DiscoveryBehaviour`. /// /// `user_defined` is a list of known address for nodes that never expire. - pub fn new(local_public_key: PublicKey, user_defined: Vec<(PeerId, Multiaddr)>) -> Self { + pub fn new( + local_public_key: PublicKey, + user_defined: Vec<(PeerId, Multiaddr)>, + enable_mdns: bool + ) -> Self { + if enable_mdns { + #[cfg(target_os = "unknown")] + warn!(target: "sub-libp2p", "mDNS is not available on this platform"); + } + let mut kademlia = Kademlia::new(local_public_key.clone().into_peer_id()); for (peer_id, addr) in &user_defined { kademlia.add_address(peer_id, addr.clone()); } - let clock = Clock::new(); DiscoveryBehaviour { user_defined, kademlia, - next_kad_random_query: Delay::new(clock.now()), + next_kad_random_query: Delay::new(Duration::new(0, 0)).compat(), duration_to_next_kad: Duration::from_secs(1), - clock, + discoveries: VecDeque::new(), local_peer_id: local_public_key.into_peer_id(), + num_connections: 0, + #[cfg(not(target_os = "unknown"))] + mdns: if enable_mdns { + match Mdns::new() { + Ok(mdns) => Some(mdns).into(), + Err(err) => { + warn!(target: "sub-libp2p", "Failed to initialize mDNS: {:?}", err); + None.into() + } + } + } else { + None.into() + }, } } @@ -71,22 +134,56 @@ impl DiscoveryBehaviour { /// Adds a hard-coded address for the given peer, that never expires. /// /// This adds an entry to the parameter that was passed to `new`. + /// + /// If we didn't know this address before, also generates a `Discovered` event. pub fn add_known_address(&mut self, peer_id: PeerId, addr: Multiaddr) { if self.user_defined.iter().all(|(p, a)| *p != peer_id && *a != addr) { + self.discoveries.push_back(peer_id.clone()); self.user_defined.push((peer_id, addr)); } } /// Call this method when a node reports an address for itself. + /// + /// **Note**: It is important that you call this method, otherwise the discovery mechanism will + /// not properly work. pub fn add_self_reported_address(&mut self, peer_id: &PeerId, addr: Multiaddr) { self.kademlia.add_address(peer_id, addr); } + + /// Start fetching a record from the DHT. + /// + /// A corresponding `ValueFound` or `ValueNotFound` event will later be generated. + pub fn get_value(&mut self, key: &Multihash) { + self.kademlia.get_value(key, NonZeroU8::new(10) + .expect("Casting 10 to NonZeroU8 should succeed; qed")); + } + + /// Start putting a record into the DHT. Other nodes can later fetch that value with + /// `get_value`. + /// + /// A corresponding `ValuePut` or `ValuePutFailed` event will later be generated. + pub fn put_value(&mut self, key: Multihash, value: Vec) { + self.kademlia.put_value(key, value); + } } /// Event generated by the `DiscoveryBehaviour`. pub enum DiscoveryOut { /// We have discovered a node. Can be called multiple times with the same identity. Discovered(PeerId), + + /// The DHT yeided results for the record request, grouped in (key, value) pairs. + ValueFound(Vec<(Multihash, Vec)>), + + /// The record requested was not found in the DHT. + ValueNotFound(Multihash), + + /// The record with a given key was successfully inserted into the DHT. + ValuePut(Multihash), + + /// Inserting a value into the DHT failed. + ValuePutFailed(Multihash), } impl NetworkBehaviour for DiscoveryBehaviour @@ -105,6 +202,8 @@ where .filter_map(|(p, a)| if p == peer_id { Some(a.clone()) } else { None }) .collect::>(); list.extend(self.kademlia.addresses_of_peer(peer_id)); + #[cfg(not(target_os = "unknown"))] + list.extend(self.mdns.addresses_of_peer(peer_id)); trace!(target: "sub-libp2p", "Addresses of {:?} are {:?}", peer_id, list); if list.is_empty() { if self.kademlia.kbuckets_entries().any(|p| p == peer_id) { @@ -119,10 +218,12 @@ where } fn inject_connected(&mut self, peer_id: PeerId, endpoint: ConnectedPoint) { + self.num_connections += 1; NetworkBehaviour::inject_connected(&mut self.kademlia, peer_id, endpoint) } fn inject_disconnected(&mut self, peer_id: &PeerId, endpoint: ConnectedPoint) { + self.num_connections -= 1; NetworkBehaviour::inject_disconnected(&mut self.kademlia, peer_id, endpoint) } @@ -150,45 +251,17 @@ where fn poll( &mut self, - params: &mut PollParameters, + params: &mut impl PollParameters, ) -> Async< NetworkBehaviourAction< ::InEvent, Self::OutEvent, >, > { - // Poll Kademlia. - match self.kademlia.poll(params) { - Async::NotReady => (), - Async::Ready(NetworkBehaviourAction::GenerateEvent(ev)) => { - match ev { - KademliaOut::Discovered { .. } => {} - KademliaOut::KBucketAdded { peer_id, .. } => { - let ev = DiscoveryOut::Discovered(peer_id); - return Async::Ready(NetworkBehaviourAction::GenerateEvent(ev)); - } - KademliaOut::FindNodeResult { key, closer_peers } => { - trace!(target: "sub-libp2p", "Libp2p => Query for {:?} yielded {:?} results", - key, closer_peers.len()); - if closer_peers.is_empty() { - warn!(target: "sub-libp2p", "Libp2p => Random Kademlia query has yielded empty \ - results"); - } - } - // We never start any other type of query. - KademliaOut::GetProvidersResult { .. } => {} - KademliaOut::GetValueResult(_) => {} - KademliaOut::PutValueResult(_) => {} - } - }, - Async::Ready(NetworkBehaviourAction::DialAddress { address }) => - return Async::Ready(NetworkBehaviourAction::DialAddress { address }), - Async::Ready(NetworkBehaviourAction::DialPeer { peer_id }) => - return Async::Ready(NetworkBehaviourAction::DialPeer { peer_id }), - Async::Ready(NetworkBehaviourAction::SendEvent { peer_id, event }) => - return Async::Ready(NetworkBehaviourAction::SendEvent { peer_id, event }), - Async::Ready(NetworkBehaviourAction::ReportObservedAddr { address }) => - return Async::Ready(NetworkBehaviourAction::ReportObservedAddr { address }), + // Immediately process the content of `discovered`. + if let Some(peer_id) = self.discoveries.pop_front() { + let ev = DiscoveryOut::Discovered(peer_id); + return Async::Ready(NetworkBehaviourAction::GenerateEvent(ev)); } // Poll the stream that fires when we need to start a random Kademlia query. @@ -202,7 +275,7 @@ where self.kademlia.find_node(random_peer_id); // Reset the `Delay` to the next random. - self.next_kad_random_query.reset(self.clock.now() + self.duration_to_next_kad); + self.next_kad_random_query = Delay::new(self.duration_to_next_kad).compat(); self.duration_to_next_kad = cmp::min(self.duration_to_next_kad * 2, Duration::from_secs(60)); }, @@ -213,6 +286,95 @@ where } } + // Poll Kademlia. + loop { + match self.kademlia.poll(params) { + Async::NotReady => break, + Async::Ready(NetworkBehaviourAction::GenerateEvent(ev)) => { + match ev { + KademliaOut::Discovered { .. } => {} + KademliaOut::KBucketAdded { peer_id, .. } => { + let ev = DiscoveryOut::Discovered(peer_id); + return Async::Ready(NetworkBehaviourAction::GenerateEvent(ev)); + } + KademliaOut::FindNodeResult { key, closer_peers } => { + trace!(target: "sub-libp2p", "Libp2p => Query for {:?} yielded {:?} results", + key, closer_peers.len()); + if closer_peers.is_empty() && self.num_connections != 0 { + warn!(target: "sub-libp2p", "Libp2p => Random Kademlia query has yielded empty \ + results"); + } + } + KademliaOut::GetValueResult(res) => { + let ev = match res { + GetValueResult::Found { results } => { + let results = results + .into_iter() + .map(|r| (r.key, r.value)) + .collect(); + + DiscoveryOut::ValueFound(results) + } + GetValueResult::NotFound { key, .. } => { + DiscoveryOut::ValueNotFound(key) + } + }; + return Async::Ready(NetworkBehaviourAction::GenerateEvent(ev)); + } + KademliaOut::PutValueResult(res) => { + let ev = match res { + PutValueResult::Ok{ key, .. } => { + DiscoveryOut::ValuePut(key) + } + PutValueResult::Err { key, .. } => { + DiscoveryOut::ValuePutFailed(key) + } + }; + return Async::Ready(NetworkBehaviourAction::GenerateEvent(ev)); + } + // We never start any other type of query. + KademliaOut::GetProvidersResult { .. } => {} + } + }, + Async::Ready(NetworkBehaviourAction::DialAddress { address }) => + return Async::Ready(NetworkBehaviourAction::DialAddress { address }), + Async::Ready(NetworkBehaviourAction::DialPeer { peer_id }) => + return Async::Ready(NetworkBehaviourAction::DialPeer { peer_id }), + Async::Ready(NetworkBehaviourAction::SendEvent { peer_id, event }) => + return Async::Ready(NetworkBehaviourAction::SendEvent { peer_id, event }), + Async::Ready(NetworkBehaviourAction::ReportObservedAddr { address }) => + return Async::Ready(NetworkBehaviourAction::ReportObservedAddr { address }), + } + } + + // Poll mDNS. + #[cfg(not(target_os = "unknown"))] + loop { + match self.mdns.poll(params) { + Async::NotReady => break, + Async::Ready(NetworkBehaviourAction::GenerateEvent(event)) => { + match event { + MdnsEvent::Discovered(list) => { + self.discoveries.extend(list.into_iter().map(|(peer_id, _)| peer_id)); + if let Some(peer_id) = self.discoveries.pop_front() { + let ev = DiscoveryOut::Discovered(peer_id); + return Async::Ready(NetworkBehaviourAction::GenerateEvent(ev)); + } + }, + MdnsEvent::Expired(_) => {} + } + }, + Async::Ready(NetworkBehaviourAction::DialAddress { address }) => + return Async::Ready(NetworkBehaviourAction::DialAddress { address }), + Async::Ready(NetworkBehaviourAction::DialPeer { peer_id }) => + return Async::Ready(NetworkBehaviourAction::DialPeer { peer_id }), + Async::Ready(NetworkBehaviourAction::SendEvent { event, .. }) => + match event {}, // `event` is an enum with no variant + Async::Ready(NetworkBehaviourAction::ReportObservedAddr { address }) => + return Async::Ready(NetworkBehaviourAction::ReportObservedAddr { address }), + } + } + Async::NotReady } } @@ -247,7 +409,7 @@ mod tests { upgrade::apply(out.stream, upgrade, endpoint) }); - let behaviour = DiscoveryBehaviour::new(keypair.public(), user_defined.clone()); + let behaviour = DiscoveryBehaviour::new(keypair.public(), user_defined.clone(), false); let mut swarm = Swarm::new(transport, behaviour, keypair.public().into_peer_id()); let listen_addr: Multiaddr = format!("/memory/{}", rand::random::()).parse().unwrap(); diff --git a/core/network/src/lib.rs b/core/network/src/lib.rs index 44ca7eab4c133f251a9d88140318c0d868a4ae96..7b740976d2c28a95e493d75471747155a71ea45b 100644 --- a/core/network/src/lib.rs +++ b/core/network/src/lib.rs @@ -158,6 +158,15 @@ //! `Arc` from the `NetworkWorker`, which can be shared amongst multiple places //! in order to give orders to the networking. //! +//! See the [`config`] module for more information about how to configure the networking. +//! +//! After the `NetworkWorker` has been created, the important things to do are: +//! +//! - Calling `NetworkWorker::poll` in order to advance the network. +//! - Calling `on_block_import` whenever a block is added to the client. +//! - Calling `on_block_finalized` whenever a block is finalized. +//! - Calling `trigger_repropagate` when a transaction is added to the pool. +//! //! More precise usage details are still being worked on and will likely change in the future. //! @@ -167,9 +176,7 @@ mod custom_proto; mod debug_info; mod discovery; mod on_demand_layer; -#[macro_use] mod protocol; -mod protocol_behaviour; mod service; mod transport; @@ -181,26 +188,26 @@ pub mod test; pub use chain::{Client as ClientHandle, FinalityProofProvider}; pub use service::{ - NetworkService, NetworkWorker, FetchFuture, TransactionPool, ManageNetwork, - NetworkMsg, ExHashT, ReportHandle, + NetworkService, NetworkWorker, TransactionPool, ExHashT, ReportHandle, + NetworkStateInfo, }; -pub use config::{NodeKeyConfig, Secret, Secp256k1Secret, Ed25519Secret}; pub use protocol::{PeerInfo, Context, consensus_gossip, message, specialization}; pub use protocol::sync::SyncState; -pub use libp2p::{Multiaddr, multiaddr, build_multiaddr}; -pub use libp2p::{identity, PeerId, core::PublicKey}; +pub use libp2p::{Multiaddr, PeerId}; +#[doc(inline)] +pub use libp2p::multiaddr; pub use message::{generic as generic_message, RequestId, Status as StatusMessage}; -pub use error::Error; -pub use protocol::on_demand::AlwaysBadChecker; pub use on_demand_layer::{OnDemand, RemoteResponse}; + +// Used by the `construct_simple_protocol!` macro. #[doc(hidden)] pub use runtime_primitives::traits::Block as BlockT; use libp2p::core::nodes::ConnectedPoint; use serde::{Deserialize, Serialize}; use slog_derive::SerdeValue; -use std::{collections::{HashMap, HashSet}, fmt, time::Duration}; +use std::{collections::{HashMap, HashSet}, time::Duration}; /// Extension trait for `NetworkBehaviour` that also accepts discovering nodes. pub trait DiscoveryNetBehaviour { @@ -213,73 +220,6 @@ pub trait DiscoveryNetBehaviour { fn add_discovered_nodes(&mut self, nodes: impl Iterator); } -/// Name of a protocol, transmitted on the wire. Should be unique for each chain. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct ProtocolId(smallvec::SmallVec<[u8; 6]>); - -impl<'a> From<&'a [u8]> for ProtocolId { - fn from(bytes: &'a [u8]) -> ProtocolId { - ProtocolId(bytes.into()) - } -} - -impl ProtocolId { - /// Exposes the `ProtocolId` as bytes. - pub fn as_bytes(&self) -> &[u8] { - self.0.as_ref() - } -} - -/// Parses a string address and returns the component, if valid. -pub fn parse_str_addr(addr_str: &str) -> Result<(PeerId, Multiaddr), ParseErr> { - let mut addr: Multiaddr = addr_str.parse()?; - - let who = match addr.pop() { - Some(multiaddr::Protocol::P2p(key)) => PeerId::from_multihash(key) - .map_err(|_| ParseErr::InvalidPeerId)?, - _ => return Err(ParseErr::PeerIdMissing), - }; - - Ok((who, addr)) -} - -/// Error that can be generated by `parse_str_addr`. -#[derive(Debug)] -pub enum ParseErr { - /// Error while parsing the multiaddress. - MultiaddrParse(multiaddr::Error), - /// Multihash of the peer ID is invalid. - InvalidPeerId, - /// The peer ID is missing from the address. - PeerIdMissing, -} - -impl fmt::Display for ParseErr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - ParseErr::MultiaddrParse(err) => write!(f, "{}", err), - ParseErr::InvalidPeerId => write!(f, "Peer id at the end of the address is invalid"), - ParseErr::PeerIdMissing => write!(f, "Peer id is missing from the address"), - } - } -} - -impl std::error::Error for ParseErr { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - ParseErr::MultiaddrParse(err) => Some(err), - ParseErr::InvalidPeerId => None, - ParseErr::PeerIdMissing => None, - } - } -} - -impl From for ParseErr { - fn from(err: multiaddr::Error) -> ParseErr { - ParseErr::MultiaddrParse(err) - } -} - /// Returns general information about the networking. /// /// Meant for general diagnostic purposes. diff --git a/core/network/src/on_demand_layer.rs b/core/network/src/on_demand_layer.rs index 86b3d6b7f4a12a304420db5f685539a3cb3b0ce4..17a70bbe0df57fe4de30b99996ece7dbc5a00e20 100644 --- a/core/network/src/on_demand_layer.rs +++ b/core/network/src/on_demand_layer.rs @@ -19,6 +19,7 @@ use crate::protocol::on_demand::RequestData; use std::sync::Arc; use futures::{prelude::*, sync::mpsc, sync::oneshot}; +use futures03::compat::{Compat01As03, Future01CompatExt as _}; use parking_lot::Mutex; use client::error::Error as ClientError; use client::light::fetcher::{Fetcher, FetchChecker, RemoteHeaderRequest, @@ -82,22 +83,22 @@ impl Fetcher for OnDemand where B: BlockT, B::Header: HeaderT, { - type RemoteHeaderResult = RemoteResponse; - type RemoteReadResult = RemoteResponse>>; - type RemoteCallResult = RemoteResponse>; - type RemoteChangesResult = RemoteResponse, u32)>>; - type RemoteBodyResult = RemoteResponse>; + type RemoteHeaderResult = Compat01As03>; + type RemoteReadResult = Compat01As03>>>; + type RemoteCallResult = Compat01As03>>; + type RemoteChangesResult = Compat01As03, u32)>>>; + type RemoteBodyResult = Compat01As03>>; fn remote_header(&self, request: RemoteHeaderRequest) -> Self::RemoteHeaderResult { let (sender, receiver) = oneshot::channel(); let _ = self.requests_send.unbounded_send(RequestData::RemoteHeader(request, sender)); - RemoteResponse { receiver } + RemoteResponse { receiver }.compat() } fn remote_read(&self, request: RemoteReadRequest) -> Self::RemoteReadResult { let (sender, receiver) = oneshot::channel(); let _ = self.requests_send.unbounded_send(RequestData::RemoteRead(request, sender)); - RemoteResponse { receiver } + RemoteResponse { receiver }.compat() } fn remote_read_child( @@ -106,25 +107,25 @@ impl Fetcher for OnDemand where ) -> Self::RemoteReadResult { let (sender, receiver) = oneshot::channel(); let _ = self.requests_send.unbounded_send(RequestData::RemoteReadChild(request, sender)); - RemoteResponse { receiver } + RemoteResponse { receiver }.compat() } fn remote_call(&self, request: RemoteCallRequest) -> Self::RemoteCallResult { let (sender, receiver) = oneshot::channel(); let _ = self.requests_send.unbounded_send(RequestData::RemoteCall(request, sender)); - RemoteResponse { receiver } + RemoteResponse { receiver }.compat() } fn remote_changes(&self, request: RemoteChangesRequest) -> Self::RemoteChangesResult { let (sender, receiver) = oneshot::channel(); let _ = self.requests_send.unbounded_send(RequestData::RemoteChanges(request, sender)); - RemoteResponse { receiver } + RemoteResponse { receiver }.compat() } fn remote_body(&self, request: RemoteBodyRequest) -> Self::RemoteBodyResult { let (sender, receiver) = oneshot::channel(); let _ = self.requests_send.unbounded_send(RequestData::RemoteBody(request, sender)); - RemoteResponse { receiver } + RemoteResponse { receiver }.compat() } } diff --git a/core/network/src/protocol.rs b/core/network/src/protocol.rs index 3038898b09c30821c3d11630b0af6749c2a3107e..97b6c7ac283a73c321656fb0e4d84e17b03ed879 100644 --- a/core/network/src/protocol.rs +++ b/core/network/src/protocol.rs @@ -14,8 +14,14 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . +use crate::{DiscoveryNetBehaviour, config::ProtocolId}; +use crate::custom_proto::{CustomProto, CustomProtoOut}; use futures::prelude::*; -use libp2p::PeerId; +use futures03::{StreamExt as _, TryStreamExt as _}; +use libp2p::{Multiaddr, PeerId}; +use libp2p::core::swarm::{ConnectedPoint, NetworkBehaviour, NetworkBehaviourAction, PollParameters}; +use libp2p::core::{nodes::Substream, muxing::StreamMuxerBox}; +use libp2p::core::protocols_handler::{ProtocolsHandler, IntoProtocolsHandler}; use primitives::storage::StorageKey; use consensus::{import_queue::IncomingBlock, import_queue::Origin, BlockOrigin}; use runtime_primitives::{generic::BlockId, ConsensusEngineId, Justification}; @@ -23,19 +29,16 @@ use runtime_primitives::traits::{ Block as BlockT, Header as HeaderT, NumberFor, One, Zero, CheckedSub, SaturatedConversion }; -use consensus::import_queue::SharedFinalityProofRequestBuilder; -use message::{ - BlockRequest as BlockRequestMessage, - FinalityProofRequest as FinalityProofRequestMessage, Message, -}; -use message::{BlockAttributes, Direction, FromBlock, RequestId}; +use consensus::import_queue::{BlockImportResult, BlockImportError}; +use message::{BlockAttributes, Direction, FromBlock, Message, RequestId}; use message::generic::{Message as GenericMessage, ConsensusMessage}; +use event::Event; use consensus_gossip::{ConsensusGossip, MessageRecipient as GossipMessageRecipient}; use on_demand::{OnDemandCore, OnDemandNetwork, RequestData}; use specialization::NetworkSpecialization; -use sync::{ChainSync, Context as SyncContext, SyncState}; +use sync::{ChainSync, SyncState}; use crate::service::{TransactionPool, ExHashT}; -use crate::config::Roles; +use crate::config::{BoxFinalityProofRequestBuilder, Roles}; use rustc_hex::ToHex; use std::collections::{BTreeMap, HashMap}; use std::sync::Arc; @@ -49,6 +52,7 @@ use util::LruHashSet; mod util; pub mod consensus_gossip; pub mod message; +pub mod event; pub mod on_demand; pub mod specialization; pub mod sync; @@ -88,9 +92,9 @@ const RPC_FAILED_REPUTATION_CHANGE: i32 = -(1 << 12); // Lock must always be taken in order declared here. pub struct Protocol, H: ExHashT> { /// Interval at which we call `tick`. - tick_timeout: tokio_timer::Interval, + tick_timeout: Box + Send>, /// Interval at which we call `propagate_extrinsics`. - propagate_timeout: tokio_timer::Interval, + propagate_timeout: Box + Send>, config: ProtocolConfig, /// Handler for on-demand requests. on_demand_core: OnDemandCore, @@ -101,12 +105,13 @@ pub struct Protocol, H: ExHashT> { context_data: ContextData, // Connected peers pending Status message. handshaking_peers: HashMap, -} - -/// A peer from whom we have received a Status message. -#[derive(Clone)] -pub struct ConnectedPeer { - pub peer_info: PeerInfo + /// Used to report reputation changes. + peerset_handle: peerset::PeersetHandle, + transaction_pool: Arc>, + /// When asked for a proof of finality, we use this struct to build one. + finality_proof_provider: Option>>, + /// Handles opening the unique substream and sending and receiving raw messages. + behaviour: CustomProto, Substream>, } /// A peer that we are connected to @@ -144,27 +149,18 @@ pub struct PeerInfo { pub best_number: ::Number, } -/// Context passed as input to the methods of `protocol.rs` and that is used to communicate back -/// with the network. -pub trait NetworkOut { - /// Adjusts the reputation of the peer. Use this to point out that a peer has been malign or - /// irresponsible or appeared lazy. - fn report_peer(&mut self, who: PeerId, reputation: i32); - - /// Force disconnecting from a peer. - fn disconnect_peer(&mut self, who: PeerId); - - /// Send a message to a peer. - fn send_message(&mut self, who: PeerId, message: Message); +struct OnDemandIn<'a, B: BlockT> { + behaviour: &'a mut CustomProto, Substream>, + peerset: peerset::PeersetHandle, } -impl<'a, 'b, B: BlockT> OnDemandNetwork for &'a mut &'b mut dyn NetworkOut { +impl<'a, B: BlockT> OnDemandNetwork for OnDemandIn<'a, B> { fn report_peer(&mut self, who: &PeerId, reputation: i32) { - NetworkOut::report_peer(**self, who.clone(), reputation) + self.peerset.report_peer(who.clone(), reputation) } fn disconnect_peer(&mut self, who: &PeerId) { - NetworkOut::disconnect_peer(**self, who.clone()) + self.behaviour.disconnect_peer(who) } fn send_header_request(&mut self, who: &PeerId, id: RequestId, block: <::Header as HeaderT>::Number) { @@ -173,7 +169,7 @@ impl<'a, 'b, B: BlockT> OnDemandNetwork for &'a mut &'b mut dyn NetworkOut block, }); - NetworkOut::send_message(**self, who.clone(), message) + self.behaviour.send_packet(who, message) } fn send_read_request(&mut self, who: &PeerId, id: RequestId, block: ::Hash, key: Vec) { @@ -183,7 +179,7 @@ impl<'a, 'b, B: BlockT> OnDemandNetwork for &'a mut &'b mut dyn NetworkOut key, }); - NetworkOut::send_message(**self, who.clone(), message) + self.behaviour.send_packet(who, message) } fn send_read_child_request( @@ -201,7 +197,7 @@ impl<'a, 'b, B: BlockT> OnDemandNetwork for &'a mut &'b mut dyn NetworkOut key, }); - NetworkOut::send_message(**self, who.clone(), message) + self.behaviour.send_packet(who, message) } fn send_call_request( @@ -219,7 +215,7 @@ impl<'a, 'b, B: BlockT> OnDemandNetwork for &'a mut &'b mut dyn NetworkOut data, }); - NetworkOut::send_message(**self, who.clone(), message) + self.behaviour.send_packet(who, message) } fn send_changes_request( @@ -241,7 +237,7 @@ impl<'a, 'b, B: BlockT> OnDemandNetwork for &'a mut &'b mut dyn NetworkOut key, }); - NetworkOut::send_message(**self, who.clone(), message) + self.behaviour.send_packet(who, message) } fn send_body_request( @@ -263,7 +259,7 @@ impl<'a, 'b, B: BlockT> OnDemandNetwork for &'a mut &'b mut dyn NetworkOut max, }); - NetworkOut::send_message(**self, who.clone(), message) + self.behaviour.send_packet(who, message) } } @@ -285,29 +281,34 @@ pub trait Context { /// Protocol context. struct ProtocolContext<'a, B: 'a + BlockT, H: 'a + ExHashT> { - network_out: &'a mut dyn NetworkOut, + behaviour: &'a mut CustomProto, Substream>, context_data: &'a mut ContextData, + peerset_handle: &'a peerset::PeersetHandle, } impl<'a, B: BlockT + 'a, H: 'a + ExHashT> ProtocolContext<'a, B, H> { - fn new(context_data: &'a mut ContextData, network_out: &'a mut dyn NetworkOut) -> Self { - ProtocolContext { network_out, context_data } + fn new( + context_data: &'a mut ContextData, + behaviour: &'a mut CustomProto, Substream>, + peerset_handle: &'a peerset::PeersetHandle, + ) -> Self { + ProtocolContext { context_data, peerset_handle, behaviour } } } impl<'a, B: BlockT + 'a, H: ExHashT + 'a> Context for ProtocolContext<'a, B, H> { fn report_peer(&mut self, who: PeerId, reputation: i32) { - self.network_out.report_peer(who, reputation) + self.peerset_handle.report_peer(who, reputation) } fn disconnect_peer(&mut self, who: PeerId) { - self.network_out.disconnect_peer(who) + self.behaviour.disconnect_peer(&who) } fn send_consensus(&mut self, who: PeerId, consensus: ConsensusMessage) { send_message( + self.behaviour, &mut self.context_data.peers, - self.network_out, who, GenericMessage::Consensus(consensus) ) @@ -315,46 +316,14 @@ impl<'a, B: BlockT + 'a, H: ExHashT + 'a> Context for ProtocolContext<'a, B, fn send_chain_specific(&mut self, who: PeerId, message: Vec) { send_message( + self.behaviour, &mut self.context_data.peers, - self.network_out, who, GenericMessage::ChainSpecific(message) ) } } -impl<'a, B: BlockT + 'a, H: ExHashT + 'a> SyncContext for ProtocolContext<'a, B, H> { - fn report_peer(&mut self, who: PeerId, reputation: i32) { - self.network_out.report_peer(who, reputation) - } - - fn disconnect_peer(&mut self, who: PeerId) { - self.network_out.disconnect_peer(who) - } - - fn client(&self) -> &dyn Client { - &*self.context_data.chain - } - - fn send_finality_proof_request(&mut self, who: PeerId, request: FinalityProofRequestMessage) { - send_message( - &mut self.context_data.peers, - self.network_out, - who, - GenericMessage::FinalityProofRequest(request) - ) - } - - fn send_block_request(&mut self, who: PeerId, request: BlockRequestMessage) { - send_message( - &mut self.context_data.peers, - self.network_out, - who, - GenericMessage::BlockRequest(request) - ) - } -} - /// Data necessary to create a context. struct ContextData { // All connected peers @@ -384,12 +353,21 @@ impl, H: ExHashT> Protocol { chain: Arc>, checker: Arc>, specialization: S, - ) -> error::Result> { + transaction_pool: Arc>, + finality_proof_provider: Option>>, + finality_proof_request_builder: Option>, + protocol_id: ProtocolId, + peerset_config: peerset::PeersetConfig, + ) -> error::Result<(Protocol, peerset::PeersetHandle)> { let info = chain.info(); - let sync = ChainSync::new(config.roles, &info); - Ok(Protocol { - tick_timeout: tokio_timer::Interval::new_interval(TICK_TIMEOUT), - propagate_timeout: tokio_timer::Interval::new_interval(PROPAGATE_TIMEOUT), + let sync = ChainSync::new(config.roles, chain.clone(), &info, finality_proof_request_builder); + let (peerset, peerset_handle) = peerset::Peerset::from_config(peerset_config); + let versions = &((MIN_VERSION as u8)..=(CURRENT_VERSION as u8)).collect::>(); + let behaviour = CustomProto::new(protocol_id, versions, peerset); + + let protocol = Protocol { + tick_timeout: Box::new(futures_timer::Interval::new(TICK_TIMEOUT).map(|v| Ok::<_, ()>(v)).compat()), + propagate_timeout: Box::new(futures_timer::Interval::new(PROPAGATE_TIMEOUT).map(|v| Ok::<_, ()>(v)).compat()), config: config, context_data: ContextData { peers: HashMap::new(), @@ -401,7 +379,38 @@ impl, H: ExHashT> Protocol { specialization: specialization, consensus_gossip: ConsensusGossip::new(), handshaking_peers: HashMap::new(), - }) + transaction_pool, + finality_proof_provider, + peerset_handle: peerset_handle.clone(), + behaviour, + }; + + Ok((protocol, peerset_handle)) + } + + /// Returns the list of all the peers we have an open channel to. + pub fn open_peers(&self) -> impl Iterator { + self.behaviour.open_peers() + } + + /// Returns true if we have a channel open with this node. + pub fn is_open(&self, peer_id: &PeerId) -> bool { + self.behaviour.is_open(peer_id) + } + + /// Disconnects the given peer if we are connected to it. + pub fn disconnect_peer(&mut self, peer_id: &PeerId) { + self.behaviour.disconnect_peer(peer_id) + } + + /// Returns true if we try to open protocols with the given peer. + pub fn is_enabled(&self, peer_id: &PeerId) -> bool { + self.behaviour.is_enabled(peer_id) + } + + /// Returns the state of the peerset manager, for debugging purposes. + pub fn peerset_debug_info(&mut self) -> serde_json::Value { + self.behaviour.peerset_debug_info() } /// Returns the number of peers we're connected to. @@ -436,24 +445,11 @@ impl, H: ExHashT> Protocol { /// Starts a new data demand request. /// /// The parameter contains a `Sender` where the result, once received, must be sent. - pub(crate) fn add_on_demand_request(&mut self, mut network_out: &mut dyn NetworkOut, rq: RequestData) { - self.on_demand_core.add_request(&mut network_out, rq); - } - - pub fn poll( - &mut self, - network_out: &mut dyn NetworkOut, - transaction_pool: &(impl TransactionPool + ?Sized) - ) -> Poll { - while let Ok(Async::Ready(_)) = self.tick_timeout.poll() { - self.tick(network_out); - } - - while let Ok(Async::Ready(_)) = self.propagate_timeout.poll() { - self.propagate_extrinsics(network_out, transaction_pool); - } - - Ok(Async::NotReady) + pub(crate) fn add_on_demand_request(&mut self, rq: RequestData) { + self.on_demand_core.add_request(OnDemandIn { + behaviour: &mut self.behaviour, + peerset: self.peerset_handle.clone(), + }, rq); } fn is_on_demand_response(&self, who: &PeerId, response_id: message::RequestId) -> bool { @@ -462,7 +458,6 @@ impl, H: ExHashT> Protocol { fn handle_response( &mut self, - network_out: &mut dyn NetworkOut, who: PeerId, response: &message::BlockResponse ) -> Option> { @@ -477,8 +472,8 @@ impl, H: ExHashT> Protocol { return request.map(|(_, r)| r) } trace!(target: "sync", "Unexpected response packet from {} ({})", who, response.id); - network_out.report_peer(who.clone(), i32::min_value()); - network_out.disconnect_peer(who); + self.peerset_handle.report_peer(who.clone(), i32::min_value()); + self.behaviour.disconnect_peer(&who); } None } @@ -497,78 +492,80 @@ 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, - network_out: &mut dyn NetworkOut, - transaction_pool: &(impl TransactionPool + ?Sized), who: PeerId, message: Message, - finality_proof_provider: Option<&dyn FinalityProofProvider> ) -> CustomMessageOutcome { match message { - GenericMessage::Status(s) => self.on_status_message(network_out, who, s), - GenericMessage::BlockRequest(r) => self.on_block_request(network_out, who, r), + GenericMessage::Status(s) => self.on_status_message(who, s), + GenericMessage::BlockRequest(r) => self.on_block_request(who, r), GenericMessage::BlockResponse(r) => { // Note, this is safe because only `ordinary bodies` and `remote bodies` are received in this matter. if self.is_on_demand_response(&who, r.id) { - self.on_remote_body_response(network_out, who, r); + self.on_remote_body_response(who, r); } else { - if let Some(request) = self.handle_response(network_out, who.clone(), &r) { - let outcome = self.on_block_response(network_out, who.clone(), request, r); + if let Some(request) = self.handle_response(who.clone(), &r) { + let outcome = self.on_block_response(who.clone(), request, r); self.update_peer_info(&who); return outcome } } }, GenericMessage::BlockAnnounce(announce) => { - let outcome = self.on_block_announce(network_out, who.clone(), announce); + let outcome = self.on_block_announce(who.clone(), announce); self.update_peer_info(&who); return outcome; }, GenericMessage::Transactions(m) => - self.on_extrinsics(network_out, transaction_pool, who, m), - GenericMessage::RemoteCallRequest(request) => self.on_remote_call_request(network_out, who, request), + self.on_extrinsics(who, m), + GenericMessage::RemoteCallRequest(request) => self.on_remote_call_request(who, request), GenericMessage::RemoteCallResponse(response) => - self.on_remote_call_response(network_out, who, response), + self.on_remote_call_response(who, response), GenericMessage::RemoteReadRequest(request) => - self.on_remote_read_request(network_out, who, request), + self.on_remote_read_request(who, request), GenericMessage::RemoteReadResponse(response) => - self.on_remote_read_response(network_out, who, response), + self.on_remote_read_response(who, response), GenericMessage::RemoteHeaderRequest(request) => - self.on_remote_header_request(network_out, who, request), + self.on_remote_header_request(who, request), GenericMessage::RemoteHeaderResponse(response) => - self.on_remote_header_response(network_out, who, response), + self.on_remote_header_response(who, response), GenericMessage::RemoteChangesRequest(request) => - self.on_remote_changes_request(network_out, who, request), + self.on_remote_changes_request(who, request), GenericMessage::RemoteChangesResponse(response) => - self.on_remote_changes_response(network_out, who, response), + self.on_remote_changes_response(who, response), GenericMessage::FinalityProofRequest(request) => - self.on_finality_proof_request(network_out, who, request, finality_proof_provider), + self.on_finality_proof_request(who, request), GenericMessage::FinalityProofResponse(response) => - return self.on_finality_proof_response(network_out, who, response), + return self.on_finality_proof_response(who, response), + GenericMessage::RemoteReadChildRequest(_) => {} GenericMessage::Consensus(msg) => { if self.context_data.peers.get(&who).map_or(false, |peer| peer.info.protocol_version > 2) { self.consensus_gossip.on_incoming( - &mut ProtocolContext::new(&mut self.context_data, network_out), + &mut ProtocolContext::new(&mut self.context_data, &mut self.behaviour, &self.peerset_handle), who, msg, ); } } - other => self.specialization.on_message( - &mut ProtocolContext::new(&mut self.context_data, network_out), + GenericMessage::ChainSpecific(msg) => self.specialization.on_message( + &mut ProtocolContext::new(&mut self.context_data, &mut self.behaviour, &self.peerset_handle), who, - &mut Some(other), + msg, ), } CustomMessageOutcome::None } - fn send_message(&mut self, network_out: &mut dyn NetworkOut, who: PeerId, message: Message) { + fn send_message(&mut self, who: PeerId, message: Message) { send_message::( + &mut self.behaviour, &mut self.context_data.peers, - network_out, who, message, ); @@ -577,31 +574,28 @@ impl, H: ExHashT> Protocol { /// Locks `self` and returns a context plus the `ConsensusGossip` struct. pub fn consensus_gossip_lock<'a>( &'a mut self, - network_out: &'a mut dyn NetworkOut ) -> (impl Context + 'a, &'a mut ConsensusGossip) { - let context = ProtocolContext::new(&mut self.context_data, network_out); + let context = ProtocolContext::new(&mut self.context_data, &mut self.behaviour, &self.peerset_handle); (context, &mut self.consensus_gossip) } /// Locks `self` and returns a context plus the network specialization. pub fn specialization_lock<'a>( &'a mut self, - network_out: &'a mut dyn NetworkOut ) -> (impl Context + 'a, &'a mut S) { - let context = ProtocolContext::new(&mut self.context_data, network_out); + let context = ProtocolContext::new(&mut self.context_data, &mut self.behaviour, &self.peerset_handle); (context, &mut self.specialization) } /// Gossip a consensus message to the network. pub fn gossip_consensus_message( &mut self, - network_out: &mut dyn NetworkOut, topic: B::Hash, engine_id: ConsensusEngineId, message: Vec, recipient: GossipMessageRecipient, ) { - let mut context = ProtocolContext::new(&mut self.context_data, network_out); + let mut context = ProtocolContext::new(&mut self.context_data, &mut self.behaviour, &self.peerset_handle); let message = ConsensusMessage { data: message, engine_id }; match recipient { GossipMessageRecipient::BroadcastToAll => @@ -609,19 +603,19 @@ impl, H: ExHashT> Protocol { GossipMessageRecipient::BroadcastNew => self.consensus_gossip.multicast(&mut context, topic, message, false), GossipMessageRecipient::Peer(who) => - self.send_message(network_out, who, GenericMessage::Consensus(message)), + self.send_message(who, GenericMessage::Consensus(message)), } } /// Called when a new peer is connected - pub fn on_peer_connected(&mut self, network_out: &mut dyn NetworkOut, who: PeerId) { + pub fn on_peer_connected(&mut self, who: PeerId) { trace!(target: "sync", "Connecting {}", who); self.handshaking_peers.insert(who.clone(), HandshakingPeer { timestamp: time::Instant::now() }); - self.send_status(network_out, who); + self.send_status(who); } /// Called by peer when it is disconnecting - pub fn on_peer_disconnected(&mut self, mut network_out: &mut dyn NetworkOut, peer: PeerId) { + pub fn on_peer_disconnected(&mut self, peer: PeerId) { trace!(target: "sync", "Disconnecting {}", peer); // lock all the the peer lists so that add/remove peer events are in order let removed = { @@ -629,20 +623,23 @@ impl, H: ExHashT> Protocol { self.context_data.peers.remove(&peer) }; if let Some(peer_data) = removed { - let mut context = ProtocolContext::new(&mut self.context_data, network_out); + let mut context = ProtocolContext::new(&mut self.context_data, &mut self.behaviour, &self.peerset_handle); if peer_data.info.protocol_version > 2 { self.consensus_gossip.peer_disconnected(&mut context, peer.clone()); } - self.sync.peer_disconnected(&mut context, peer.clone()); + self.sync.peer_disconnected(peer.clone()); self.specialization.on_disconnect(&mut context, peer.clone()); - self.on_demand_core.on_disconnect(&mut network_out, peer); + self.on_demand_core.on_disconnect(OnDemandIn { + behaviour: &mut self.behaviour, + peerset: self.peerset_handle.clone(), + }, peer); } } /// Called as a back-pressure mechanism if the networking detects that the peer cannot process /// our messaging rate fast enough. - pub fn on_clogged_peer(&self, network_out: &mut dyn NetworkOut, who: PeerId, _msg: Option>) { - network_out.report_peer(who.clone(), CLOGGED_PEER_REPUTATION_CHANGE); + pub fn on_clogged_peer(&self, who: PeerId, _msg: Option>) { + self.peerset_handle.report_peer(who.clone(), CLOGGED_PEER_REPUTATION_CHANGE); // Print some diagnostics. if let Some(peer) = self.context_data.peers.get(&who) { @@ -657,7 +654,6 @@ impl, H: ExHashT> Protocol { fn on_block_request( &mut self, - network_out: &mut dyn NetworkOut, peer: PeerId, request: message::BlockRequest ) { @@ -671,8 +667,8 @@ impl, H: ExHashT> Protocol { // sending block requests to the node that is unable to serve it is considered a bad behavior if !self.config.roles.is_full() { trace!(target: "sync", "Peer {} is trying to sync from the light node", peer); - network_out.disconnect_peer(peer.clone()); - network_out.report_peer(peer, i32::min_value()); + self.behaviour.disconnect_peer(&peer); + self.peerset_handle.report_peer(peer, i32::min_value()); return; } @@ -730,12 +726,16 @@ impl, H: ExHashT> Protocol { blocks: blocks, }; trace!(target: "sync", "Sending BlockResponse with {} blocks", response.blocks.len()); - self.send_message(network_out, peer, GenericMessage::BlockResponse(response)) + self.send_message(peer, GenericMessage::BlockResponse(response)) + } + + /// Adjusts the reputation of a node. + pub fn report_peer(&self, who: PeerId, reputation: i32) { + self.peerset_handle.report_peer(who, reputation) } fn on_block_response( &mut self, - network_out: &mut dyn NetworkOut, peer: PeerId, request: message::BlockRequest, response: message::BlockResponse, @@ -758,29 +758,29 @@ impl, H: ExHashT> Protocol { // TODO [andre]: move this logic to the import queue so that // justifications are imported asynchronously (#1482) if request.fields == message::BlockAttributes::JUSTIFICATION { - let outcome = self.sync.on_block_justification_data( - &mut ProtocolContext::new(&mut self.context_data, network_out), - peer, - response - ); - - if let Some((origin, hash, nb, just)) = outcome { - CustomMessageOutcome::JustificationImport(origin, hash, nb, just) - } else { - CustomMessageOutcome::None + match self.sync.on_block_justification(peer, response) { + Ok(sync::OnBlockJustification::Nothing) => CustomMessageOutcome::None, + Ok(sync::OnBlockJustification::Import { peer, hash, number, justification }) => + CustomMessageOutcome::JustificationImport(peer, hash, number, justification), + Err(sync::BadPeer(id, repu)) => { + self.behaviour.disconnect_peer(&id); + self.peerset_handle.report_peer(id, repu); + CustomMessageOutcome::None + } } - } else { - let outcome = self.sync.on_block_data( - &mut ProtocolContext::new(&mut self.context_data, network_out), - peer, - request, - response - ); - if let Some((origin, blocks)) = outcome { - CustomMessageOutcome::BlockImport(origin, blocks) - } else { - CustomMessageOutcome::None + match self.sync.on_block_data(peer, request, response) { + Ok(sync::OnBlockData::Import(origin, blocks)) => + CustomMessageOutcome::BlockImport(origin, blocks), + Ok(sync::OnBlockData::Request(peer, req)) => { + self.send_message(peer, GenericMessage::BlockRequest(req)); + CustomMessageOutcome::None + } + Err(sync::BadPeer(id, repu)) => { + self.behaviour.disconnect_peer(&id); + self.peerset_handle.report_peer(id, repu); + CustomMessageOutcome::None + } } } } @@ -788,14 +788,18 @@ impl, H: ExHashT> Protocol { /// Perform time based maintenance. /// /// > **Note**: This method normally doesn't have to be called except for testing purposes. - pub fn tick(&mut self, mut network_out: &mut dyn NetworkOut) { - self.consensus_gossip.tick(&mut ProtocolContext::new(&mut self.context_data, network_out)); - self.maintain_peers(network_out); - self.sync.tick(&mut ProtocolContext::new(&mut self.context_data, network_out)); - self.on_demand_core.maintain_peers(&mut network_out); + pub fn tick(&mut self) { + self.consensus_gossip.tick( + &mut ProtocolContext::new(&mut self.context_data, &mut self.behaviour, &self.peerset_handle) + ); + self.maintain_peers(); + self.on_demand_core.maintain_peers(OnDemandIn { + behaviour: &mut self.behaviour, + peerset: self.peerset_handle.clone(), + }); } - fn maintain_peers(&mut self, network_out: &mut dyn NetworkOut) { + fn maintain_peers(&mut self) { let tick = time::Instant::now(); let mut aborting = Vec::new(); { @@ -816,20 +820,22 @@ impl, H: ExHashT> Protocol { } } - self.specialization.maintain_peers(&mut ProtocolContext::new(&mut self.context_data, network_out)); + self.specialization.maintain_peers( + &mut ProtocolContext::new(&mut self.context_data, &mut self.behaviour, &self.peerset_handle) + ); for p in aborting { - network_out.disconnect_peer(p.clone()); - network_out.report_peer(p, TIMEOUT_REPUTATION_CHANGE); + self.behaviour.disconnect_peer(&p); + self.peerset_handle.report_peer(p, TIMEOUT_REPUTATION_CHANGE); } } /// Called by peer to report status - fn on_status_message(&mut self, mut network_out: &mut dyn NetworkOut, who: PeerId, status: message::Status) { + fn on_status_message(&mut self, who: PeerId, status: message::Status) { trace!(target: "sync", "New peer {} {:?}", who, status); let protocol_version = { if self.context_data.peers.contains_key(&who) { debug!("Unexpected status packet from {}", who); - network_out.report_peer(who, UNEXPECTED_STATUS_REPUTATION_CHANGE); + self.peerset_handle.report_peer(who, UNEXPECTED_STATUS_REPUTATION_CHANGE); return; } if status.genesis_hash != self.genesis_hash { @@ -838,14 +844,14 @@ impl, H: ExHashT> Protocol { "Peer is on different chain (our genesis: {} theirs: {})", self.genesis_hash, status.genesis_hash ); - network_out.report_peer(who.clone(), i32::min_value()); - network_out.disconnect_peer(who); + self.peerset_handle.report_peer(who.clone(), i32::min_value()); + self.behaviour.disconnect_peer(&who); return; } if status.version < MIN_VERSION && CURRENT_VERSION < status.min_supported_version { trace!(target: "protocol", "Peer {:?} using unsupported protocol version {}", who, status.version); - network_out.report_peer(who.clone(), i32::min_value()); - network_out.disconnect_peer(who); + self.peerset_handle.report_peer(who.clone(), i32::min_value()); + self.behaviour.disconnect_peer(&who); return; } @@ -853,8 +859,8 @@ impl, H: ExHashT> Protocol { // we're not interested in light peers if status.roles.is_light() { debug!(target: "sync", "Peer {} is unable to serve light requests", who); - network_out.report_peer(who.clone(), i32::min_value()); - network_out.disconnect_peer(who); + self.peerset_handle.report_peer(who.clone(), i32::min_value()); + self.behaviour.disconnect_peer(&who); return; } @@ -870,8 +876,8 @@ impl, H: ExHashT> Protocol { .saturated_into::(); if blocks_difference > LIGHT_MAXIMAL_BLOCKS_DIFFERENCE { debug!(target: "sync", "Peer {} is far behind us and will unable to serve light requests", who); - network_out.report_peer(who.clone(), PEER_BEHIND_US_LIGHT_REPUTATION_CHANGE); - network_out.disconnect_peer(who); + self.peerset_handle.report_peer(who.clone(), PEER_BEHIND_US_LIGHT_REPUTATION_CHANGE); + self.behaviour.disconnect_peer(&who); return; } } @@ -908,9 +914,19 @@ impl, H: ExHashT> Protocol { }; let info = self.context_data.peers.get(&who).expect("We just inserted above; QED").info.clone(); - self.on_demand_core.on_connect(&mut network_out, who.clone(), status.roles, status.best_number); - let mut context = ProtocolContext::new(&mut self.context_data, network_out); - self.sync.new_peer(&mut context, who.clone(), info); + self.on_demand_core.on_connect(OnDemandIn { + behaviour: &mut self.behaviour, + peerset: self.peerset_handle.clone(), + }, who.clone(), status.roles, status.best_number); + match self.sync.new_peer(who.clone(), info) { + Ok(None) => (), + Ok(Some(req)) => self.send_message(who.clone(), GenericMessage::BlockRequest(req)), + Err(sync::BadPeer(id, repu)) => { + self.behaviour.disconnect_peer(&id); + self.peerset_handle.report_peer(id, repu) + } + } + let mut context = ProtocolContext::new(&mut self.context_data, &mut self.behaviour, &self.peerset_handle); if protocol_version > 2 { self.consensus_gossip.new_peer(&mut context, who.clone(), status.roles); } @@ -920,8 +936,6 @@ impl, H: ExHashT> Protocol { /// Called when peer sends us new extrinsics fn on_extrinsics( &mut self, - network_out: &mut dyn NetworkOut, - transaction_pool: &(impl TransactionPool + ?Sized), who: PeerId, extrinsics: message::Transactions ) { @@ -933,8 +947,8 @@ impl, H: ExHashT> Protocol { trace!(target: "sync", "Received {} extrinsics from {}", extrinsics.len(), who); if let Some(ref mut peer) = self.context_data.peers.get_mut(&who) { for t in extrinsics { - if let Some(hash) = transaction_pool.import(&t) { - network_out.report_peer(who.clone(), NEW_EXTRINSIC_REPUTATION_CHANGE); + if let Some(hash) = self.transaction_pool.import(&t) { + self.peerset_handle.report_peer(who.clone(), NEW_EXTRINSIC_REPUTATION_CHANGE); peer.known_extrinsics.insert(hash); } else { trace!(target: "sync", "Extrinsic rejected"); @@ -946,8 +960,6 @@ impl, H: ExHashT> Protocol { /// Call when we must propagate ready extrinsics to peers. pub fn propagate_extrinsics( &mut self, - network_out: &mut dyn NetworkOut, - transaction_pool: &(impl TransactionPool + ?Sized) ) { debug!(target: "sync", "Propagating extrinsics"); @@ -956,7 +968,7 @@ impl, H: ExHashT> Protocol { return; } - let extrinsics = transaction_pool.transactions(); + let extrinsics = self.transaction_pool.transactions(); let mut propagated_to = HashMap::new(); for (who, peer) in self.context_data.peers.iter_mut() { let (hashes, to_send): (Vec<_>, Vec<_>) = extrinsics @@ -973,18 +985,18 @@ impl, H: ExHashT> Protocol { .push(who.to_base58()); } trace!(target: "sync", "Sending {} transactions to {}", to_send.len(), who); - network_out.send_message(who.clone(), GenericMessage::Transactions(to_send)) + self.behaviour.send_packet(who, GenericMessage::Transactions(to_send)) } } - transaction_pool.on_broadcasted(propagated_to); + self.transaction_pool.on_broadcasted(propagated_to); } /// Make sure an important block is propagated to peers. /// /// In chain-based consensus, we often need to make sure non-best forks are /// at least temporarily synced. - pub fn announce_block(&mut self, network_out: &mut dyn NetworkOut, hash: B::Hash) { + pub fn announce_block(&mut self, hash: B::Hash) { let header = match self.context_data.chain.header(&BlockId::Hash(hash)) { Ok(Some(header)) => header, Ok(None) => { @@ -996,6 +1008,12 @@ impl, H: ExHashT> Protocol { return; } }; + + // don't announce genesis block since it will be ignored + if header.number().is_zero() { + return; + } + let hash = header.hash(); let message = GenericMessage::BlockAnnounce(message::BlockAnnounce { header: header.clone() }); @@ -1003,12 +1021,12 @@ impl, H: ExHashT> Protocol { for (who, ref mut peer) in self.context_data.peers.iter_mut() { trace!(target: "sync", "Reannouncing block {:?} to {}", hash, who); peer.known_blocks.insert(hash); - network_out.send_message(who.clone(), message.clone()) + self.behaviour.send_packet(who, message.clone()) } } /// Send Status message - fn send_status(&mut self, network_out: &mut dyn NetworkOut, who: PeerId) { + fn send_status(&mut self, who: PeerId) { let info = self.context_data.chain.info(); let status = message::generic::Status { version: CURRENT_VERSION, @@ -1020,12 +1038,11 @@ impl, H: ExHashT> Protocol { chain_status: self.specialization.status(), }; - self.send_message(network_out, who, GenericMessage::Status(status)) + self.send_message(who, GenericMessage::Status(status)) } fn on_block_announce( &mut self, - mut network_out: &mut dyn NetworkOut, who: PeerId, announce: message::BlockAnnounce ) -> CustomMessageOutcome { @@ -1036,29 +1053,32 @@ impl, H: ExHashT> Protocol { peer.known_blocks.insert(hash.clone()); } } - self.on_demand_core.on_block_announce(&mut network_out, who.clone(), *header.number()); - let try_import = self.sync.on_block_announce( - &mut ProtocolContext::new(&mut self.context_data, network_out), - who.clone(), - hash, - &header, - ); - - // try_import is only true when we have all data required to import 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) - if !try_import { - return CustomMessageOutcome::None; + self.on_demand_core.on_block_announce(OnDemandIn { + behaviour: &mut self.behaviour, + peerset: self.peerset_handle.clone(), + }, who.clone(), *header.number()); + + match self.sync.on_block_announce(who.clone(), hash, &header) { + 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 + // 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) + return CustomMessageOutcome::None + } + sync::OnBlockAnnounce::ImportHeader => () // We proceed with the import. } // to import header from announced block let's construct response to request that normally would have // been sent over network (but it is not in our case) let blocks_to_import = self.sync.on_block_data( - &mut ProtocolContext::new(&mut self.context_data, network_out), who.clone(), message::generic::BlockRequest { id: 0, @@ -1083,17 +1103,25 @@ impl, H: ExHashT> Protocol { }, ); match blocks_to_import { - Some((origin, blocks)) => CustomMessageOutcome::BlockImport(origin, blocks), - None => CustomMessageOutcome::None, + Ok(sync::OnBlockData::Import(origin, blocks)) => CustomMessageOutcome::BlockImport(origin, blocks), + Ok(sync::OnBlockData::Request(peer, req)) => { + self.send_message(peer, GenericMessage::BlockRequest(req)); + CustomMessageOutcome::None + } + Err(sync::BadPeer(id, repu)) => { + self.behaviour.disconnect_peer(&id); + self.peerset_handle.report_peer(id, repu); + CustomMessageOutcome::None + } } } /// Call this when a block has been imported in the import queue and we should announce it on /// the network. - pub fn on_block_imported(&mut self, network_out: &mut dyn NetworkOut, hash: B::Hash, header: &B::Header) { + pub fn on_block_imported(&mut self, hash: B::Hash, header: &B::Header) { self.sync.update_chain_info(header); self.specialization.on_block_imported( - &mut ProtocolContext::new(&mut self.context_data, network_out), + &mut ProtocolContext::new(&mut self.context_data, &mut self.behaviour, &self.peerset_handle), hash.clone(), header, ); @@ -1110,24 +1138,19 @@ impl, H: ExHashT> Protocol { for (who, ref mut peer) in self.context_data.peers.iter_mut() { if peer.known_blocks.insert(hash.clone()) { trace!(target: "sync", "Announcing block {:?} to {}", hash, who); - network_out.send_message(who.clone(), message.clone()) + self.behaviour.send_packet(who, message.clone()) } } } /// Call this when a block has been finalized. The sync layer may have some additional /// requesting to perform. - pub fn on_block_finalized(&mut self, network_out: &mut dyn NetworkOut, hash: B::Hash, header: &B::Header) { - self.sync.on_block_finalized( - &hash, - *header.number(), - &mut ProtocolContext::new(&mut self.context_data, network_out), - ); + pub fn on_block_finalized(&mut self, hash: B::Hash, header: &B::Header) { + self.sync.on_block_finalized(&hash, *header.number()) } fn on_remote_call_request( &mut self, - network_out: &mut dyn NetworkOut, who: PeerId, request: message::RemoteCallRequest, ) { @@ -1151,13 +1174,12 @@ impl, H: ExHashT> Protocol { request.block, error ); - network_out.report_peer(who.clone(), RPC_FAILED_REPUTATION_CHANGE); + self.peerset_handle.report_peer(who.clone(), RPC_FAILED_REPUTATION_CHANGE); Default::default() } }; self.send_message( - network_out, who, GenericMessage::RemoteCallResponse(message::RemoteCallResponse { id: request.id, @@ -1170,63 +1192,51 @@ impl, H: ExHashT> Protocol { /// /// Uses `protocol` to queue a new justification request and tries to dispatch all pending /// requests. - pub fn request_justification(&mut self, network_out: &mut dyn NetworkOut, hash: &B::Hash, number: NumberFor) { - let mut context = - ProtocolContext::new(&mut self.context_data, network_out); - self.sync.request_justification(&hash, number, &mut context); - } - - /// Clears all pending justification requests. - pub fn clear_justification_requests(&mut self) { - self.sync.clear_justification_requests() + pub fn request_justification(&mut self, hash: &B::Hash, number: NumberFor) { + self.sync.request_justification(&hash, number) } /// A batch of blocks have been processed, with or without errors. - /// Call this when a batch of blocks have been processed by the import queue, with or without + /// Call this when a batch of blocks have been processed by the importqueue, with or without /// errors. pub fn blocks_processed( &mut self, - network_out: &mut dyn NetworkOut, - processed_blocks: Vec, - has_error: bool + imported: usize, + count: usize, + results: Vec<(Result>, BlockImportError>, B::Hash)> ) { - let mut context = ProtocolContext::new(&mut self.context_data, network_out); - self.sync.blocks_processed(&mut context, processed_blocks, has_error); - } - - /// Restart the sync process. - pub fn restart(&mut self, network_out: &mut dyn NetworkOut) { let peers = self.context_data.peers.clone(); - let mut context = ProtocolContext::new(&mut self.context_data, network_out); - self.sync.restart(&mut context, |peer_id| peers.get(peer_id).map(|i| i.info.clone())); - } - - /// Notify about successful import of the given block. - pub fn block_imported(&mut self, hash: &B::Hash, number: NumberFor) { - self.sync.block_imported(hash, number) - } - - pub fn set_finality_proof_request_builder(&mut self, request_builder: SharedFinalityProofRequestBuilder) { - self.sync.set_finality_proof_request_builder(request_builder) + let results = self.sync.on_blocks_processed( + imported, + count, + results, + |peer_id| peers.get(peer_id).map(|i| i.info.clone()) + ); + for result in results { + match result { + Ok((id, req)) => { + let msg = GenericMessage::BlockRequest(req); + send_message(&mut self.behaviour, &mut self.context_data.peers, id, msg) + } + Err(sync::BadPeer(id, repu)) => { + self.behaviour.disconnect_peer(&id); + self.peerset_handle.report_peer(id, repu) + } + } + } } /// Call this when a justification has been processed by the import queue, with or without /// errors. pub fn justification_import_result(&mut self, hash: B::Hash, number: NumberFor, success: bool) { - self.sync.justification_import_result(hash, number, success) + self.sync.on_justification_import(hash, number, success) } /// Request a finality proof for the given block. /// /// Queues a new finality proof request and tries to dispatch all pending requests. - pub fn request_finality_proof( - &mut self, - network_out: &mut dyn NetworkOut, - hash: &B::Hash, - number: NumberFor - ) { - let mut context = ProtocolContext::new(&mut self.context_data, network_out); - self.sync.request_finality_proof(&hash, number, &mut context); + pub fn request_finality_proof(&mut self, hash: &B::Hash, number: NumberFor) { + self.sync.request_finality_proof(&hash, number) } pub fn finality_proof_import_result( @@ -1234,22 +1244,23 @@ impl, H: ExHashT> Protocol { request_block: (B::Hash, NumberFor), finalization_result: Result<(B::Hash, NumberFor), ()>, ) { - self.sync.finality_proof_import_result(request_block, finalization_result) + self.sync.on_finality_proof_import(request_block, finalization_result) } fn on_remote_call_response( &mut self, - mut network_out: &mut dyn NetworkOut, who: PeerId, response: message::RemoteCallResponse ) { trace!(target: "sync", "Remote call response {} from {}", response.id, who); - self.on_demand_core.on_remote_call_response(&mut network_out, who, response); + self.on_demand_core.on_remote_call_response(OnDemandIn { + behaviour: &mut self.behaviour, + peerset: self.peerset_handle.clone(), + }, who, response); } fn on_remote_read_request( &mut self, - network_out: &mut dyn NetworkOut, who: PeerId, request: message::RemoteReadRequest, ) { @@ -1269,7 +1280,6 @@ impl, H: ExHashT> Protocol { } }; self.send_message( - network_out, who, GenericMessage::RemoteReadResponse(message::RemoteReadResponse { id: request.id, @@ -1280,17 +1290,18 @@ impl, H: ExHashT> Protocol { fn on_remote_read_response( &mut self, - mut network_out: &mut dyn NetworkOut, who: PeerId, response: message::RemoteReadResponse ) { trace!(target: "sync", "Remote read response {} from {}", response.id, who); - self.on_demand_core.on_remote_read_response(&mut network_out, who, response); + self.on_demand_core.on_remote_read_response(OnDemandIn { + behaviour: &mut self.behaviour, + peerset: self.peerset_handle.clone(), + }, who, response); } fn on_remote_header_request( &mut self, - network_out: &mut dyn NetworkOut, who: PeerId, request: message::RemoteHeaderRequest>, ) { @@ -1309,7 +1320,6 @@ impl, H: ExHashT> Protocol { } }; self.send_message( - network_out, who, GenericMessage::RemoteHeaderResponse(message::RemoteHeaderResponse { id: request.id, @@ -1321,17 +1331,18 @@ impl, H: ExHashT> Protocol { fn on_remote_header_response( &mut self, - mut network_out: &mut dyn NetworkOut, who: PeerId, response: message::RemoteHeaderResponse, ) { trace!(target: "sync", "Remote header proof response {} from {}", response.id, who); - self.on_demand_core.on_remote_header_response(&mut network_out, who, response); + self.on_demand_core.on_remote_header_response(OnDemandIn { + behaviour: &mut self.behaviour, + peerset: self.peerset_handle.clone(), + }, who, response); } fn on_remote_changes_request( &mut self, - network_out: &mut dyn NetworkOut, who: PeerId, request: message::RemoteChangesRequest, ) { @@ -1369,7 +1380,6 @@ impl, H: ExHashT> Protocol { } }; self.send_message( - network_out, who, GenericMessage::RemoteChangesResponse(message::RemoteChangesResponse { id: request.id, @@ -1383,7 +1393,6 @@ impl, H: ExHashT> Protocol { fn on_remote_changes_response( &mut self, - mut network_out: &mut dyn NetworkOut, who: PeerId, response: message::RemoteChangesResponse, B::Hash>, ) { @@ -1392,18 +1401,19 @@ impl, H: ExHashT> Protocol { who, response.max ); - self.on_demand_core.on_remote_changes_response(&mut network_out, who, response); + self.on_demand_core.on_remote_changes_response(OnDemandIn { + behaviour: &mut self.behaviour, + peerset: self.peerset_handle.clone(), + }, who, response); } fn on_finality_proof_request( &mut self, - network_out: &mut dyn NetworkOut, who: PeerId, request: message::FinalityProofRequest, - finality_proof_provider: Option<&dyn FinalityProofProvider> ) { trace!(target: "sync", "Finality proof request from {} for {}", who, request.block); - let finality_proof = finality_proof_provider.as_ref() + let finality_proof = self.finality_proof_provider.as_ref() .ok_or_else(|| String::from("Finality provider is not configured")) .and_then(|provider| provider.prove_finality(request.block, &request.request).map_err(|e| e.to_string()) @@ -1420,7 +1430,6 @@ impl, H: ExHashT> Protocol { }, }; self.send_message( - network_out, who, GenericMessage::FinalityProofResponse(message::FinalityProofResponse { id: 0, @@ -1432,31 +1441,31 @@ impl, H: ExHashT> Protocol { fn on_finality_proof_response( &mut self, - network_out: &mut dyn NetworkOut, who: PeerId, response: message::FinalityProofResponse, ) -> CustomMessageOutcome { trace!(target: "sync", "Finality proof response from {} for {}", who, response.block); - let outcome = self.sync.on_block_finality_proof_data( - &mut ProtocolContext::new(&mut self.context_data, network_out), - who, - response, - ); - - if let Some((origin, hash, nb, proof)) = outcome { - CustomMessageOutcome::FinalityProofImport(origin, hash, nb, proof) - } else { - CustomMessageOutcome::None + match self.sync.on_block_finality_proof(who, response) { + Ok(sync::OnBlockFinalityProof::Nothing) => CustomMessageOutcome::None, + Ok(sync::OnBlockFinalityProof::Import { peer, hash, number, proof }) => + CustomMessageOutcome::FinalityProofImport(peer, hash, number, proof), + Err(sync::BadPeer(id, repu)) => { + self.behaviour.disconnect_peer(&id); + self.peerset_handle.report_peer(id, repu); + CustomMessageOutcome::None + } } } fn on_remote_body_response( &mut self, - mut network_out: &mut dyn NetworkOut, peer: PeerId, response: message::BlockResponse ) { - self.on_demand_core.on_remote_body_response(&mut network_out, peer, response); + self.on_demand_core.on_remote_body_response(OnDemandIn { + behaviour: &mut self.behaviour, + peerset: self.peerset_handle.clone(), + }, peer, response); } } @@ -1470,8 +1479,8 @@ pub enum CustomMessageOutcome { } fn send_message( + behaviour: &mut CustomProto, Substream>, peers: &mut HashMap>, - network_out: &mut dyn NetworkOut, who: PeerId, mut message: Message, ) { @@ -1486,5 +1495,142 @@ fn send_message( peer.block_request = Some((time::Instant::now(), r.clone())); } } - network_out.send_message(who, message); + behaviour.send_packet(&who, message); +} + +impl, H: ExHashT> NetworkBehaviour for +Protocol { + type ProtocolsHandler = , Substream> as NetworkBehaviour>::ProtocolsHandler; + type OutEvent = CustomMessageOutcome; + + fn new_handler(&mut self) -> Self::ProtocolsHandler { + self.behaviour.new_handler() + } + + fn addresses_of_peer(&mut self, peer_id: &PeerId) -> Vec { + self.behaviour.addresses_of_peer(peer_id) + } + + fn inject_connected(&mut self, peer_id: PeerId, endpoint: ConnectedPoint) { + self.behaviour.inject_connected(peer_id, endpoint) + } + + fn inject_disconnected(&mut self, peer_id: &PeerId, endpoint: ConnectedPoint) { + self.behaviour.inject_disconnected(peer_id, endpoint) + } + + fn inject_node_event( + &mut self, + peer_id: PeerId, + event: <::Handler as ProtocolsHandler>::OutEvent, + ) { + self.behaviour.inject_node_event(peer_id, event) + } + + fn poll( + &mut self, + params: &mut impl PollParameters, + ) -> Async< + NetworkBehaviourAction< + <::Handler as ProtocolsHandler>::InEvent, + Self::OutEvent + > + > { + while let Ok(Async::Ready(_)) = self.tick_timeout.poll() { + self.tick(); + } + + while let Ok(Async::Ready(_)) = self.propagate_timeout.poll() { + self.propagate_extrinsics(); + } + + for (id, r) in self.sync.block_requests() { + send_message(&mut self.behaviour, &mut self.context_data.peers, id, GenericMessage::BlockRequest(r)) + } + for (id, r) in self.sync.justification_requests() { + send_message(&mut self.behaviour, &mut self.context_data.peers, id, GenericMessage::BlockRequest(r)) + } + for (id, r) in self.sync.finality_proof_requests() { + send_message(&mut self.behaviour, &mut self.context_data.peers, id, GenericMessage::FinalityProofRequest(r)) + } + + let event = match self.behaviour.poll(params) { + Async::NotReady => return Async::NotReady, + Async::Ready(NetworkBehaviourAction::GenerateEvent(ev)) => ev, + Async::Ready(NetworkBehaviourAction::DialAddress { address }) => + return Async::Ready(NetworkBehaviourAction::DialAddress { address }), + Async::Ready(NetworkBehaviourAction::DialPeer { peer_id }) => + return Async::Ready(NetworkBehaviourAction::DialPeer { peer_id }), + Async::Ready(NetworkBehaviourAction::SendEvent { peer_id, event }) => + return Async::Ready(NetworkBehaviourAction::SendEvent { peer_id, event }), + Async::Ready(NetworkBehaviourAction::ReportObservedAddr { address }) => + return Async::Ready(NetworkBehaviourAction::ReportObservedAddr { address }), + }; + + let outcome = match event { + CustomProtoOut::CustomProtocolOpen { peer_id, version, .. } => { + debug_assert!( + version <= CURRENT_VERSION as u8 + && version >= MIN_VERSION as u8 + ); + self.on_peer_connected(peer_id); + CustomMessageOutcome::None + } + CustomProtoOut::CustomProtocolClosed { peer_id, .. } => { + self.on_peer_disconnected(peer_id); + CustomMessageOutcome::None + }, + CustomProtoOut::CustomMessage { peer_id, message } => + self.on_custom_message(peer_id, message), + CustomProtoOut::Clogged { peer_id, messages } => { + debug!(target: "sync", "{} clogging messages:", messages.len()); + for msg in messages.into_iter().take(5) { + debug!(target: "sync", "{:?}", msg); + self.on_clogged_peer(peer_id.clone(), Some(msg)); + } + CustomMessageOutcome::None + } + }; + + if let CustomMessageOutcome::None = outcome { + Async::NotReady + } else { + Async::Ready(NetworkBehaviourAction::GenerateEvent(outcome)) + } + } + + fn inject_replaced(&mut self, peer_id: PeerId, closed_endpoint: ConnectedPoint, new_endpoint: ConnectedPoint) { + self.behaviour.inject_replaced(peer_id, closed_endpoint, new_endpoint) + } + + fn inject_addr_reach_failure( + &mut self, + peer_id: Option<&PeerId>, + addr: &Multiaddr, + error: &dyn std::error::Error + ) { + self.behaviour.inject_addr_reach_failure(peer_id, addr, error) + } + + fn inject_dial_failure(&mut self, peer_id: &PeerId) { + self.behaviour.inject_dial_failure(peer_id) + } + + fn inject_new_listen_addr(&mut self, addr: &Multiaddr) { + self.behaviour.inject_new_listen_addr(addr) + } + + fn inject_expired_listen_addr(&mut self, addr: &Multiaddr) { + self.behaviour.inject_expired_listen_addr(addr) + } + + fn inject_new_external_addr(&mut self, addr: &Multiaddr) { + self.behaviour.inject_new_external_addr(addr) + } +} + +impl, H: ExHashT> DiscoveryNetBehaviour for Protocol { + fn add_discovered_nodes(&mut self, peer_ids: impl Iterator) { + self.behaviour.add_discovered_nodes(peer_ids) + } } diff --git a/core/network/src/protocol/consensus_gossip.rs b/core/network/src/protocol/consensus_gossip.rs index a1c9783b91be5fd84d8086e07d837f367c54cd70..f1343269596aa1c5eb7784be54e4fe08ffc3c000 100644 --- a/core/network/src/protocol/consensus_gossip.rs +++ b/core/network/src/protocol/consensus_gossip.rs @@ -432,13 +432,14 @@ impl ConsensusGossip { } let engine_id = message.engine_id; - //validate the message + // validate the message let validation = self.validators.get(&engine_id) .cloned() .map(|v| { let mut context = NetworkContext { gossip: self, protocol, engine_id }; v.validate(&mut context, &who, &message.data) }); + let validation_result = match validation { Some(ValidationResult::ProcessAndKeep(topic)) => Some((topic, true)), Some(ValidationResult::ProcessAndDiscard(topic)) => Some((topic, false)), diff --git a/core/network/src/protocol/event.rs b/core/network/src/protocol/event.rs new file mode 100644 index 0000000000000000000000000000000000000000..2edbb0fbf7563120dd2378f2c4ccb5eab97c115a --- /dev/null +++ b/core/network/src/protocol/event.rs @@ -0,0 +1,41 @@ +// 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 . + +//! Network event types. These are are not the part of the protocol, but rather +//! events that happen on the network like DHT get/put results received. + +use libp2p::multihash::Multihash; + +/// Events generated by DHT as a response to get_value and put_value requests. +pub enum DhtEvent { + /// The value was found. + ValueFound(Vec<(Multihash, Vec)>), + + /// The requested record has not been found in the DHT. + ValueNotFound(Multihash), + + /// The record has been successfully inserted into the DHT. + ValuePut(Multihash), + + /// An error has occured while putting a record into the DHT. + ValuePutFailed(Multihash), +} + +/// Type for events generated by networking layer. +pub enum Event { + /// Event generated by a DHT. + Dht(DhtEvent), +} diff --git a/core/network/src/protocol/specialization.rs b/core/network/src/protocol/specialization.rs index 41b10bf7079c79c018d918156894aa24f2b80f85..7f6b7dc44f716a2fecb18b84dbeacf6f1cd32093 100644 --- a/core/network/src/protocol/specialization.rs +++ b/core/network/src/protocol/specialization.rs @@ -16,6 +16,8 @@ //! Specializations of the substrate network protocol to allow more complex forms of communication. +pub use crate::protocol::event::{DhtEvent, Event}; + use crate::protocol::Context; use libp2p::PeerId; use runtime_primitives::traits::Block as BlockT; @@ -36,9 +38,12 @@ pub trait NetworkSpecialization: Send + Sync + 'static { &mut self, ctx: &mut dyn Context, who: PeerId, - message: &mut Option> + message: Vec ); + /// Called when a network-specific event arrives. + fn on_event(&mut self, event: Event); + /// Called on abort. #[deprecated(note = "This method is never called; aborting corresponds to dropping the object")] fn on_abort(&mut self) { } @@ -125,11 +130,18 @@ macro_rules! construct_simple_protocol { &mut self, _ctx: &mut $crate::Context<$block>, _who: $crate::PeerId, - _message: &mut Option<$crate::message::Message<$block>> + _message: Vec, ) { $( self.$sub_protocol_name.on_message(_ctx, _who, _message); )* } + fn on_event( + &mut self, + _event: $crate::specialization::Event + ) { + $( self.$sub_protocol_name.on_event(_event); )* + } + fn on_abort(&mut self) { $( self.$sub_protocol_name.on_abort(); )* } diff --git a/core/network/src/protocol/sync.rs b/core/network/src/protocol/sync.rs index 04780862228da032f0f57075bf631390c52d2d6b..e4e1c3559944dbca876e8ab10a6dae739000db87 100644 --- a/core/network/src/protocol/sync.rs +++ b/core/network/src/protocol/sync.rs @@ -1,16 +1,16 @@ // 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 . @@ -24,154 +24,161 @@ //! //! The `ChainSync` struct maintains the state of the block requests. Whenever something happens on //! the network, or whenever a block has been successfully verified, call the appropriate method in -//! order to update it. You must also regularly call `tick()`. -//! -//! To each of these methods, you must pass a `Context` object that the `ChainSync` will use to -//! send its new outgoing requests. +//! order to update it. //! -use std::cmp::max; -use std::ops::Range; -use std::collections::{HashMap, VecDeque}; -use log::{debug, trace, warn, info, error}; -use crate::protocol::PeerInfo as ProtocolPeerInfo; -use libp2p::PeerId; -use client::{BlockStatus, ClientInfo}; -use consensus::{BlockOrigin, import_queue::{IncomingBlock, SharedFinalityProofRequestBuilder}}; -use client::error::Error as ClientError; use blocks::BlockCollection; +use client::{BlockStatus, ClientInfo, error::Error as ClientError}; +use consensus::{BlockOrigin, import_queue::{IncomingBlock, BlockImportResult, BlockImportError}}; +use crate::{ + config::{Roles, BoxFinalityProofRequestBuilder}, + message::{self, generic::FinalityProofRequest, BlockAttributes, BlockRequest, BlockResponse, FinalityProofResponse}, + protocol +}; +use either::Either; use extra_requests::ExtraRequests; -use runtime_primitives::traits::{ - Block as BlockT, Header as HeaderT, NumberFor, Zero, One, - CheckedSub, SaturatedConversion +use libp2p::PeerId; +use log::{debug, trace, warn, info, error}; +use runtime_primitives::{ + Justification, + generic::BlockId, + traits::{Block as BlockT, Header, NumberFor, Zero, One, CheckedSub, SaturatedConversion} }; -use runtime_primitives::{Justification, generic::BlockId}; -use crate::message; -use crate::config::Roles; -use std::collections::HashSet; +use std::{fmt, ops::Range, collections::{HashMap, HashSet, VecDeque}, sync::Arc}; mod blocks; mod extra_requests; /// Maximum blocks to request in a single packet. const MAX_BLOCKS_TO_REQUEST: usize = 128; + /// Maximum blocks to store in the import queue. const MAX_IMPORTING_BLOCKS: usize = 2048; -/// We use a heuristic that with a high likelihood, by the time `MAJOR_SYNC_BLOCKS` have been -/// imported we'll be on the same chain as (or at least closer to) the peer so we want to delay the -/// ancestor search to not waste time doing that when we're so far behind. -const MAJOR_SYNC_BLOCKS: usize = 5; + +/// We use a heuristic that with a high likelihood, by the time +/// `MAJOR_SYNC_BLOCKS` have been imported we'll be on the same +/// chain as (or at least closer to) the peer so we want to delay +/// the ancestor search to not waste time doing that when we are +/// so far behind. +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. + +/// 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); -/// Reputation change when a peer failed to answer our legitimate ancestry block search. + +/// Reputation change when a peer failed to answer our legitimate ancestry +/// block search. const ANCESTRY_BLOCK_ERROR_REPUTATION_CHANGE: i32 = -(1 << 9); -/// Reputation change when a peer sent us a status message with a different genesis than us. + +/// Reputation change when a peer sent us a status message with a different +/// genesis than us. const GENESIS_MISMATCH_REPUTATION_CHANGE: i32 = i32::min_value() + 1; -/// Context for a network-specific handler. -pub trait Context { - /// Get a reference to the client. - fn client(&self) -> &dyn crate::chain::Client; +/// Reputation change for peers which send us a block with an incomplete header. +const INCOMPLETE_HEADER_REPUTATION_CHANGE: i32 = -(1 << 20); - /// Adjusts the reputation of the peer. Use this to point out that a peer has been malign or - /// irresponsible or appeared lazy. - fn report_peer(&mut self, who: PeerId, reputation: i32); +/// Reputation change for peers which send us a block which we fail to verify. +const VERIFICATION_FAIL_REPUTATION_CHANGE: i32 = -(1 << 20); - /// Force disconnecting from a peer. Use this when a peer misbehaved. - fn disconnect_peer(&mut self, who: PeerId); +/// Reputation change for peers which send us a bad block. +const BAD_BLOCK_REPUTATION_CHANGE: i32 = -(1 << 29); - /// Request a finality proof from a peer. - fn send_finality_proof_request(&mut self, who: PeerId, request: message::FinalityProofRequest); +/// Reputation change for peers which send us a block with bad justifications. +const BAD_JUSTIFICATION_REPUTATION_CHANGE: i32 = -(1 << 16); - /// Request a block from a peer. - fn send_block_request(&mut self, who: PeerId, request: message::BlockRequest); +/// The main data structure which contains all the state for a chains +/// active syncing strategy. +pub struct ChainSync { + /// Chain client. + client: Arc>, + /// The active peers that we are using to sync and their PeerSync status + peers: HashMap>, + /// A `BlockCollection` of blocks that are being downloaded from peers + blocks: BlockCollection, + /// The best block number in our queue of blocks to import + best_queued_number: NumberFor, + /// The best block hash in our queue of blocks to import + best_queued_hash: B::Hash, + /// The role of this node, e.g. light or full + role: Roles, + /// What block attributes we require for this node, usually derived from + /// what role we are, but could be customized + required_block_attributes: message::BlockAttributes, + /// Any extra finality proof requests. + extra_finality_proofs: ExtraRequests, + /// Any extra justification requests. + extra_justifications: ExtraRequests, + /// A set of hashes of blocks that are being downloaded or have been + /// downloaded and are queued for import. + queue_blocks: HashSet, + /// The best block number that we are currently importing. + best_importing_number: NumberFor, + request_builder: Option> } -#[derive(Debug, Clone)] /// All the data we have about a Peer that we are trying to sync with -pub(crate) struct PeerSync { - /// The common number is the block number that is a common point of ancestry for both our chains - /// (as far as we know) +#[derive(Debug, Clone)] +pub struct PeerSync { + /// The common number is the block number that is a common point of + /// ancestry for both our chains (as far as we know). pub common_number: NumberFor, - /// The hash of the best block that we've seen for this peer + /// The hash of the best block that we've seen for this peer. pub best_hash: B::Hash, - /// The number of the best block that we've seen for this peer + /// The number of the best block that we've seen for this peer. pub best_number: NumberFor, - /// The state of syncing this peer is in for us, generally categories into `Available` or "busy" - /// with something as defined by `PeerSyncState`. + /// The state of syncing this peer is in for us, generally categories + /// into `Available` or "busy" with something as defined by `PeerSyncState`. pub state: PeerSyncState, - /// A queue of blocks that this peer has announced to us, should only contain - /// `ANNOUNCE_HISTORY_SIZE` entries. - pub recently_announced: VecDeque, + /// A queue of blocks that this peer has announced to us, should only + /// contain `ANNOUNCE_HISTORY_SIZE` entries. + pub recently_announced: VecDeque } /// The sync status of a peer we are trying to sync with #[derive(Debug)] -pub(crate) struct PeerInfo { +pub struct PeerInfo { /// Their best block hash. pub best_hash: B::Hash, /// Their best block number. - pub best_number: NumberFor, -} - -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -/// The ancestor search state expresses which algorithm, and its stateful parameters, we are using to -/// try to find an ancestor block -pub(crate) enum AncestorSearchState { - /// Use exponential backoff to find an ancestor, then switch to binary search. - /// We keep track of the exponent. - ExponentialBackoff(NumberFor), - /// Using binary search to find the best ancestor. - /// We keep track of left and right bounds. - BinarySearch(NumberFor, NumberFor), + pub best_number: NumberFor } +/// The state of syncing between a Peer and ourselves. +/// +/// Generally two categories, "busy" or `Available`. If busy, the enum +/// defines what we are busy with. #[derive(Copy, Clone, Eq, PartialEq, Debug)] -/// The state of syncing between a Peer and ourselves. Generally two categories, "busy" or -/// `Available`. If busy, the Enum defines what we are busy with. -pub(crate) enum PeerSyncState { - /// Searching for ancestors the Peer has in common with us. - AncestorSearch(NumberFor, AncestorSearchState), +pub enum PeerSyncState { /// Available for sync requests. Available, + /// Searching for ancestors the Peer has in common with us. + AncestorSearch(NumberFor, AncestorSearchState), /// Actively downloading new blocks, starting from the given Number. DownloadingNew(NumberFor), - /// Downloading a stale block with given Hash. Stale means that it's a block with a number that - /// is lower than our best number. It might be from a fork and not necessarily already imported. + /// Downloading a stale block with given Hash. Stale means that it is a + /// block with a number that is lower than our best number. It might be + /// from a fork and not necessarily already imported. DownloadingStale(B::Hash), /// Downloading justification for given block hash. DownloadingJustification(B::Hash), /// Downloading finality proof for given block hash. - DownloadingFinalityProof(B::Hash), + DownloadingFinalityProof(B::Hash) } -/// The main data structure to contain all the state for a chains active syncing strategy. -pub struct ChainSync { - /// The active peers that we are using to sync and their PeerSync status - peers: HashMap>, - /// A `BlockCollection` of blocks that are being downloaded from peers - blocks: BlockCollection, - /// The best block number in our queue of blocks to import - best_queued_number: NumberFor, - /// The best block hash in our queue of blocks to import - best_queued_hash: B::Hash, - /// The role of this node, e.g. light or full - role: Roles, - /// What block attributes we require for this node, usually derived from what role we are, but - /// could be customized - required_block_attributes: message::BlockAttributes, - extra_finality_proofs: ExtraRequests, - extra_justifications: ExtraRequests, - /// A set of hashes of blocks that are being downloaded or have been downloaded and are queued - /// for import. - queue_blocks: HashSet, - /// The best block number that we are currently importing - best_importing_number: NumberFor, - request_builder: Option>, +impl PeerSyncState { + pub fn is_available(&self) -> bool { + if let PeerSyncState::Available = self { + true + } else { + false + } + } } /// Reported sync state. @@ -183,7 +190,7 @@ pub enum SyncState { Downloading } -/// Syncing status and statistics +/// Syncing status and statistics. #[derive(Clone)] pub struct Status { /// Current global sync state. @@ -191,20 +198,85 @@ pub struct Status { /// Target sync block number. pub best_seen_block: Option>, /// Number of peers participating in syncing. - pub num_peers: u32, + pub num_peers: u32 +} + +/// A peer did not behave as expected and should be reported. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BadPeer(pub PeerId, pub i32); + +impl fmt::Display for BadPeer { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "bad peer {}; reputation change: {}", self.0, self.1) + } +} + +impl std::error::Error for BadPeer {} + +/// Result of [`ChainSync::on_block_data`]. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum OnBlockData { + /// The block should be imported. + Import(BlockOrigin, Vec>), + /// A new block request needs to be made to the given peer. + Request(PeerId, BlockRequest) +} + +/// Result of [`ChainSync::on_block_announce`]. +#[derive(Debug, Clone, PartialEq, Eq)] +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`]. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum OnBlockJustification { + /// The justification needs no further handling. + Nothing, + /// The justification should be imported. + Import { + peer: PeerId, + hash: B::Hash, + number: NumberFor, + justification: Justification + } +} + +/// Result of [`ChainSync::on_block_finality_proof`]. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum OnBlockFinalityProof { + /// The proof needs no further handling. + Nothing, + /// The proof should be imported. + Import { + peer: PeerId, + hash: B::Hash, + number: NumberFor, + proof: Vec + } } impl ChainSync { - /// Create a new instance. Pass the initial known state of the chain. - pub(crate) fn new(role: Roles, info: &ClientInfo) -> Self { - let mut required_block_attributes = - message::BlockAttributes::HEADER | message::BlockAttributes::JUSTIFICATION; + /// Create a new instance. + pub fn new( + role: Roles, + client: Arc>, + info: &ClientInfo, + request_builder: Option> + ) -> Self { + let mut required_block_attributes = BlockAttributes::HEADER | BlockAttributes::JUSTIFICATION; if role.is_full() { - required_block_attributes |= message::BlockAttributes::BODY; + required_block_attributes |= BlockAttributes::BODY } ChainSync { + client, peers: HashMap::new(), blocks: BlockCollection::new(), best_queued_hash: info.chain.best_hash, @@ -215,130 +287,118 @@ impl ChainSync { required_block_attributes, queue_blocks: Default::default(), best_importing_number: Zero::zero(), - request_builder: None, + request_builder } } - /// Returns the number for the best seen blocks among connected peers, if any - fn best_seen_block(&self) -> Option> { - self.peers.values().max_by_key(|p| p.best_number).map(|p| p.best_number) - } - - /// Returns the SyncState that we are currently in based on a provided `best_seen` block number. - /// A chain is classified as downloading if the provided best block is more than `MAJOR_SYNC_BLOCKS` - /// behind the best queued block. - fn state(&self, best_seen: &Option>) -> SyncState { - match best_seen { - &Some(n) if n > self.best_queued_number && n - self.best_queued_number > 5.into() => SyncState::Downloading, - _ => SyncState::Idle, - } + /// Returns the state of the sync of the given peer. + /// + /// Returns `None` if the peer is unknown. + pub fn peer_info(&self, who: &PeerId) -> Option> { + self.peers.get(who).map(|p| PeerInfo { best_hash: p.best_hash, best_number: p.best_number }) } - /// Returns the state of the sync of the given peer. Returns `None` if the peer is unknown. - pub(crate) fn peer_info(&self, who: &PeerId) -> Option> { - self.peers.get(who).map(|peer| { - PeerInfo { - best_hash: peer.best_hash, - best_number: peer.best_number, - } - }) - } + /// Returns the current sync status. + pub fn status(&self) -> Status { + let best_seen = self.peers.values().max_by_key(|p| p.best_number).map(|p| p.best_number); + let sync_state = + if let Some(n) = best_seen { + // A chain is classified as downloading if the provided best block is + // more than `MAJOR_SYNC_BLOCKS` behind the best queued block. + if n > self.best_queued_number && n - self.best_queued_number > MAJOR_SYNC_BLOCKS.into() { + SyncState::Downloading + } else { + SyncState::Idle + } + } else { + SyncState::Idle + }; - /// Returns sync status. - pub(crate) fn status(&self) -> Status { - let best_seen = self.best_seen_block(); - let state = self.state(&best_seen); Status { - state, + state: sync_state, best_seen_block: best_seen, - num_peers: self.peers.len() as u32, + num_peers: self.peers.len() as u32 } } - /// Handle new connected peer. Call this method whenever we connect to a new peer. - pub(crate) fn new_peer( - &mut self, - protocol: &mut dyn Context, - who: PeerId, - info: ProtocolPeerInfo - ) { - // there's nothing sync can get from the node that has no blockchain data - // (the opposite is not true, but all requests are served at protocol level) + /// Handle a new connected peer. + /// + /// Call this method whenever we connect to a new peer. + pub fn new_peer(&mut self, who: PeerId, info: protocol::PeerInfo) -> Result>, BadPeer> { + // There is nothing sync can get from the node that has no blockchain data. if !info.roles.is_full() { - return; + return Ok(None) } - - let status = block_status(&*protocol.client(), &self.queue_blocks, info.best_hash); - match (status, info.best_number) { - (Err(e), _) => { + match self.block_status(&info.best_hash) { + Err(e) => { debug!(target:"sync", "Error reading blockchain: {:?}", e); - protocol.report_peer(who.clone(), BLOCKCHAIN_STATUS_READ_ERROR_REPUTATION_CHANGE); - protocol.disconnect_peer(who); - }, - (Ok(BlockStatus::KnownBad), _) => { + Err(BadPeer(who, BLOCKCHAIN_STATUS_READ_ERROR_REPUTATION_CHANGE)) + } + Ok(BlockStatus::KnownBad) => { info!("New peer with known bad best block {} ({}).", info.best_hash, info.best_number); - protocol.report_peer(who.clone(), i32::min_value()); - protocol.disconnect_peer(who); - }, - (Ok(BlockStatus::Unknown), b) if b.is_zero() => { - info!("New peer with unknown genesis hash {} ({}).", info.best_hash, info.best_number); - protocol.report_peer(who.clone(), i32::min_value()); - protocol.disconnect_peer(who); - }, - (Ok(BlockStatus::Unknown), _) if self.queue_blocks.len() > MAJOR_SYNC_BLOCKS => { + Err(BadPeer(who, i32::min_value())) + } + Ok(BlockStatus::Unknown) => { + if info.best_number.is_zero() { + info!("New peer with unknown genesis hash {} ({}).", info.best_hash, info.best_number); + return Err(BadPeer(who, i32::min_value())) + } + // If there are more than `MAJOR_SYNC_BLOCKS` in the import queue then we have // enough to do in the import queue that it's not worth kicking off // an ancestor search, which is what we do in the next match case below. - debug!( - target:"sync", - "New peer with unknown best hash {} ({}), assuming common block.", - self.best_queued_hash, - self.best_queued_number - ); - self.peers.insert(who, PeerSync { - common_number: self.best_queued_number, - best_hash: info.best_hash, - best_number: info.best_number, - state: PeerSyncState::Available, - recently_announced: Default::default(), - }); - } - (Ok(BlockStatus::Unknown), _) => { - let our_best = self.best_queued_number; - if our_best.is_zero() { - // We are at genesis, just start downloading - debug!(target:"sync", "New peer with best hash {} ({}).", info.best_hash, info.best_number); - self.peers.insert(who.clone(), PeerSync { - common_number: Zero::zero(), + if self.queue_blocks.len() > MAJOR_SYNC_BLOCKS.into() { + debug!( + target:"sync", + "New peer with unknown best hash {} ({}), assuming common block.", + self.best_queued_hash, + self.best_queued_number + ); + self.peers.insert(who, PeerSync { + common_number: self.best_queued_number, best_hash: info.best_hash, best_number: info.best_number, state: PeerSyncState::Available, - recently_announced: Default::default(), + recently_announced: Default::default() }); - self.download_new(protocol, who) - } else { - let common_best = ::std::cmp::min(our_best, info.best_number); - debug!(target:"sync", - "New peer with unknown best hash {} ({}), searching for common ancestor.", - info.best_hash, - info.best_number - ); + return Ok(None) + } + + // If we are at genesis, just start downloading. + if self.best_queued_number.is_zero() { + debug!(target:"sync", "New peer with best hash {} ({}).", info.best_hash, info.best_number); self.peers.insert(who.clone(), PeerSync { common_number: Zero::zero(), best_hash: info.best_hash, best_number: info.best_number, - state: PeerSyncState::AncestorSearch( - common_best, - AncestorSearchState::ExponentialBackoff(One::one()) - ), + state: PeerSyncState::Available, recently_announced: Default::default(), }); - Self::request_ancestry(protocol, who, common_best) + return Ok(self.select_new_blocks(who).map(|(_, req)| req)) } - }, - (Ok(BlockStatus::Queued), _) | - (Ok(BlockStatus::InChainWithState), _) | - (Ok(BlockStatus::InChainPruned), _) => { + + let common_best = std::cmp::min(self.best_queued_number, info.best_number); + + debug!(target:"sync", + "New peer with unknown best hash {} ({}), searching for common ancestor.", + info.best_hash, + info.best_number + ); + + self.peers.insert(who, PeerSync { + common_number: Zero::zero(), + best_hash: info.best_hash, + best_number: info.best_number, + state: PeerSyncState::AncestorSearch( + common_best, + AncestorSearchState::ExponentialBackoff(One::one()) + ), + recently_announced: Default::default() + }); + + Ok(Some(ancestry_request::(common_best))) + } + Ok(BlockStatus::Queued) | Ok(BlockStatus::InChainWithState) | Ok(BlockStatus::InChainPruned) => { debug!(target:"sync", "New peer with known best hash {} ({}).", info.best_hash, info.best_number); self.peers.insert(who.clone(), PeerSync { common_number: info.best_number, @@ -347,395 +407,421 @@ impl ChainSync { state: PeerSyncState::Available, recently_announced: Default::default(), }); + Ok(None) } } } - /// This function handles the ancestor search strategy used. The goal is to find a common point - /// that both our chains agree on that is as close to the tip as possible. - /// The way this works is we first have an exponential backoff strategy, where we try to step - /// forward until we find a block hash mismatch. The size of the step doubles each step we take. - /// - /// When we've found a block hash mismatch we then fall back to a binary search between the two - /// last known points to find the common block closest to the tip. - fn handle_ancestor_search_state( - state: AncestorSearchState, - curr_block_num: NumberFor, - block_hash_match: bool, - ) -> Option<(AncestorSearchState, NumberFor)> { - let two = >::one() + >::one(); - match state { - AncestorSearchState::ExponentialBackoff(next_distance_to_tip) => { - if block_hash_match && next_distance_to_tip == One::one() { - // We found the ancestor in the first step so there is no need to execute binary search. - return None; - } - if block_hash_match { - let left = curr_block_num; - let right = left + next_distance_to_tip / two; - let middle = left + (right - left) / two; - Some((AncestorSearchState::BinarySearch(left, right), middle)) - } else { - let next_block_num = curr_block_num.checked_sub(&next_distance_to_tip) - .unwrap_or_else(Zero::zero); - let next_distance_to_tip = next_distance_to_tip * two; - Some((AncestorSearchState::ExponentialBackoff(next_distance_to_tip), next_block_num)) - } - }, - AncestorSearchState::BinarySearch(mut left, mut right) => { - if left >= curr_block_num { - return None; - } - if block_hash_match { - left = curr_block_num; - } else { - right = curr_block_num; - } - assert!(right >= left); - let middle = left + (right - left) / two; - Some((AncestorSearchState::BinarySearch(left, right), middle)) - }, + /// Signal that `best_header` has been queued for import and update the + /// `ChainSync` state with that information. + pub fn update_chain_info(&mut self, best_header: &B::Header) { + self.on_block_queued(&best_header.hash(), *best_header.number()) + } + + /// Schedule a justification request for the given block. + pub fn request_justification(&mut self, hash: &B::Hash, number: NumberFor) { + let client = &self.client; + self.extra_justifications.schedule((*hash, number), |base, block| { + client.is_descendent_of(base, block) + }) + } + + /// Schedule a finality proof request for the given block. + pub fn request_finality_proof(&mut self, hash: &B::Hash, number: NumberFor) { + let client = &self.client; + self.extra_finality_proofs.schedule((*hash, number), |base, block| { + client.is_descendent_of(base, block) + }) + } + + /// Get an iterator over all scheduled justification requests. + pub fn justification_requests(&mut self) -> impl Iterator)> + '_ { + let peers = &mut self.peers; + let mut matcher = self.extra_justifications.matcher(); + std::iter::from_fn(move || { + if let Some((peer, request)) = matcher.next(&peers) { + peers.get_mut(&peer) + .expect("`Matcher::next` guarantees the `PeerId` comes from the given peers; qed") + .state = PeerSyncState::DownloadingJustification(request.0); + let req = message::generic::BlockRequest { + id: 0, + fields: BlockAttributes::JUSTIFICATION, + from: message::FromBlock::Hash(request.0), + to: None, + direction: message::Direction::Ascending, + max: Some(1) + }; + Some((peer, req)) + } else { + None + } + }) + } + + /// Get an iterator over all scheduled finality proof requests. + pub fn finality_proof_requests(&mut self) -> impl Iterator)> + '_ { + let peers = &mut self.peers; + let request_builder = &mut self.request_builder; + let mut matcher = self.extra_finality_proofs.matcher(); + std::iter::from_fn(move || { + if let Some((peer, request)) = matcher.next(&peers) { + peers.get_mut(&peer) + .expect("`Matcher::next` guarantees the `PeerId` comes from the given peers; qed") + .state = PeerSyncState::DownloadingFinalityProof(request.0); + let req = message::generic::FinalityProofRequest { + id: 0, + block: request.0, + request: request_builder.as_mut() + .map(|builder| builder.build_request_data(&request.0)) + .unwrap_or_default() + }; + Some((peer, req)) + } else { + None + } + }) + } + + /// Get an iterator over all block requests of all peers. + pub fn block_requests(&mut self) -> impl Iterator)> + '_ { + if self.queue_blocks.len() > MAX_IMPORTING_BLOCKS { + trace!(target: "sync", "Too many blocks in the queue."); + return Either::Left(std::iter::empty()) } + let blocks = &mut self.blocks; + let attrs = &self.required_block_attributes; + 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((range, req)) = peer_block_request(id, peer, blocks, attrs) { + peer.state = PeerSyncState::DownloadingNew(range.start); + trace!(target: "sync", "new block request for {}", id); + Some((id.clone(), req)) + } else { + trace!(target: "sync", "no new block request for {}", id); + None + } + }); + Either::Right(iter) } /// Handle a response from the remote to a block request that we made. /// /// `request` must be the original request that triggered `response`. /// - /// If this corresponds to a valid block, this outputs the block that must be imported in the - /// import queue. - #[must_use] - pub(crate) fn on_block_data( - &mut self, - protocol: &mut dyn Context, - who: PeerId, - request: message::BlockRequest, - response: message::BlockResponse - ) -> Option<(BlockOrigin, Vec>)> { - let new_blocks: Vec> = if let Some(ref mut peer) = self.peers.get_mut(&who) { - let mut blocks = response.blocks; - if request.direction == message::Direction::Descending { - trace!(target: "sync", "Reversing incoming block list"); - blocks.reverse(); - } - let peer_state = peer.state.clone(); - match peer_state { - PeerSyncState::DownloadingNew(start_block) => { - self.blocks.clear_peer_download(&who); - peer.state = PeerSyncState::Available; - self.blocks.insert(start_block, blocks, who); - self.blocks - .drain(self.best_queued_number + One::one()) - .into_iter() - .map(|block_data| { + /// If this corresponds to a valid block, this outputs the block that + /// must be imported in the import queue. + pub fn on_block_data + (&mut self, who: PeerId, request: BlockRequest, response: BlockResponse) -> Result, BadPeer> + { + let new_blocks: Vec> = + if let Some(peer) = self.peers.get_mut(&who) { + let mut blocks = response.blocks; + if request.direction == message::Direction::Descending { + trace!(target: "sync", "Reversing incoming block list"); + blocks.reverse() + } + match &mut peer.state { + PeerSyncState::DownloadingNew(start_block) => { + self.blocks.clear_peer_download(&who); + self.blocks.insert(*start_block, blocks, who); + peer.state = PeerSyncState::Available; + self.blocks + .drain(self.best_queued_number + One::one()) + .into_iter() + .map(|block_data| { + IncomingBlock { + hash: block_data.block.hash, + header: block_data.block.header, + body: block_data.block.body, + justification: block_data.block.justification, + origin: block_data.origin, + } + }).collect() + } + PeerSyncState::DownloadingStale(_) => { + peer.state = PeerSyncState::Available; + blocks.into_iter().map(|b| { IncomingBlock { - hash: block_data.block.hash, - header: block_data.block.header, - body: block_data.block.body, - justification: block_data.block.justification, - origin: block_data.origin, + hash: b.hash, + header: b.header, + body: b.body, + justification: b.justification, + origin: Some(who.clone()), } }).collect() - }, - PeerSyncState::DownloadingStale(_) => { - peer.state = PeerSyncState::Available; - blocks.into_iter().map(|b| { - IncomingBlock { - hash: b.hash, - header: b.header, - body: b.body, - justification: b.justification, - origin: Some(who.clone()), - } - }).collect() - }, - PeerSyncState::AncestorSearch(num, state) => { - let block_hash_match = match (blocks.get(0), protocol.client().block_hash(num)) { - (Some(ref block), Ok(maybe_our_block_hash)) => { - trace!(target: "sync", "Got ancestry block #{} ({}) from peer {}", num, block.hash, who); - maybe_our_block_hash.map_or(false, |x| x == block.hash) - }, - (None, _) => { - debug!(target: "sync", "Invalid response when searching for ancestor from {}", who); - protocol.report_peer(who.clone(), i32::min_value()); - protocol.disconnect_peer(who); - return None - }, - (_, Err(e)) => { - info!("Error answering legitimate blockchain query: {:?}", e); - protocol.report_peer(who.clone(), ANCESTRY_BLOCK_ERROR_REPUTATION_CHANGE); - protocol.disconnect_peer(who); - return None - }, - }; - if block_hash_match && peer.common_number < num { - peer.common_number = num; - } - if !block_hash_match && num.is_zero() { - trace!(target:"sync", "Ancestry search: genesis mismatch for peer {}", who); - protocol.report_peer(who.clone(), GENESIS_MISMATCH_REPUTATION_CHANGE); - protocol.disconnect_peer(who); - return None } - if let Some((next_state, next_block_num)) = - Self::handle_ancestor_search_state(state, num, block_hash_match) { - peer.state = PeerSyncState::AncestorSearch(next_block_num, next_state); - Self::request_ancestry(protocol, who, next_block_num); - return None - } else { - peer.state = PeerSyncState::Available; - vec![] + PeerSyncState::AncestorSearch(num, state) => { + let block_hash_match = match (blocks.get(0), self.client.block_hash(*num)) { + (Some(block), Ok(maybe_our_block_hash)) => { + trace!(target: "sync", "Got ancestry block #{} ({}) from peer {}", num, block.hash, who); + maybe_our_block_hash.map_or(false, |x| x == block.hash) + }, + (None, _) => { + debug!(target: "sync", "Invalid response when searching for ancestor from {}", who); + return Err(BadPeer(who, i32::min_value())) + }, + (_, Err(e)) => { + info!("Error answering legitimate blockchain query: {:?}", e); + return Err(BadPeer(who, ANCESTRY_BLOCK_ERROR_REPUTATION_CHANGE)) + } + }; + if block_hash_match && peer.common_number < *num { + peer.common_number = *num; + } + if !block_hash_match && num.is_zero() { + trace!(target:"sync", "Ancestry search: genesis mismatch for peer {}", who); + return Err(BadPeer(who, GENESIS_MISMATCH_REPUTATION_CHANGE)) + } + if let Some((next_state, next_num)) = handle_ancestor_search_state(state, *num, block_hash_match) { + peer.state = PeerSyncState::AncestorSearch(next_num, next_state); + return Ok(OnBlockData::Request(who, ancestry_request::(next_num))) + } else { + peer.state = PeerSyncState::Available; + Vec::new() + } } - }, - PeerSyncState::Available | - PeerSyncState::DownloadingJustification(..) | - PeerSyncState::DownloadingFinalityProof(..) => Vec::new(), - } - } else { - Vec::new() - }; - let is_recent = new_blocks - .first() - .map(|block| self.peers.iter().any(|(_, peer)| peer.recently_announced.contains(&block.hash))) + | PeerSyncState::Available + | PeerSyncState::DownloadingJustification(..) + | PeerSyncState::DownloadingFinalityProof(..) => Vec::new() + } + } else { + Vec::new() + }; + + let is_recent = new_blocks.first() + .map(|block| { + self.peers.iter().any(|(_, peer)| peer.recently_announced.contains(&block.hash)) + }) .unwrap_or(false); - let origin = if is_recent { BlockOrigin::NetworkBroadcast } else { BlockOrigin::NetworkInitialSync }; - if let Some((hash, number)) = new_blocks.last() - .and_then(|b| b.header.as_ref().map(|h| (b.hash.clone(), *h.number()))) - { - trace!(target:"sync", "Accepted {} blocks ({:?}) with origin {:?}", new_blocks.len(), hash, origin); - self.block_queued(&hash, number); + let origin = + if is_recent { + BlockOrigin::NetworkBroadcast + } else { + BlockOrigin::NetworkInitialSync + }; + + if let Some((h, n)) = new_blocks.last().and_then(|b| b.header.as_ref().map(|h| (&b.hash, *h.number()))) { + trace!(target:"sync", "Accepted {} blocks ({:?}) with origin {:?}", new_blocks.len(), h, origin); + self.on_block_queued(h, n) } - self.maintain_sync(protocol); - let new_best_importing_number = new_blocks - .last() - .and_then(|b| b.header.as_ref().map(|h| h.number().clone())) + + let new_best_importing_number = new_blocks.last() + .and_then(|b| b.header.as_ref().map(|h| *h.number())) .unwrap_or_else(|| Zero::zero()); - self.queue_blocks - .extend(new_blocks.iter().map(|b| b.hash.clone())); - self.best_importing_number = max(new_best_importing_number, self.best_importing_number); - Some((origin, new_blocks)) + + self.queue_blocks.extend(new_blocks.iter().map(|b| b.hash)); + + self.best_importing_number = std::cmp::max(new_best_importing_number, self.best_importing_number); + + Ok(OnBlockData::Import(origin, new_blocks)) } /// Handle a response from the remote to a justification request that we made. /// /// `request` must be the original request that triggered `response`. /// - /// Returns `Some` if this produces a justification that must be imported into the import - /// queue. - #[must_use] - pub(crate) fn on_block_justification_data( - &mut self, - protocol: &mut dyn Context, - who: PeerId, - response: message::BlockResponse, - ) -> Option<(PeerId, B::Hash, NumberFor, Justification)> + /// Returns `Some` if this produces a justification that must be imported + /// into the import queue. + pub fn on_block_justification + (&mut self, who: PeerId, response: BlockResponse) -> Result, BadPeer> { - let peer = if let Some(peer) = self.peers.get_mut(&who) { - peer - } else { - error!(target: "sync", "Called on_block_justification_data with a bad peer ID"); - return None; - }; + let peer = + if let Some(peer) = self.peers.get_mut(&who) { + peer + } else { + error!(target: "sync", "Called on_block_justification with a bad peer ID"); + return Ok(OnBlockJustification::Nothing) + }; if let PeerSyncState::DownloadingJustification(hash) = peer.state { peer.state = PeerSyncState::Available; - // we only request one justification at a time - match response.blocks.into_iter().next() { - Some(response) => { - if hash != response.hash { - info!("Invalid block justification provided by {}: requested: {:?} got: {:?}", - who, hash, response.hash); - protocol.report_peer(who.clone(), i32::min_value()); - protocol.disconnect_peer(who); - return None; - } - return self.extra_justifications.on_response(who, response.justification) - } - None => { - // we might have asked the peer for a justification on a block that we thought it had - // (regardless of whether it had a justification for it or not). - trace!(target: "sync", "Peer {:?} provided empty response for justification request {:?}", - who, - hash, + // We only request one justification at a time + debug_assert_eq!(1, response.blocks.len()); + + if let Some(block) = response.blocks.into_iter().next() { + if hash != block.hash { + info!( + "Invalid block justification provided by {}: requested: {:?} got: {:?}", who, hash, block.hash ); - return None; + return Err(BadPeer(who, i32::min_value())) } + if let Some((peer, hash, number, j)) = self.extra_justifications.on_response(who, block.justification) { + return Ok(OnBlockJustification::Import { peer, hash, number, justification: j }) + } + } else { + // we might have asked the peer for a justification on a block that we thought it had + // (regardless of whether it had a justification for it or not). + trace!(target: "sync", "Peer {:?} provided empty response for justification request {:?}", who, hash) } } - self.maintain_sync(protocol); - None + Ok(OnBlockJustification::Nothing) } /// Handle new finality proof data. - pub(crate) fn on_block_finality_proof_data( - &mut self, - protocol: &mut dyn Context, - who: PeerId, - response: message::FinalityProofResponse, - ) -> Option<(PeerId, B::Hash, NumberFor, Vec)> { - let peer = if let Some(peer) = self.peers.get_mut(&who) { - peer - } else { - error!(target: "sync", "Called on_block_finality_proof_data with a bad peer ID"); - return None; - }; + pub fn on_block_finality_proof + (&mut self, who: PeerId, resp: FinalityProofResponse) -> Result, BadPeer> + { + let peer = + if let Some(peer) = self.peers.get_mut(&who) { + peer + } else { + error!(target: "sync", "Called on_block_finality_proof_data with a bad peer ID"); + return Ok(OnBlockFinalityProof::Nothing) + }; if let PeerSyncState::DownloadingFinalityProof(hash) = peer.state { peer.state = PeerSyncState::Available; - // we only request one finality proof at a time - if hash != response.block { - info!( - "Invalid block finality proof provided: requested: {:?} got: {:?}", - hash, - response.block, - ); - - protocol.report_peer(who.clone(), i32::min_value()); - protocol.disconnect_peer(who); - return None; + // We only request one finality proof at a time. + if hash != resp.block { + info!("Invalid block finality proof provided: requested: {:?} got: {:?}", hash, resp.block); + return Err(BadPeer(who, i32::min_value())) } - return self.extra_finality_proofs.on_response(who, response.proof) + if let Some((peer, hash, number, p)) = self.extra_finality_proofs.on_response(who, resp.proof) { + return Ok(OnBlockFinalityProof::Import { peer, hash, number, proof: p }) + } } - self.maintain_sync(protocol); - None + Ok(OnBlockFinalityProof::Nothing) } /// A batch of blocks have been processed, with or without errors. - /// Call this when a batch of blocks have been processed by the import queue, with or without - /// errors. - pub fn blocks_processed(&mut self, protocol: &mut dyn Context, processed_blocks: Vec, has_error: bool) { - for hash in processed_blocks { - self.queue_blocks.remove(&hash); - } - if has_error { - self.best_importing_number = Zero::zero(); - } - self.maintain_sync(protocol) - } + /// + /// Call this when a batch of blocks have been processed by the import + /// queue, with or without errors. + /// + /// `peer_info` is passed in case of a restart. + pub fn on_blocks_processed<'a>( + &'a mut self, + imported: usize, + count: usize, + results: Vec<(Result>, BlockImportError>, B::Hash)>, + mut peer_info: impl FnMut(&PeerId) -> Option> + ) -> impl Iterator), BadPeer>> + 'a { + trace!(target: "sync", "Imported {} of {}", imported, count); + + let mut output = Vec::new(); + + let mut has_error = false; + let mut hashes = vec![]; + for (result, hash) in results { + hashes.push(hash); + + if has_error { + continue; + } - /// Maintain the sync process (download new blocks, fetch justifications). - fn maintain_sync(&mut self, protocol: &mut dyn Context) { - let peers: Vec = self.peers.keys().map(|p| p.clone()).collect(); - for peer in peers { - self.download_new(protocol, peer); - } - self.tick(protocol) - } + if result.is_err() { + has_error = true; + } - /// Called periodically to perform any time-based actions. Must be called at a regular - /// interval. - pub fn tick(&mut self, protocol: &mut dyn Context) { - self.send_justification_requests(protocol); - self.send_finality_proof_request(protocol) - } + match result { + Ok(BlockImportResult::ImportedKnown(_number)) => {} + Ok(BlockImportResult::ImportedUnknown(number, aux, who)) => { + if aux.clear_justification_requests { + trace!( + target: "sync", + "Block imported clears all pending justification requests {}: {:?}", + number, + hash + ); + self.extra_justifications.reset() + } - fn send_justification_requests(&mut self, protocol: &mut dyn Context) { - let mut matcher = self.extra_justifications.matcher(); - while let Some((peer, request)) = matcher.next(&self.peers) { - self.peers.get_mut(&peer) - .expect("`Matcher::next` guarantees the `PeerId` comes from the given peers; qed") - .state = PeerSyncState::DownloadingJustification(request.0); - protocol.send_block_request(peer, message::generic::BlockRequest { - id: 0, - fields: message::BlockAttributes::JUSTIFICATION, - from: message::FromBlock::Hash(request.0), - to: None, - direction: message::Direction::Ascending, - max: Some(1) - }) - } - } + if aux.needs_justification { + trace!(target: "sync", "Block imported but requires justification {}: {:?}", number, hash); + self.request_justification(&hash, number); + } - fn send_finality_proof_request(&mut self, protocol: &mut dyn Context) { - let mut matcher = self.extra_finality_proofs.matcher(); - while let Some((peer, request)) = matcher.next(&self.peers) { - self.peers.get_mut(&peer) - .expect("`Matcher::next` guarantees the `PeerId` comes from the given peers; qed") - .state = PeerSyncState::DownloadingFinalityProof(request.0); - protocol.send_finality_proof_request(peer, message::generic::FinalityProofRequest { - id: 0, - block: request.0, - request: self.request_builder.as_ref() - .map(|builder| builder.build_request_data(&request.0)) - .unwrap_or_default() - }) + if aux.bad_justification { + if let Some(peer) = who { + info!("Sent block with bad justification to import"); + output.push(Err(BadPeer(peer, BAD_JUSTIFICATION_REPUTATION_CHANGE))); + } + } + + if aux.needs_finality_proof { + trace!(target: "sync", "Block imported but requires finality proof {}: {:?}", number, hash); + self.request_finality_proof(&hash, number); + } + }, + Err(BlockImportError::IncompleteHeader(who)) => { + if let Some(peer) = who { + info!("Peer sent block with incomplete header to import"); + output.push(Err(BadPeer(peer, INCOMPLETE_HEADER_REPUTATION_CHANGE))); + output.extend(self.restart(&mut peer_info)); + } + }, + Err(BlockImportError::VerificationFailed(who, e)) => { + if let Some(peer) = who { + info!("Verification failed from peer: {}", e); + output.push(Err(BadPeer(peer, VERIFICATION_FAIL_REPUTATION_CHANGE))); + output.extend(self.restart(&mut peer_info)); + } + }, + Err(BlockImportError::BadBlock(who)) => { + if let Some(peer) = who { + info!("Bad block"); + output.push(Err(BadPeer(peer, BAD_BLOCK_REPUTATION_CHANGE))); + output.extend(self.restart(&mut peer_info)); + } + }, + Err(BlockImportError::UnknownParent) | + Err(BlockImportError::Cancelled) | + Err(BlockImportError::Other(_)) => { + output.extend(self.restart(&mut peer_info)); + }, + }; } - } - /// Request a justification for the given block. - /// - /// Uses `protocol` to queue a new justification request and tries to dispatch all pending - /// requests. - pub fn request_justification(&mut self, hash: &B::Hash, number: NumberFor, protocol: &mut dyn Context) { - self.extra_justifications.schedule((*hash, number), |base, block| { - protocol.client().is_descendent_of(base, block) - }); - self.send_justification_requests(protocol) - } + for hash in hashes { + self.queue_blocks.remove(&hash); + } + if has_error { + self.best_importing_number = Zero::zero() + } - /// Clears all pending justification requests. - pub fn clear_justification_requests(&mut self) { - self.extra_justifications.reset() + output.into_iter() } - /// Call this when a justification has been processed by the import queue, with or without - /// errors. - pub fn justification_import_result(&mut self, hash: B::Hash, number: NumberFor, success: bool) { + /// Call this when a justification has been processed by the import queue, + /// with or without errors. + pub fn on_justification_import(&mut self, hash: B::Hash, number: NumberFor, success: bool) { let finalization_result = if success { Ok((hash, number)) } else { Err(()) }; if !self.extra_justifications.try_finalize_root((hash, number), finalization_result, true) { debug!(target: "sync", "Got justification import result for unknown justification {:?} {:?} request.", hash, number, - ); + ) } } - /// Request a finality proof for the given block. - /// - /// Queues a new finality proof request and tries to dispatch all pending requests. - pub fn request_finality_proof(&mut self, hash: &B::Hash, number: NumberFor, protocol: &mut dyn Context) { - self.extra_finality_proofs.schedule((*hash, number), |base, block| { - protocol.client().is_descendent_of(base, block) - }); - self.send_finality_proof_request(protocol) - } - - pub fn finality_proof_import_result( - &mut self, - request_block: (B::Hash, NumberFor), - finalization_result: Result<(B::Hash, NumberFor), ()>, - ) { - self.extra_finality_proofs.try_finalize_root(request_block, finalization_result, true); - } - - pub fn set_finality_proof_request_builder(&mut self, builder: SharedFinalityProofRequestBuilder) { - self.request_builder = Some(builder) - } - - /// Log that a block has been successfully imported - pub fn block_imported(&mut self, hash: &B::Hash, number: NumberFor) { - trace!(target: "sync", "Block imported successfully {} ({})", number, hash); + pub fn on_finality_proof_import(&mut self, req: (B::Hash, NumberFor), res: Result<(B::Hash, NumberFor), ()>) { + self.extra_finality_proofs.try_finalize_root(req, res, true); } /// Notify about finalization of the given block. - pub fn on_block_finalized(&mut self, hash: &B::Hash, number: NumberFor, protocol: &mut dyn Context) { + pub fn on_block_finalized(&mut self, hash: &B::Hash, number: NumberFor) { + let client = &self.client; let r = self.extra_finality_proofs.on_block_finalized(hash, number, |base, block| { - protocol.client().is_descendent_of(base, block) + client.is_descendent_of(base, block) }); if let Err(err) = r { - warn!(target: "sync", "Error cleaning up pending extra finality proof data requests: {:?}", err); + warn!(target: "sync", "Error cleaning up pending extra finality proof data requests: {:?}", err) } + let client = &self.client; let r = self.extra_justifications.on_block_finalized(hash, number, |base, block| { - protocol.client().is_descendent_of(base, block) + client.is_descendent_of(base, block) }); if let Err(err) = r { @@ -743,9 +829,11 @@ impl ChainSync { } } - /// Called when a block has been queued for import. Updates our internal state for best queued - /// block and then goes through all peers to update our view of their state as well. - fn block_queued(&mut self, hash: &B::Hash, number: NumberFor) { + /// Called when a block has been queued for import. + /// + /// Updates our internal state for best queued block and then goes + /// through all peers to update our view of their state as well. + fn on_block_queued(&mut self, hash: &B::Hash, number: NumberFor) { if number > self.best_queued_number { self.best_queued_number = number; self.best_queued_hash = *hash; @@ -774,43 +862,29 @@ impl ChainSync { } } - /// Signal that `best_header` has been queued for import and update the `ChainSync` state with - /// that information. - pub(crate) fn update_chain_info(&mut self, best_header: &B::Header) { - let hash = best_header.hash(); - self.block_queued(&hash, best_header.number().clone()) - } - /// Call when a node announces a new block. /// - /// If true 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. - #[must_use] - pub(crate) fn on_block_announce( - &mut self, - protocol: &mut dyn Context, - who: PeerId, - hash: B::Hash, - header: &B::Header, - ) -> bool { + /// If true 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, header: &B::Header) -> OnBlockAnnounce { let number = *header.number(); debug!(target: "sync", "Received block announcement with number {:?}", number); if number.is_zero() { - warn!(target: "sync", "Ignored invalid block announcement from {}: {}", who, hash); - return false; + warn!(target: "sync", "Ignored genesis block (#0) announcement from {}: {}", who, hash); + return OnBlockAnnounce::Nothing } - let parent_status = block_status(&*protocol.client(), &self.queue_blocks, header.parent_hash().clone()).ok() - .unwrap_or(BlockStatus::Unknown); + let parent_status = self.block_status(header.parent_hash()).ok().unwrap_or(BlockStatus::Unknown); let known_parent = parent_status != BlockStatus::Unknown; let ancient_parent = parent_status == BlockStatus::InChainPruned; - let known = self.is_known(protocol, &hash); + let known = self.is_known(&hash); let peer = if let Some(peer) = self.peers.get_mut(&who) { peer } else { error!(target: "sync", "Called on_block_announce with a bad peer ID"); - return false; + return OnBlockAnnounce::Nothing }; while peer.recently_announced.len() >= ANNOUNCE_HISTORY_SIZE { peer.recently_announced.pop_front(); @@ -822,7 +896,7 @@ impl ChainSync { peer.best_hash = hash; } if let PeerSyncState::AncestorSearch(_, _) = peer.state { - return false; + return OnBlockAnnounce::Nothing } // We assume that the announced block is the latest they have seen, and so our common number // is either one further ahead or it's the one they just announced, if we know about it. @@ -835,260 +909,283 @@ impl ChainSync { // known block case if known || self.is_already_downloading(&hash) { trace!(target: "sync", "Known block announce from {}: {}", who, hash); - return false; + 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())) { - if protocol.client().block_status(&BlockId::Number(*header.number())) - .unwrap_or(BlockStatus::Unknown) == BlockStatus::InChainPruned - { + 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 + "Ignored unknown ancient block announced from {}: {} {:?}", who, hash, header ); - return false; + return OnBlockAnnounce::Nothing } - trace!( target: "sync", - "Considering new unknown stale block announced from {}: {} {:?}", - who, hash, header + "Considering new unknown stale block announced from {}: {} {:?}", who, hash, header ); - let request = self.download_unknown_stale(&who, &hash); - match request { - Some(request) => if requires_additional_data { - protocol.send_block_request(who, request); - return false; + if let Some(request) = self.download_unknown_stale(&who, &hash) { + if requires_additional_data { + return OnBlockAnnounce::Request(who, request) } else { - return true; - }, - None => return false, + return OnBlockAnnounce::ImportHeader + } + } else { + return OnBlockAnnounce::Nothing } } else { if ancient_parent { - trace!( - target: "sync", - "Ignored ancient stale block announced from {}: {} {:?}", - who, hash, header - ); - return false; + trace!(target: "sync", "Ignored ancient stale block announced from {}: {} {:?}", who, hash, header); + return OnBlockAnnounce::Nothing } - - let request = self.download_stale(&who, &hash); - match request { - Some(request) => if requires_additional_data { - protocol.send_block_request(who, request); - return false; + if let Some(request) = self.download_stale(&who, &hash) { + if requires_additional_data { + return OnBlockAnnounce::Request(who, request) } else { - return true; - }, - None => return false, + return OnBlockAnnounce::ImportHeader + } + } else { + return OnBlockAnnounce::Nothing } } } if ancient_parent { trace!(target: "sync", "Ignored ancient block announced from {}: {} {:?}", who, hash, header); - return false; + 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 false, + None => return OnBlockAnnounce::Nothing }; - let is_required_data_available = - !requires_additional_data && - range.end - range.start == One::one() && - range.start == *header.number(); - if !is_required_data_available { - protocol.send_block_request(who, request); - return false; - } - true - } + let is_required_data_available = !requires_additional_data + && range.end - range.start == One::one() + && range.start == *header.number(); - /// Convenience function to iterate through all peers and see if there are any that we are - /// downloading this hash from. - fn is_already_downloading(&self, hash: &B::Hash) -> bool { - self.peers.iter().any(|(_, p)| p.state == PeerSyncState::DownloadingStale(*hash)) - } + if !is_required_data_available { + return OnBlockAnnounce::Request(who, request) + } - /// Returns true if the block with given hash exists in the import queue with known status or is - /// already imported. - fn is_known(&self, protocol: &mut dyn Context, hash: &B::Hash) -> bool { - block_status(&*protocol.client(), &self.queue_blocks, *hash).ok().map_or(false, |s| s != BlockStatus::Unknown) + OnBlockAnnounce::ImportHeader } /// Call when a peer has disconnected. - pub(crate) fn peer_disconnected(&mut self, protocol: &mut dyn Context, who: PeerId) { + pub fn peer_disconnected(&mut self, who: PeerId) { self.blocks.clear_peer_download(&who); self.peers.remove(&who); self.extra_justifications.peer_disconnected(&who); self.extra_finality_proofs.peer_disconnected(&who); - self.maintain_sync(protocol); } /// Restart the sync process. - pub(crate) fn restart( - &mut self, - protocol: &mut dyn Context, - mut peer_info: impl FnMut(&PeerId) -> Option> - ) { + fn restart<'a, F> + (&'a mut self, mut peer_info: F) -> impl Iterator), BadPeer>> + 'a + where F: FnMut(&PeerId) -> Option> + 'a + { self.queue_blocks.clear(); self.best_importing_number = Zero::zero(); self.blocks.clear(); - let info = protocol.client().info(); + let info = self.client.info(); self.best_queued_hash = info.chain.best_hash; self.best_queued_number = info.chain.best_number; debug!(target:"sync", "Restarted with {} ({})", self.best_queued_number, self.best_queued_hash); - let ids: Vec = self.peers.drain().map(|(id, _)| id).collect(); - for id in ids { - if let Some(info) = peer_info(&id) { - self.new_peer(protocol, id, info); + let old_peers = std::mem::replace(&mut self.peers, HashMap::new()); + old_peers.into_iter().filter_map(move |(id, _)| { + let info = peer_info(&id)?; + match self.new_peer(id.clone(), info) { + Ok(None) => None, + Ok(Some(x)) => Some(Ok((id, x))), + Err(e) => Some(Err(e)) } - } + }) } - // Download old block with known parent. - fn download_stale( - &mut self, - who: &PeerId, - hash: &B::Hash, - ) -> Option> { + /// Download old block with known parent. + fn download_stale(&mut self, who: &PeerId, hash: &B::Hash) -> Option> { let peer = self.peers.get_mut(who)?; - match peer.state { - PeerSyncState::Available => { - 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), - }) - }, - _ => None, + 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> { + /// 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)?; - match peer.state { - PeerSyncState::Available => { - 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), - }) - }, - _ => None, - } - } - - // Issue a request for a peer to download new blocks, if any are available. - fn download_new(&mut self, protocol: &mut dyn Context, who: PeerId) { - if let Some((_, request)) = self.select_new_blocks(who.clone()) { - protocol.send_block_request(who, request); + 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 peer. - fn select_new_blocks(&mut self, who: PeerId) -> Option<(Range>, message::BlockRequest)> { + /// 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 if self.queue_blocks.len() > MAX_IMPORTING_BLOCKS { trace!(target: "sync", "Too many blocks in the queue."); - return None; + return None } let peer = self.peers.get_mut(&who)?; - match peer.state { - PeerSyncState::Available => { - trace!( - target: "sync", - "Considering new block download from {}, common block is {}, best is {:?}", - who, - peer.common_number, - peer.best_number, - ); - let range = self.blocks.needed_blocks( - who.clone(), - MAX_BLOCKS_TO_REQUEST, - peer.best_number, - peer.common_number - ); - match range { - Some(range) => { - trace!(target: "sync", "Requesting blocks from {}, ({} to {})", who, range.start, range.end); - let from = message::FromBlock::Number(range.start); - let max = Some((range.end - range.start).saturated_into::()); - peer.state = PeerSyncState::DownloadingNew(range.start); - Some(( - range, - message::generic::BlockRequest { - id: 0, - fields: self.required_block_attributes.clone(), - from, - to: None, - direction: message::Direction::Ascending, - max, - }, - )) - }, - None => { - trace!(target: "sync", "Nothing to request"); - None - }, - } - }, - _ => { - trace!(target: "sync", "Peer {} is busy", who); - None - }, + + if !peer.state.is_available() { + trace!(target: "sync", "Peer {} is busy", who); + return None } + + trace!( + target: "sync", + "Considering new block download from {}, common block is {}, best is {:?}", + who, + peer.common_number, + peer.best_number + ); + + if let Some((range, req)) = peer_block_request(&who, peer, &mut self.blocks, &self.required_block_attributes) { + trace!(target: "sync", "Requesting blocks from {}, ({} to {})", who, range.start, range.end); + peer.state = PeerSyncState::DownloadingNew(range.start); + Some((range, req)) + } else { + trace!(target: "sync", "Nothing to request from {}", who); + None + } + } + + /// What is the status of the block corresponding to the given hash? + fn block_status(&self, hash: &B::Hash) -> Result { + if self.queue_blocks.contains(hash) { + return Ok(BlockStatus::Queued) + } + self.client.block_status(&BlockId::Hash(*hash)) + } + + /// Is the block corresponding to the given hash known? + fn is_known(&self, hash: &B::Hash) -> bool { + self.block_status(hash).ok().map_or(false, |s| s != BlockStatus::Unknown) + } + + /// Is any peer downloading the given hash? + fn is_already_downloading(&self, hash: &B::Hash) -> bool { + self.peers.iter().any(|(_, p)| p.state == PeerSyncState::DownloadingStale(*hash)) + } +} + +/// Request the ancestry for a block. Sends a request for header and justification for the given +/// block number. Used during ancestry search. +fn ancestry_request(block: NumberFor) -> BlockRequest { + message::generic::BlockRequest { + id: 0, + fields: BlockAttributes::HEADER | BlockAttributes::JUSTIFICATION, + from: message::FromBlock::Number(block), + to: None, + direction: message::Direction::Ascending, + max: Some(1) } +} - /// Request the ancestry for a block. Sends a request for header and justification for the given - /// block number. Used during ancestry search. - fn request_ancestry(protocol: &mut dyn Context, who: PeerId, block: NumberFor) { - trace!(target: "sync", "Requesting ancestry block #{} from {}", block, who); +/// The ancestor search state expresses which algorithm, and its stateful parameters, we are using to +/// try to find an ancestor block +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum AncestorSearchState { + /// Use exponential backoff to find an ancestor, then switch to binary search. + /// We keep track of the exponent. + ExponentialBackoff(NumberFor), + /// Using binary search to find the best ancestor. + /// We keep track of left and right bounds. + BinarySearch(NumberFor, NumberFor), +} + +/// This function handles the ancestor search strategy used. The goal is to find a common point +/// that both our chains agree on that is as close to the tip as possible. +/// The way this works is we first have an exponential backoff strategy, where we try to step +/// forward until we find a block hash mismatch. The size of the step doubles each step we take. +/// +/// When we've found a block hash mismatch we then fall back to a binary search between the two +/// last known points to find the common block closest to the tip. +fn handle_ancestor_search_state( + state: &AncestorSearchState, + curr_block_num: NumberFor, + block_hash_match: bool +) -> Option<(AncestorSearchState, NumberFor)> { + let two = >::one() + >::one(); + match state { + AncestorSearchState::ExponentialBackoff(next_distance_to_tip) => { + let next_distance_to_tip = *next_distance_to_tip; + if block_hash_match && next_distance_to_tip == One::one() { + // We found the ancestor in the first step so there is no need to execute binary search. + return None; + } + if block_hash_match { + let left = curr_block_num; + let right = left + next_distance_to_tip / two; + let middle = left + (right - left) / two; + Some((AncestorSearchState::BinarySearch(left, right), middle)) + } else { + let next_block_num = curr_block_num.checked_sub(&next_distance_to_tip) + .unwrap_or_else(Zero::zero); + let next_distance_to_tip = next_distance_to_tip * two; + Some((AncestorSearchState::ExponentialBackoff(next_distance_to_tip), next_block_num)) + } + } + AncestorSearchState::BinarySearch(mut left, mut right) => { + if left >= curr_block_num { + return None; + } + if block_hash_match { + left = curr_block_num; + } else { + right = curr_block_num; + } + assert!(right >= left); + let middle = left + (right - left) / two; + Some((AncestorSearchState::BinarySearch(left, right), middle)) + } + } +} + +/// Get a new block request for the peer if any. +fn peer_block_request( + id: &PeerId, + peer: &PeerSync, + blocks: &mut BlockCollection, + attrs: &message::BlockAttributes, +) -> Option<(Range>, BlockRequest)> { + if let Some(range) = blocks.needed_blocks(id.clone(), MAX_BLOCKS_TO_REQUEST, peer.best_number, peer.common_number) { let request = message::generic::BlockRequest { id: 0, - fields: message::BlockAttributes::HEADER | message::BlockAttributes::JUSTIFICATION, - from: message::FromBlock::Number(block), + fields: attrs.clone(), + from: message::FromBlock::Number(range.start), to: None, direction: message::Direction::Ascending, - max: Some(1), + max: Some((range.end - range.start).saturated_into::()) }; - protocol.send_block_request(who, request); - } -} - -/// Returns the BlockStatus for given block hash, looking first in the import queue and then in the -/// provided chain. -fn block_status( - chain: &dyn crate::chain::Client, - queue_blocks: &HashSet, - hash: B::Hash) -> Result -{ - if queue_blocks.contains(&hash) { - return Ok(BlockStatus::Queued); + Some((range, request)) + } else { + None } - - chain.block_status(&BlockId::Hash(hash)) } diff --git a/core/network/src/protocol_behaviour.rs b/core/network/src/protocol_behaviour.rs deleted file mode 100644 index 81c0502f602b8313fa49f35deb8c34bb017c10df..0000000000000000000000000000000000000000 --- a/core/network/src/protocol_behaviour.rs +++ /dev/null @@ -1,463 +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 . - -//! Implementation of libp2p's `NetworkBehaviour` trait that handles everything Substrate-specific. - -use crate::{ExHashT, DiscoveryNetBehaviour, ProtocolId}; -use crate::custom_proto::{CustomProto, CustomProtoOut}; -use crate::chain::{Client, FinalityProofProvider}; -use crate::protocol::{self, CustomMessageOutcome, Protocol, ProtocolConfig, sync::SyncState}; -use crate::protocol::{PeerInfo, NetworkOut, message::Message, on_demand::RequestData}; -use crate::protocol::consensus_gossip::MessageRecipient as GossipMessageRecipient; -use crate::protocol::specialization::NetworkSpecialization; -use crate::service::TransactionPool; - -use client::light::fetcher::FetchChecker; -use futures::prelude::*; -use consensus::import_queue::SharedFinalityProofRequestBuilder; -use log::debug; -use libp2p::{PeerId, Multiaddr}; -use libp2p::core::swarm::{ConnectedPoint, NetworkBehaviour, NetworkBehaviourAction, PollParameters}; -use libp2p::core::{nodes::Substream, muxing::StreamMuxerBox}; -use libp2p::core::protocols_handler::{ProtocolsHandler, IntoProtocolsHandler}; -use runtime_primitives::{traits::{Block as BlockT, NumberFor}, ConsensusEngineId}; -use std::sync::Arc; - -/// Implementation of `NetworkBehaviour` that handles everything related to Substrate and Polkadot. -pub struct ProtocolBehaviour, H: ExHashT> { - /// Handles opening the unique substream and sending and receiving raw messages. - behaviour: CustomProto, Substream>, - /// Handles the logic behind the raw messages that we receive. - protocol: Protocol, - /// Used to report reputation changes. - peerset_handle: peerset::PeersetHandle, - transaction_pool: Arc>, - /// When asked for a proof of finality, we use this struct to build one. - finality_proof_provider: Option>>, -} - -impl, H: ExHashT> ProtocolBehaviour { - /// Builds a new `ProtocolBehaviour`. - pub fn new( - config: ProtocolConfig, - chain: Arc>, - checker: Arc>, - specialization: S, - transaction_pool: Arc>, - finality_proof_provider: Option>>, - protocol_id: ProtocolId, - versions: &[u8], - peerset: peerset::Peerset, - peerset_handle: peerset::PeersetHandle, - ) -> crate::error::Result { - let protocol = Protocol::new(config, chain, checker, specialization)?; - let behaviour = CustomProto::new(protocol_id, versions, peerset); - - Ok(ProtocolBehaviour { - protocol, - behaviour, - peerset_handle, - transaction_pool, - finality_proof_provider, - }) - } - - /// Returns the list of all the peers we have an open channel to. - pub fn open_peers(&self) -> impl Iterator { - self.behaviour.open_peers() - } - - /// Returns true if we have a channel open with this node. - pub fn is_open(&self, peer_id: &PeerId) -> bool { - self.behaviour.is_open(peer_id) - } - - /// Disconnects the given peer if we are connected to it. - pub fn disconnect_peer(&mut self, peer_id: &PeerId) { - self.behaviour.disconnect_peer(peer_id) - } - - /// Adjusts the reputation of a node. - pub fn report_peer(&mut self, who: PeerId, reputation: i32) { - self.peerset_handle.report_peer(who, reputation) - } - - /// Returns true if we try to open protocols with the given peer. - pub fn is_enabled(&self, peer_id: &PeerId) -> bool { - self.behaviour.is_enabled(peer_id) - } - - /// Sends a message to a peer. - /// - /// Has no effect if the custom protocol is not open with the given peer. - /// - /// Also note that even we have a valid open substream, it may in fact be already closed - /// without us knowing, in which case the packet will not be received. - pub fn send_packet(&mut self, target: &PeerId, message: Message) { - self.behaviour.send_packet(target, message) - } - - /// Returns the state of the peerset manager, for debugging purposes. - pub fn peerset_debug_info(&mut self) -> serde_json::Value { - self.behaviour.peerset_debug_info() - } - - /// Returns the number of peers we're connected to. - pub fn num_connected_peers(&self) -> usize { - self.protocol.num_connected_peers() - } - - /// Returns the number of peers we're connected to and that are being queried. - pub fn num_active_peers(&self) -> usize { - self.protocol.num_active_peers() - } - - /// Current global sync state. - pub fn sync_state(&self) -> SyncState { - self.protocol.sync_state() - } - - /// Target sync block number. - pub fn best_seen_block(&self) -> Option> { - self.protocol.best_seen_block() - } - - /// Number of peers participating in syncing. - pub fn num_sync_peers(&self) -> u32 { - self.protocol.num_sync_peers() - } - - /// Starts a new data demand request. - /// - /// The parameter contains a `Sender` where the result, once received, must be sent. - pub(crate) fn add_on_demand_request(&mut self, rq: RequestData) { - self.protocol.add_on_demand_request( - &mut LocalNetworkOut { inner: &mut self.behaviour, peerset_handle: &self.peerset_handle }, - rq - ); - } - - /// Returns information about all the peers we are connected to after the handshake message. - pub fn peers_info(&self) -> impl Iterator)> { - self.protocol.peers_info() - } - - /// Locks `self` and gives access to the protocol and a context that can be used in order to - /// use `consensus_gossip_lock` or `specialization_lock`. - /// - /// **Important**: ONLY USE THIS FUNCTION TO CALL `consensus_gossip_lock` or `specialization_lock`. - /// This function is a very bad API. - pub fn protocol_context_lock<'a>( - &'a mut self, - ) -> (&'a mut Protocol, LocalNetworkOut<'a, B>) { - let net_out = LocalNetworkOut { inner: &mut self.behaviour, peerset_handle: &self.peerset_handle }; - (&mut self.protocol, net_out) - } - - /// Gossip a consensus message to the network. - pub fn gossip_consensus_message( - &mut self, - topic: B::Hash, - engine_id: ConsensusEngineId, - message: Vec, - recipient: GossipMessageRecipient, - ) { - self.protocol.gossip_consensus_message( - &mut LocalNetworkOut { inner: &mut self.behaviour, peerset_handle: &self.peerset_handle }, - topic, - engine_id, - message, - recipient - ); - } - - /// Call when we must propagate ready extrinsics to peers. - pub fn propagate_extrinsics(&mut self) { - self.protocol.propagate_extrinsics( - &mut LocalNetworkOut { inner: &mut self.behaviour, peerset_handle: &self.peerset_handle }, - &*self.transaction_pool - ) - } - - /// Make sure an important block is propagated to peers. - /// - /// In chain-based consensus, we often need to make sure non-best forks are - /// at least temporarily synced. - pub fn announce_block(&mut self, hash: B::Hash) { - self.protocol.announce_block( - &mut LocalNetworkOut { inner: &mut self.behaviour, peerset_handle: &self.peerset_handle }, - hash - ) - } - - /// Call this when a block has been imported in the import queue and we should announce it on - /// the network. - pub fn on_block_imported(&mut self, hash: B::Hash, header: &B::Header) { - self.protocol.on_block_imported( - &mut LocalNetworkOut { inner: &mut self.behaviour, peerset_handle: &self.peerset_handle }, - hash, - header - ) - } - - /// Call this when a block has been finalized. The sync layer may have some additional - /// requesting to perform. - pub fn on_block_finalized(&mut self, hash: B::Hash, header: &B::Header) { - self.protocol.on_block_finalized( - &mut LocalNetworkOut { inner: &mut self.behaviour, peerset_handle: &self.peerset_handle }, - hash, - header - ) - } - - /// Request a justification for the given block. - /// - /// Uses `protocol` to queue a new justification request and tries to dispatch all pending - /// requests. - pub fn request_justification(&mut self, hash: &B::Hash, number: NumberFor) { - self.protocol.request_justification( - &mut LocalNetworkOut { inner: &mut self.behaviour, peerset_handle: &self.peerset_handle }, - hash, - number - ) - } - - /// Clears all pending justification requests. - pub fn clear_justification_requests(&mut self) { - self.protocol.clear_justification_requests() - } - - /// A batch of blocks have been processed, with or without errors. - /// Call this when a batch of blocks have been processed by the import queue, with or without - /// errors. - pub fn blocks_processed( - &mut self, - processed_blocks: Vec, - has_error: bool, - ) { - self.protocol.blocks_processed( - &mut LocalNetworkOut { inner: &mut self.behaviour, peerset_handle: &self.peerset_handle }, - processed_blocks, - has_error, - ) - } - - /// Restart the sync process. - pub fn restart(&mut self) { - let mut net_out = LocalNetworkOut { inner: &mut self.behaviour, peerset_handle: &self.peerset_handle }; - self.protocol.restart(&mut net_out); - } - - /// Notify about successful import of the given block. - pub fn block_imported(&mut self, hash: &B::Hash, number: NumberFor) { - self.protocol.block_imported(hash, number) - } - - pub fn set_finality_proof_request_builder(&mut self, request_builder: SharedFinalityProofRequestBuilder) { - self.protocol.set_finality_proof_request_builder(request_builder) - } - - /// Call this when a justification has been processed by the import queue, with or without - /// errors. - pub fn justification_import_result(&mut self, hash: B::Hash, number: NumberFor, success: bool) { - self.protocol.justification_import_result(hash, number, success) - } - - /// Request a finality proof for the given block. - /// - /// Queues a new finality proof request and tries to dispatch all pending requests. - pub fn request_finality_proof( - &mut self, - hash: &B::Hash, - number: NumberFor, - ) { - self.protocol.request_finality_proof( - &mut LocalNetworkOut { inner: &mut self.behaviour, peerset_handle: &self.peerset_handle }, - &hash, - number, - ); - } - - pub fn finality_proof_import_result( - &mut self, - request_block: (B::Hash, NumberFor), - finalization_result: Result<(B::Hash, NumberFor), ()>, - ) { - self.protocol.finality_proof_import_result(request_block, finalization_result) - } - - pub fn tick(&mut self) { - self.protocol.tick(&mut LocalNetworkOut { inner: &mut self.behaviour, peerset_handle: &self.peerset_handle }); - } -} - -impl, H: ExHashT> NetworkBehaviour for -ProtocolBehaviour { - type ProtocolsHandler = , Substream> as NetworkBehaviour>::ProtocolsHandler; - type OutEvent = CustomMessageOutcome; - - fn new_handler(&mut self) -> Self::ProtocolsHandler { - self.behaviour.new_handler() - } - - fn addresses_of_peer(&mut self, peer_id: &PeerId) -> Vec { - self.behaviour.addresses_of_peer(peer_id) - } - - fn inject_connected(&mut self, peer_id: PeerId, endpoint: ConnectedPoint) { - self.behaviour.inject_connected(peer_id, endpoint) - } - - fn inject_disconnected(&mut self, peer_id: &PeerId, endpoint: ConnectedPoint) { - self.behaviour.inject_disconnected(peer_id, endpoint) - } - - fn inject_node_event( - &mut self, - peer_id: PeerId, - event: <::Handler as ProtocolsHandler>::OutEvent, - ) { - self.behaviour.inject_node_event(peer_id, event) - } - - fn poll( - &mut self, - params: &mut PollParameters, - ) -> Async< - NetworkBehaviourAction< - <::Handler as ProtocolsHandler>::InEvent, - Self::OutEvent - > - > { - let mut net_out = LocalNetworkOut { inner: &mut self.behaviour, peerset_handle: &self.peerset_handle }; - match self.protocol.poll(&mut net_out, &*self.transaction_pool) { - Ok(Async::Ready(v)) => void::unreachable(v), - Ok(Async::NotReady) => {} - Err(err) => void::unreachable(err), - } - - let event = match self.behaviour.poll(params) { - Async::NotReady => return Async::NotReady, - Async::Ready(NetworkBehaviourAction::GenerateEvent(ev)) => ev, - Async::Ready(NetworkBehaviourAction::DialAddress { address }) => - return Async::Ready(NetworkBehaviourAction::DialAddress { address }), - Async::Ready(NetworkBehaviourAction::DialPeer { peer_id }) => - return Async::Ready(NetworkBehaviourAction::DialPeer { peer_id }), - Async::Ready(NetworkBehaviourAction::SendEvent { peer_id, event }) => - return Async::Ready(NetworkBehaviourAction::SendEvent { peer_id, event }), - Async::Ready(NetworkBehaviourAction::ReportObservedAddr { address }) => - return Async::Ready(NetworkBehaviourAction::ReportObservedAddr { address }), - }; - - let mut network_out = LocalNetworkOut { - inner: &mut self.behaviour, - peerset_handle: &self.peerset_handle, - }; - - let outcome = match event { - CustomProtoOut::CustomProtocolOpen { peer_id, version, .. } => { - debug_assert!( - version <= protocol::CURRENT_VERSION as u8 - && version >= protocol::MIN_VERSION as u8 - ); - self.protocol.on_peer_connected(&mut network_out, peer_id); - CustomMessageOutcome::None - } - CustomProtoOut::CustomProtocolClosed { peer_id, .. } => { - self.protocol.on_peer_disconnected(&mut network_out, peer_id); - CustomMessageOutcome::None - }, - CustomProtoOut::CustomMessage { peer_id, message } => - self.protocol.on_custom_message( - &mut network_out, - &*self.transaction_pool, - peer_id, - message, - self.finality_proof_provider.as_ref().map(|p| &**p) - ), - CustomProtoOut::Clogged { peer_id, messages } => { - debug!(target: "sync", "{} clogging messages:", messages.len()); - for msg in messages.into_iter().take(5) { - debug!(target: "sync", "{:?}", msg); - self.protocol.on_clogged_peer(&mut network_out, peer_id.clone(), Some(msg)); - } - CustomMessageOutcome::None - } - }; - - if let CustomMessageOutcome::None = outcome { - Async::NotReady - } else { - Async::Ready(NetworkBehaviourAction::GenerateEvent(outcome)) - } - } - - fn inject_replaced(&mut self, peer_id: PeerId, closed_endpoint: ConnectedPoint, new_endpoint: ConnectedPoint) { - self.behaviour.inject_replaced(peer_id, closed_endpoint, new_endpoint) - } - - fn inject_addr_reach_failure( - &mut self, - peer_id: Option<&PeerId>, - addr: &Multiaddr, - error: &dyn std::error::Error - ) { - self.behaviour.inject_addr_reach_failure(peer_id, addr, error) - } - - fn inject_dial_failure(&mut self, peer_id: &PeerId) { - self.behaviour.inject_dial_failure(peer_id) - } - - fn inject_new_listen_addr(&mut self, addr: &Multiaddr) { - self.behaviour.inject_new_listen_addr(addr) - } - - fn inject_expired_listen_addr(&mut self, addr: &Multiaddr) { - self.behaviour.inject_expired_listen_addr(addr) - } - - fn inject_new_external_addr(&mut self, addr: &Multiaddr) { - self.behaviour.inject_new_external_addr(addr) - } -} - -impl, H: ExHashT> DiscoveryNetBehaviour - for ProtocolBehaviour { - fn add_discovered_nodes(&mut self, peer_ids: impl Iterator) { - self.behaviour.add_discovered_nodes(peer_ids) - } -} - -/// Has to be public for stupid API reasons. This should be made private again ASAP. -pub struct LocalNetworkOut<'a, B: BlockT> { - inner: &'a mut CustomProto, Substream>, - peerset_handle: &'a peerset::PeersetHandle, -} - -impl<'a, B: BlockT> NetworkOut for LocalNetworkOut<'a, B> { - fn report_peer(&mut self, who: PeerId, reputation: i32) { - self.peerset_handle.report_peer(who, reputation) - } - - fn disconnect_peer(&mut self, who: PeerId) { - self.inner.disconnect_peer(&who) - } - - fn send_message(&mut self, who: PeerId, message: Message) { - self.inner.send_packet(&who, message) - } -} diff --git a/core/network/src/service.rs b/core/network/src/service.rs index eb28573d95d3bf3635666dee1b64ae1a8b6978f5..4cbe704eae090cf8e5e8f3c361f381a4b224706a 100644 --- a/core/network/src/service.rs +++ b/core/network/src/service.rs @@ -14,42 +14,40 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use std::collections::HashMap; -use std::{fs, io, path::Path}; -use std::sync::Arc; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::time::Duration; - +//! Main entry point of the substrate-network crate. +//! +//! There are two main structs in this module: [`NetworkWorker`] and [`NetworkService`]. +//! The [`NetworkWorker`] *is* the network and implements the `Future` trait. It must be polled in +//! order fo the network to advance. +//! The [`NetworkService`] is merely a shared version of the [`NetworkWorker`]. You can obtain an +//! `Arc` by calling [`NetworkWorker::service`]. +//! +//! The methods of the [`NetworkService`] are implemented by sending a message over a channel, +//! which is then processed by [`NetworkWorker::poll`]. + +use std::{collections::HashMap, fs, marker::PhantomData, io, path::Path}; +use std::sync::{Arc, atomic::{AtomicBool, AtomicUsize, Ordering}}; + +use consensus::import_queue::{ImportQueue, Link}; +use consensus::import_queue::{BlockImportResult, BlockImportError}; +use futures::{prelude::*, sync::mpsc}; use log::{warn, error, info}; -use libp2p::core::swarm::NetworkBehaviour; -use libp2p::core::{nodes::Substream, transport::boxed::Boxed, muxing::StreamMuxerBox}; -use futures::{prelude::*, sync::oneshot, sync::mpsc}; -use parking_lot::{Mutex, RwLock}; -use crate::protocol_behaviour::ProtocolBehaviour; -use crate::{behaviour::Behaviour, parse_str_addr}; -use crate::{NetworkState, NetworkStateNotConnectedPeer, NetworkStatePeer}; -use crate::{transport, config::NodeKeyConfig, config::NonReservedPeerMode}; +use libp2p::core::{swarm::NetworkBehaviour, transport::boxed::Boxed, muxing::StreamMuxerBox}; +use libp2p::{PeerId, Multiaddr, multihash::Multihash}; +use parking_lot::Mutex; use peerset::PeersetHandle; -use consensus::import_queue::{ImportQueue, Link, SharedFinalityProofRequestBuilder}; use runtime_primitives::{traits::{Block as BlockT, NumberFor}, ConsensusEngineId}; -use crate::AlwaysBadChecker; -use crate::protocol::consensus_gossip::{ConsensusGossip, MessageRecipient as GossipMessageRecipient}; -use crate::protocol::message::Message; -use crate::protocol::on_demand::RequestData; -use crate::protocol::{self, Context, CustomMessageOutcome, ConnectedPeer, PeerInfo}; -use crate::protocol::sync::SyncState; -use crate::config::Params; +use crate::{behaviour::{Behaviour, BehaviourOut}, config::parse_str_addr}; +use crate::{NetworkState, NetworkStateNotConnectedPeer, NetworkStatePeer}; +use crate::{transport, config::NodeKeyConfig, config::NonReservedPeerMode}; +use crate::config::{Params, TransportConfig}; use crate::error::Error; +use crate::protocol::{self, Protocol, Context, CustomMessageOutcome, PeerInfo}; +use crate::protocol::consensus_gossip::{ConsensusGossip, MessageRecipient as GossipMessageRecipient}; +use crate::protocol::{event::Event, on_demand::{AlwaysBadChecker, RequestData}}; use crate::protocol::specialization::NetworkSpecialization; - -/// Interval at which we update the `peers` field on the main thread. -const CONNECTED_PEERS_INTERVAL: Duration = Duration::from_millis(500); - -pub use libp2p::PeerId; - -/// Type that represents fetch completion future. -pub type FetchFuture = oneshot::Receiver>; +use crate::protocol::sync::SyncState; /// Minimum Requirements for a Hash within Networking pub trait ExHashT: @@ -87,23 +85,24 @@ impl ReportHandle { /// Substrate network service. Handles network IO and manages connectivity. pub struct NetworkService, H: ExHashT> { - /// Are we connected to any peer? - is_offline: Arc, + /// Number of peers we're connected to. + num_connected: Arc, + /// The local external addresses. + external_addresses: Arc>>, /// Are we actively catching up with the chain? is_major_syncing: Arc, - /// Peers whom we are connected with. - peers: Arc>>>, - /// Channel for networking messages processed by the background thread. - network_chan: mpsc::UnboundedSender>, - /// Network service - network: Arc>>, + /// Local copy of the `PeerId` of the local node. + local_peer_id: PeerId, /// Bandwidth logging system. Can be queried to know the average bandwidth consumed. bandwidth: Arc, /// Peerset manager (PSM); manages the reputation of nodes and indicates the network which /// nodes it should be connected to or not. peerset: PeersetHandle, - /// Protocol sender - protocol_sender: mpsc::UnboundedSender>, + /// Channel that sends messages to the actual worker. + to_worker: mpsc::UnboundedSender>, + /// Marker to pin the `H` generic. Serves no purpose except to not break backwards + /// compatibility. + _marker: PhantomData, } impl, H: ExHashT> NetworkWorker { @@ -115,8 +114,7 @@ impl, H: ExHashT> NetworkWorker pub fn new( params: Params, ) -> Result, Error> { - let (network_chan, network_port) = mpsc::unbounded(); - let (protocol_sender, protocol_rx) = mpsc::unbounded(); + let (to_worker, from_worker) = mpsc::unbounded(); if let Some(ref path) = params.network_config.net_config_path { fs::create_dir_all(Path::new(path))?; @@ -148,14 +146,13 @@ impl, H: ExHashT> NetworkWorker } } - // Build the peerset. - let (peerset, peerset_handle) = peerset::Peerset::from_config(peerset::PeersetConfig { + let peerset_config = peerset::PeersetConfig { in_peers: params.network_config.in_peers, out_peers: params.network_config.out_peers, bootnodes, reserved_only: params.network_config.non_reserved_mode == NonReservedPeerMode::Deny, reserved_nodes, - }); + }; // Private and public keys configuration. if let NodeKeyConfig::Secp256k1(_) = params.network_config.node_key { @@ -166,11 +163,9 @@ impl, H: ExHashT> NetworkWorker let local_peer_id = local_public.clone().into_peer_id(); info!(target: "sub-libp2p", "Local node identity is: {}", local_peer_id.to_base58()); - // Start in off-line mode, since we're not connected to any nodes yet. - let is_offline = Arc::new(AtomicBool::new(true)); + let num_connected = Arc::new(AtomicUsize::new(0)); let is_major_syncing = Arc::new(AtomicBool::new(false)); - let peers: Arc>>> = Arc::new(Default::default()); - let protocol = ProtocolBehaviour::new( + let (protocol, peerset_handle) = Protocol::new( protocol::ProtocolConfig { roles: params.roles }, params.chain, params.on_demand.as_ref().map(|od| od.checker().clone()) @@ -178,10 +173,9 @@ impl, H: ExHashT> NetworkWorker params.specialization, params.transaction_pool, params.finality_proof_provider, + params.finality_proof_request_builder, params.protocol_id, - &((protocol::MIN_VERSION as u8)..=(protocol::CURRENT_VERSION as u8)).collect::>(), - peerset, - peerset_handle.clone(), + peerset_config, )?; // Build the swarm. @@ -196,12 +190,19 @@ impl, H: ExHashT> NetworkWorker user_agent, local_public, known_addresses, - params.network_config.enable_mdns - ); - let (transport, bandwidth) = transport::build_transport( - local_identity, - params.network_config.wasm_external_transport + match params.network_config.transport { + TransportConfig::MemoryOnly => false, + TransportConfig::Normal { enable_mdns, .. } => enable_mdns, + } ); + let (transport, bandwidth) = { + let (config_mem, config_wasm) = match params.network_config.transport { + TransportConfig::MemoryOnly => (true, None), + TransportConfig::Normal { wasm_external_transport, .. } => + (false, wasm_external_transport) + }; + transport::build_transport(local_identity, config_mem, config_wasm) + }; (Swarm::::new(transport, behaviour, local_peer_id.clone()), bandwidth) }; @@ -217,31 +218,28 @@ impl, H: ExHashT> NetworkWorker Swarm::::add_external_address(&mut swarm, addr.clone()); } - let network = Arc::new(Mutex::new(swarm)); + let external_addresses = Arc::new(Mutex::new(Vec::new())); let service = Arc::new(NetworkService { bandwidth, - is_offline: is_offline.clone(), + external_addresses: external_addresses.clone(), + num_connected: num_connected.clone(), is_major_syncing: is_major_syncing.clone(), - network_chan, - peers: peers.clone(), - peerset: peerset_handle.clone(), - network: network.clone(), - protocol_sender: protocol_sender.clone(), + peerset: peerset_handle, + local_peer_id, + to_worker: to_worker.clone(), + _marker: PhantomData, }); Ok(NetworkWorker { - is_offline, + external_addresses, + num_connected, is_major_syncing, - network_service: network, - peerset: peerset_handle, + network_service: swarm, service, - peers, import_queue: params.import_queue, - network_port, - protocol_rx, + from_worker, on_demand_in: params.on_demand.and_then(|od| od.extract_receiver()), - connected_peers_interval: tokio_timer::Interval::new_interval(CONNECTED_PEERS_INTERVAL), }) } @@ -257,27 +255,32 @@ impl, H: ExHashT> NetworkWorker /// Returns the number of peers we're connected to. pub fn num_connected_peers(&self) -> usize { - self.network_service.lock().user_protocol_mut().num_connected_peers() + self.network_service.user_protocol().num_connected_peers() } /// Returns the number of peers we're connected to and that are being queried. pub fn num_active_peers(&self) -> usize { - self.network_service.lock().user_protocol_mut().num_active_peers() + self.network_service.user_protocol().num_active_peers() } /// Current global sync state. pub fn sync_state(&self) -> SyncState { - self.network_service.lock().user_protocol_mut().sync_state() + self.network_service.user_protocol().sync_state() } /// Target sync block number. pub fn best_seen_block(&self) -> Option> { - self.network_service.lock().user_protocol_mut().best_seen_block() + self.network_service.user_protocol().best_seen_block() } /// Number of peers participating in syncing. pub fn num_sync_peers(&self) -> u32 { - self.network_service.lock().user_protocol_mut().num_sync_peers() + self.network_service.user_protocol().num_sync_peers() + } + + /// Adds an address for a node. + pub fn add_known_address(&mut self, peer_id: PeerId, addr: Multiaddr) { + self.network_service.add_known_address(peer_id, addr); } /// Return a `NetworkService` that can be shared through the code base and can be used to @@ -285,97 +288,23 @@ impl, H: ExHashT> NetworkWorker pub fn service(&self) -> &Arc> { &self.service } -} - -impl, H: ExHashT> NetworkService { - /// Returns the network identity of the node. - pub fn local_peer_id(&self) -> PeerId { - Swarm::::local_peer_id(&*self.network.lock()).clone() - } - - /// Called when a new block is imported by the client. - pub fn on_block_imported(&self, hash: B::Hash, header: B::Header) { - let _ = self - .protocol_sender - .unbounded_send(ProtocolMsg::BlockImported(hash, header)); - } - - /// Called when a new block is finalized by the client. - pub fn on_block_finalized(&self, hash: B::Hash, header: B::Header) { - let _ = self - .protocol_sender - .unbounded_send(ProtocolMsg::BlockFinalized(hash, header)); - } - - /// Called when new transactons are imported by the client. - pub fn trigger_repropagate(&self) { - let _ = self.protocol_sender.unbounded_send(ProtocolMsg::PropagateExtrinsics); - } - - /// Make sure an important block is propagated to peers. - /// - /// In chain-based consensus, we often need to make sure non-best forks are - /// at least temporarily synced. - pub fn announce_block(&self, hash: B::Hash) { - let _ = self.protocol_sender.unbounded_send(ProtocolMsg::AnnounceBlock(hash)); - } - - /// Send a consensus message through the gossip - pub fn gossip_consensus_message( - &self, - topic: B::Hash, - engine_id: ConsensusEngineId, - message: Vec, - recipient: GossipMessageRecipient, - ) { - let _ = self - .protocol_sender - .unbounded_send(ProtocolMsg::GossipConsensusMessage( - topic, engine_id, message, recipient, - )); - } - - /// Report a given peer as either beneficial (+) or costly (-) according to the - /// given scalar. - pub fn report_peer(&self, who: PeerId, cost_benefit: i32) { - self.peerset.report_peer(who, cost_benefit); - } - /// Send a message to the given peer. Has no effect if we're not connected to this peer. - /// - /// This method is extremely poor in terms of API and should be eventually removed. - pub fn disconnect_peer(&self, who: PeerId) { - let _ = self.network_chan.unbounded_send(NetworkMsg::DisconnectPeer(who)); + /// You must call this when a new block is imported by the client. + pub fn on_block_imported(&mut self, hash: B::Hash, header: B::Header) { + self.network_service.user_protocol_mut().on_block_imported(hash, &header); } - /// Execute a closure with the chain-specific network specialization. - pub fn with_spec(&self, f: F) - where F: FnOnce(&mut S, &mut dyn Context) + Send + 'static - { - let _ = self - .protocol_sender - .unbounded_send(ProtocolMsg::ExecuteWithSpec(Box::new(f))); + /// You must call this when a new block is finalized by the client. + pub fn on_block_finalized(&mut self, hash: B::Hash, header: B::Header) { + self.network_service.user_protocol_mut().on_block_finalized(hash, &header); } - /// Execute a closure with the consensus gossip. - pub fn with_gossip(&self, f: F) - where F: FnOnce(&mut ConsensusGossip, &mut dyn Context) + Send + 'static - { - let _ = self - .protocol_sender - .unbounded_send(ProtocolMsg::ExecuteWithGossip(Box::new(f))); - } - - /// Are we in the process of downloading the chain? - pub fn is_major_syncing(&self) -> bool { - self.is_major_syncing.load(Ordering::Relaxed) - } -} - -impl, H: ExHashT> NetworkService { /// Get network state. - pub fn network_state(&self) -> NetworkState { - let mut swarm = self.network.lock(); + /// + /// **Note**: Use this only for debugging. This API is unstable. There are warnings literaly + /// everywhere about this. Please don't use this function to retrieve actual information. + pub fn network_state(&mut self) -> NetworkState { + let swarm = &mut self.network_service; let open = swarm.user_protocol().open_peers().cloned().collect::>(); let connected_peers = { @@ -423,8 +352,8 @@ impl, H: ExHashT> NetworkServic peer_id: Swarm::::local_peer_id(&swarm).to_base58(), listened_addresses: Swarm::::listeners(&swarm).cloned().collect(), external_addresses: Swarm::::external_addresses(&swarm).cloned().collect(), - average_download_per_sec: self.bandwidth.average_download_per_sec(), - average_upload_per_sec: self.bandwidth.average_upload_per_sec(), + average_download_per_sec: self.service.bandwidth.average_download_per_sec(), + average_upload_per_sec: self.service.bandwidth.average_upload_per_sec(), connected_peers, not_connected_peers, peerset: swarm.user_protocol_mut().peerset_debug_info(), @@ -432,155 +361,219 @@ impl, H: ExHashT> NetworkServic } /// Get currently connected peers. - /// - /// > **Warning**: This method can return outdated information and should only ever be used - /// > when obtaining outdated information is acceptable. - pub fn peers_debug_info(&self) -> Vec<(PeerId, PeerInfo)> { - let peers = (*self.peers.read()).clone(); - peers.into_iter().map(|(idx, connected)| (idx, connected.peer_info)).collect() + pub fn peers_debug_info(&mut self) -> Vec<(PeerId, PeerInfo)> { + self.network_service.user_protocol_mut() + .peers_info() + .map(|(id, info)| (id.clone(), info.clone())) + .collect() } } -impl, H: ExHashT> - ::consensus::SyncOracle for NetworkService { - fn is_major_syncing(&self) -> bool { - self.is_major_syncing() +impl, H: ExHashT> NetworkService { + /// Returns the network identity of the node. + pub fn local_peer_id(&self) -> PeerId { + self.local_peer_id.clone() } - fn is_offline(&self) -> bool { - self.is_offline.load(Ordering::Relaxed) + /// You must call this when new transactons are imported by the transaction pool. + /// + /// The latest transactions will be fetched from the `TransactionPool` that was passed at + /// initialization as part of the configuration. + pub fn trigger_repropagate(&self) { + let _ = self.to_worker.unbounded_send(ServerToWorkerMsg::PropagateExtrinsics); } -} -/// Trait for managing network -pub trait ManageNetwork { - /// Set to allow unreserved peers to connect - fn accept_unreserved_peers(&self); - /// Set to deny unreserved peers to connect - fn deny_unreserved_peers(&self); - /// Remove reservation for the peer - fn remove_reserved_peer(&self, peer: PeerId); - /// Add reserved peer - fn add_reserved_peer(&self, peer: String) -> Result<(), String>; -} + /// Make sure an important block is propagated to peers. + /// + /// In chain-based consensus, we often need to make sure non-best forks are + /// at least temporarily synced. This function forces such an announcement. + pub fn announce_block(&self, hash: B::Hash) { + let _ = self.to_worker.unbounded_send(ServerToWorkerMsg::AnnounceBlock(hash)); + } + + /// Send a consensus message through the gossip + pub fn gossip_consensus_message( + &self, + topic: B::Hash, + engine_id: ConsensusEngineId, + message: Vec, + recipient: GossipMessageRecipient, + ) { + let _ = self + .to_worker + .unbounded_send(ServerToWorkerMsg::GossipConsensusMessage( + topic, engine_id, message, recipient, + )); + } -impl, H: ExHashT> ManageNetwork for NetworkService { - fn accept_unreserved_peers(&self) { + /// Report a given peer as either beneficial (+) or costly (-) according to the + /// given scalar. + pub fn report_peer(&self, who: PeerId, cost_benefit: i32) { + self.peerset.report_peer(who, cost_benefit); + } + + /// Request a justification for the given block from the network. + /// + /// On success, the justification will be passed to the import queue that was part at + /// initialization as part of the configuration. + pub fn request_justification(&self, hash: &B::Hash, number: NumberFor) { + let _ = self + .to_worker + .unbounded_send(ServerToWorkerMsg::RequestJustification(hash.clone(), number)); + } + + /// Execute a closure with the chain-specific network specialization. + pub fn with_spec(&self, f: F) + where F: FnOnce(&mut S, &mut dyn Context) + Send + 'static + { + let _ = self + .to_worker + .unbounded_send(ServerToWorkerMsg::ExecuteWithSpec(Box::new(f))); + } + + /// Execute a closure with the consensus gossip. + pub fn with_gossip(&self, f: F) + where F: FnOnce(&mut ConsensusGossip, &mut dyn Context) + Send + 'static + { + let _ = self + .to_worker + .unbounded_send(ServerToWorkerMsg::ExecuteWithGossip(Box::new(f))); + } + + /// Are we in the process of downloading the chain? + pub fn is_major_syncing(&self) -> bool { + self.is_major_syncing.load(Ordering::Relaxed) + } + + /// 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. + pub fn get_value(&self, key: &Multihash) { + let _ = self + .to_worker + .unbounded_send(ServerToWorkerMsg::GetValue(key.clone())); + } + + /// 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. + pub fn put_value(&self, key: Multihash, value: Vec) { + let _ = self + .to_worker + .unbounded_send(ServerToWorkerMsg::PutValue(key, value)); + } + + /// Connect to unreserved peers and allow unreserved peers to connect. + pub fn accept_unreserved_peers(&self) { self.peerset.set_reserved_only(false); } - fn deny_unreserved_peers(&self) { + /// Disconnect from unreserved peers and deny new unreserved peers to connect. + pub fn deny_unreserved_peers(&self) { self.peerset.set_reserved_only(true); } - fn remove_reserved_peer(&self, peer: PeerId) { + /// Removes a `PeerId` from the list of reserved peers. + pub fn remove_reserved_peer(&self, peer: PeerId) { self.peerset.remove_reserved_peer(peer); } - fn add_reserved_peer(&self, peer: String) -> Result<(), String> { + /// Adds a `PeerId` and its address as reserved. + pub fn add_reserved_peer(&self, peer: String) -> Result<(), String> { let (peer_id, addr) = parse_str_addr(&peer).map_err(|e| format!("{:?}", e))?; self.peerset.add_reserved_peer(peer_id.clone()); - self.network.lock().add_known_address(peer_id, addr); + let _ = self + .to_worker + .unbounded_send(ServerToWorkerMsg::AddKnownAddress(peer_id, addr)); Ok(()) } -} -/// Messages to be handled by Libp2pNetService. -#[derive(Debug)] -pub enum NetworkMsg { - /// Send an outgoing custom message. - Outgoing(PeerId, Message), - /// Disconnect a peer we're connected to, or do nothing if we're not connected. - DisconnectPeer(PeerId), - /// Performs a reputation adjustement on a peer. - ReportPeer(PeerId, i32), - /// Synchronization response. - #[cfg(any(test, feature = "test-helpers"))] - Synchronized, -} + /// Returns the number of peers we're connected to. + pub fn num_connected(&self) -> usize { + self.num_connected.load(Ordering::Relaxed) + } -/// Messages sent to Protocol from elsewhere inside the system. -pub enum ProtocolMsg> { - /// A batch of blocks has been processed, with or without errors. - BlocksProcessed(Vec, bool), - /// Tell protocol to restart sync. - RestartSync, - /// Tell protocol to propagate extrinsics. - PropagateExtrinsics, - /// Tell protocol that a block was imported (sent by the import-queue). - BlockImportedSync(B::Hash, NumberFor), - /// Tell protocol to clear all pending justification requests. - ClearJustificationRequests, - /// Tell protocol to request justification for a block. - RequestJustification(B::Hash, NumberFor), - /// Inform protocol whether a justification was successfully imported. - JustificationImportResult(B::Hash, NumberFor, bool), - /// Set finality proof request builder. - SetFinalityProofRequestBuilder(SharedFinalityProofRequestBuilder), - /// Tell protocol to request finality proof for a block. - RequestFinalityProof(B::Hash, NumberFor), - /// Inform protocol whether a finality proof was successfully imported. - FinalityProofImportResult((B::Hash, NumberFor), Result<(B::Hash, NumberFor), ()>), - /// Propagate a block to peers. - AnnounceBlock(B::Hash), - /// A block has been imported (sent by the client). - BlockImported(B::Hash, B::Header), - /// A block has been finalized (sent by the client). - BlockFinalized(B::Hash, B::Header), - /// Execute a closure with the chain-specific network specialization. - ExecuteWithSpec(Box + Send + 'static>), - /// Execute a closure with the consensus gossip. - ExecuteWithGossip(Box + Send + 'static>), - /// Incoming gossip consensus message. - GossipConsensusMessage(B::Hash, ConsensusEngineId, Vec, GossipMessageRecipient), - /// Tell protocol to perform regular maintenance. - #[cfg(any(test, feature = "test-helpers"))] - Tick, - /// Synchronization request. - #[cfg(any(test, feature = "test-helpers"))] - Synchronize, + /// Returns the local external addresses. + pub fn external_addresses(&self) -> Vec { + self.external_addresses.lock().clone() + } } -/// A task, consisting of a user-provided closure, to be executed on the Protocol thread. -pub trait SpecTask> { - fn call_box(self: Box, spec: &mut S, context: &mut dyn Context); -} +impl, H: ExHashT> + ::consensus::SyncOracle for NetworkService { + fn is_major_syncing(&self) -> bool { + self.is_major_syncing() + } -impl, F: FnOnce(&mut S, &mut dyn Context)> SpecTask for F { - fn call_box(self: Box, spec: &mut S, context: &mut dyn Context) { - (*self)(spec, context) + fn is_offline(&self) -> bool { + self.num_connected.load(Ordering::Relaxed) == 0 } } -/// A task, consisting of a user-provided closure, to be executed on the Protocol thread. -pub trait GossipTask { - fn call_box(self: Box, gossip: &mut ConsensusGossip, context: &mut dyn Context); +/// Trait for providing information about the local network state +pub trait NetworkStateInfo { + /// Returns the local external addresses. + fn external_addresses(&self) -> Vec; + + /// Returns the local Peer ID. + fn peer_id(&self) -> PeerId; } -impl, &mut dyn Context)> GossipTask for F { - fn call_box(self: Box, gossip: &mut ConsensusGossip, context: &mut dyn Context) { - (*self)(gossip, context) +impl NetworkStateInfo for NetworkService + where + B: runtime_primitives::traits::Block, + S: NetworkSpecialization, + H: ExHashT, +{ + /// Returns the local external addresses. + fn external_addresses(&self) -> Vec { + self.external_addresses.lock().clone() + } + + /// Returns the local Peer ID. + fn peer_id(&self) -> PeerId { + self.local_peer_id.clone() } } -/// Future tied to the `Network` service and that must be polled in order for the network to -/// advance. +/// Messages sent from the `NetworkService` to the `NetworkWorker`. +/// +/// Each entry corresponds to a method of `NetworkService`. +enum ServerToWorkerMsg> { + PropagateExtrinsics, + RequestJustification(B::Hash, NumberFor), + AnnounceBlock(B::Hash), + ExecuteWithSpec(Box) + Send>), + ExecuteWithGossip(Box, &mut dyn Context) + Send>), + GossipConsensusMessage(B::Hash, ConsensusEngineId, Vec, GossipMessageRecipient), + GetValue(Multihash), + PutValue(Multihash, Vec), + AddKnownAddress(PeerId, Multiaddr), +} + +/// Main network worker. Must be polled in order for the network to advance. +/// +/// You are encouraged to poll this in a separate background thread or task. #[must_use = "The NetworkWorker must be polled in order for the network to work"] pub struct NetworkWorker, H: ExHashT> { - is_offline: Arc, + /// Updated by the `NetworkWorker` and loaded by the `NetworkService`. + external_addresses: Arc>>, + /// Updated by the `NetworkWorker` and loaded by the `NetworkService`. + num_connected: Arc, + /// Updated by the `NetworkWorker` and loaded by the `NetworkService`. is_major_syncing: Arc, /// The network service that can be extracted and shared through the codebase. service: Arc>, - network_service: Arc>>, - peers: Arc>>>, + /// The *actual* network. + network_service: Swarm, + /// The import queue that was passed as initialization. import_queue: Box>, - network_port: mpsc::UnboundedReceiver>, - protocol_rx: mpsc::UnboundedReceiver>, - peerset: PeersetHandle, + /// Messages from the `NetworkService` and that must be processed. + from_worker: mpsc::UnboundedReceiver>, + /// Receiver for queries from the on-demand that must be processed. on_demand_in: Option>>, - - /// Interval at which we update the `connected_peers` Arc. - connected_peers_interval: tokio_timer::Interval, } impl, H: ExHashT> Future for NetworkWorker { @@ -588,163 +581,66 @@ impl, H: ExHashT> Future for Ne type Error = io::Error; fn poll(&mut self) -> Poll { - // Implementation of `import_queue::Link` trait using the available local variables. - struct NetworkLink<'a, B: BlockT, S: NetworkSpecialization, H: ExHashT> { - protocol: &'a mut Swarm, - } - impl<'a, B: BlockT, S: NetworkSpecialization, H: ExHashT> Link for NetworkLink<'a, B, S, H> { - fn block_imported(&mut self, hash: &B::Hash, number: NumberFor) { - self.protocol.user_protocol_mut().block_imported(&hash, number) - } - fn blocks_processed(&mut self, hashes: Vec, has_error: bool) { - self.protocol.user_protocol_mut().blocks_processed(hashes, has_error) - } - fn justification_imported(&mut self, who: PeerId, hash: &B::Hash, number: NumberFor, success: bool) { - self.protocol.user_protocol_mut().justification_import_result(hash.clone(), number, success); - if !success { - info!("Invalid justification provided by {} for #{}", who, hash); - self.protocol.user_protocol_mut().disconnect_peer(&who); - self.protocol.user_protocol_mut().report_peer(who, i32::min_value()); - } - } - fn clear_justification_requests(&mut self) { - self.protocol.user_protocol_mut().clear_justification_requests() - } - fn request_justification(&mut self, hash: &B::Hash, number: NumberFor) { - self.protocol.user_protocol_mut().request_justification(hash, number) - } - fn request_finality_proof(&mut self, hash: &B::Hash, number: NumberFor) { - self.protocol.user_protocol_mut().request_finality_proof(hash, number) - } - fn finality_proof_imported( - &mut self, - who: PeerId, - request_block: (B::Hash, NumberFor), - finalization_result: Result<(B::Hash, NumberFor), ()>, - ) { - let success = finalization_result.is_ok(); - self.protocol.user_protocol_mut().finality_proof_import_result(request_block, finalization_result); - if !success { - info!("Invalid finality proof provided by {} for #{}", who, request_block.0); - self.protocol.user_protocol_mut().disconnect_peer(&who); - self.protocol.user_protocol_mut().report_peer(who, i32::min_value()); - } - } - fn report_peer(&mut self, who: PeerId, reputation_change: i32) { - self.protocol.user_protocol_mut().report_peer(who, reputation_change) - } - fn restart(&mut self) { - self.protocol.user_protocol_mut().restart() - } - fn set_finality_proof_request_builder(&mut self, builder: SharedFinalityProofRequestBuilder) { - self.protocol.user_protocol_mut().set_finality_proof_request_builder(builder) - } - } - - { - let mut network_service = self.network_service.lock(); - let mut link = NetworkLink { - protocol: &mut network_service, - }; - self.import_queue.poll_actions(&mut link); - } - - while let Ok(Async::Ready(_)) = self.connected_peers_interval.poll() { - let mut network_service = self.network_service.lock(); - let infos = network_service.user_protocol_mut().peers_info().map(|(id, info)| { - (id.clone(), ConnectedPeer { peer_info: info.clone() }) - }).collect(); - *self.peers.write() = infos; - } + // Poll the import queue for actions to perform. + self.import_queue.poll_actions(&mut NetworkLink { + protocol: &mut self.network_service, + }); // Check for new incoming on-demand requests. if let Some(on_demand_in) = self.on_demand_in.as_mut() { while let Ok(Async::Ready(Some(rq))) = on_demand_in.poll() { - let mut network_service = self.network_service.lock(); - network_service.user_protocol_mut().add_on_demand_request(rq); - } - } - - loop { - match self.network_port.poll() { - Ok(Async::NotReady) => break, - Ok(Async::Ready(Some(NetworkMsg::Outgoing(who, outgoing_message)))) => - self.network_service.lock().user_protocol_mut().send_packet(&who, outgoing_message), - Ok(Async::Ready(Some(NetworkMsg::ReportPeer(who, reputation)))) => - self.peerset.report_peer(who, reputation), - Ok(Async::Ready(Some(NetworkMsg::DisconnectPeer(who)))) => - self.network_service.lock().user_protocol_mut().disconnect_peer(&who), - - #[cfg(any(test, feature = "test-helpers"))] - Ok(Async::Ready(Some(NetworkMsg::Synchronized))) => {} - - Ok(Async::Ready(None)) | Err(_) => return Ok(Async::Ready(())), + self.network_service.user_protocol_mut().add_on_demand_request(rq); } } loop { - let msg = match self.protocol_rx.poll() { + // Process the next message coming from the `NetworkService`. + let msg = match self.from_worker.poll() { Ok(Async::Ready(Some(msg))) => msg, Ok(Async::Ready(None)) | Err(_) => return Ok(Async::Ready(())), Ok(Async::NotReady) => break, }; - let mut network_service = self.network_service.lock(); - match msg { - ProtocolMsg::BlockImported(hash, header) => - network_service.user_protocol_mut().on_block_imported(hash, &header), - ProtocolMsg::BlockFinalized(hash, header) => - network_service.user_protocol_mut().on_block_finalized(hash, &header), - ProtocolMsg::ExecuteWithSpec(task) => { - let (protocol, mut net_out) = network_service.user_protocol_mut().protocol_context_lock(); - let (mut context, spec) = protocol.specialization_lock(&mut net_out); - task.call_box(spec, &mut context); + ServerToWorkerMsg::ExecuteWithSpec(task) => { + let protocol = self.network_service.user_protocol_mut(); + let (mut context, spec) = protocol.specialization_lock(); + task(spec, &mut context); }, - ProtocolMsg::ExecuteWithGossip(task) => { - let (protocol, mut net_out) = network_service.user_protocol_mut().protocol_context_lock(); - let (mut context, gossip) = protocol.consensus_gossip_lock(&mut net_out); - task.call_box(gossip, &mut context); + ServerToWorkerMsg::ExecuteWithGossip(task) => { + let protocol = self.network_service.user_protocol_mut(); + let (mut context, gossip) = protocol.consensus_gossip_lock(); + task(gossip, &mut context); } - ProtocolMsg::GossipConsensusMessage(topic, engine_id, message, recipient) => - network_service.user_protocol_mut().gossip_consensus_message(topic, engine_id, message, recipient), - ProtocolMsg::BlocksProcessed(hashes, has_error) => - network_service.user_protocol_mut().blocks_processed(hashes, has_error), - ProtocolMsg::RestartSync => - network_service.user_protocol_mut().restart(), - ProtocolMsg::AnnounceBlock(hash) => - network_service.user_protocol_mut().announce_block(hash), - ProtocolMsg::BlockImportedSync(hash, number) => - network_service.user_protocol_mut().block_imported(&hash, number), - ProtocolMsg::ClearJustificationRequests => - network_service.user_protocol_mut().clear_justification_requests(), - ProtocolMsg::RequestJustification(hash, number) => - network_service.user_protocol_mut().request_justification(&hash, number), - ProtocolMsg::JustificationImportResult(hash, number, success) => - network_service.user_protocol_mut().justification_import_result(hash, number, success), - ProtocolMsg::SetFinalityProofRequestBuilder(builder) => - network_service.user_protocol_mut().set_finality_proof_request_builder(builder), - ProtocolMsg::RequestFinalityProof(hash, number) => - network_service.user_protocol_mut().request_finality_proof(&hash, number), - ProtocolMsg::FinalityProofImportResult(requested_block, finalziation_result) => - network_service.user_protocol_mut() - .finality_proof_import_result(requested_block, finalziation_result), - ProtocolMsg::PropagateExtrinsics => - network_service.user_protocol_mut().propagate_extrinsics(), - #[cfg(any(test, feature = "test-helpers"))] - ProtocolMsg::Tick => network_service.user_protocol_mut().tick(), - #[cfg(any(test, feature = "test-helpers"))] - ProtocolMsg::Synchronize => {}, + ServerToWorkerMsg::GossipConsensusMessage(topic, engine_id, message, recipient) => + self.network_service.user_protocol_mut().gossip_consensus_message(topic, engine_id, message, recipient), + ServerToWorkerMsg::AnnounceBlock(hash) => + self.network_service.user_protocol_mut().announce_block(hash), + ServerToWorkerMsg::RequestJustification(hash, number) => + self.network_service.user_protocol_mut().request_justification(&hash, number), + ServerToWorkerMsg::PropagateExtrinsics => + self.network_service.user_protocol_mut().propagate_extrinsics(), + ServerToWorkerMsg::GetValue(key) => + self.network_service.get_value(&key), + ServerToWorkerMsg::PutValue(key, value) => + self.network_service.put_value(key, value), + ServerToWorkerMsg::AddKnownAddress(peer_id, addr) => + self.network_service.add_known_address(peer_id, addr), } } loop { - let mut network_service = self.network_service.lock(); - let poll_value = network_service.poll(); + // Process the next action coming from the network. + let poll_value = self.network_service.poll(); let outcome = match poll_value { Ok(Async::NotReady) => break, - Ok(Async::Ready(Some(outcome))) => outcome, + 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)); + CustomMessageOutcome::None + }, Ok(Async::Ready(None)) => CustomMessageOutcome::None, Err(err) => { error!(target: "sync", "Error in the network: {:?}", err); @@ -763,9 +659,13 @@ impl, H: ExHashT> Future for Ne } } - let mut network_service = self.network_service.lock(); - self.is_offline.store(network_service.user_protocol_mut().num_connected_peers() == 0, Ordering::Relaxed); - self.is_major_syncing.store(match network_service.user_protocol_mut().sync_state() { + // Update the variables shared with the `NetworkService`. + self.num_connected.store(self.network_service.user_protocol_mut().num_connected_peers(), Ordering::Relaxed); + { + let external_addresses = Swarm::::external_addresses(&self.network_service).cloned().collect(); + *self.external_addresses.lock() = external_addresses; + } + self.is_major_syncing.store(match self.network_service.user_protocol_mut().sync_state() { SyncState::Idle => false, SyncState::Downloading => true, }, Ordering::Relaxed); @@ -777,5 +677,49 @@ impl, H: ExHashT> Future for Ne /// The libp2p swarm, customized for our needs. type Swarm = libp2p::core::Swarm< Boxed<(PeerId, StreamMuxerBox), io::Error>, - Behaviour, CustomMessageOutcome, Substream> + Behaviour >; + +// Implementation of `import_queue::Link` trait using the available local variables. +struct NetworkLink<'a, B: BlockT, S: NetworkSpecialization, H: ExHashT> { + protocol: &'a mut Swarm, +} + +impl<'a, B: BlockT, S: NetworkSpecialization, H: ExHashT> Link for NetworkLink<'a, B, S, H> { + fn blocks_processed( + &mut self, + imported: usize, + count: usize, + results: Vec<(Result>, BlockImportError>, B::Hash)> + ) { + self.protocol.user_protocol_mut().blocks_processed(imported, count, results) + } + fn justification_imported(&mut self, who: PeerId, hash: &B::Hash, number: NumberFor, success: bool) { + self.protocol.user_protocol_mut().justification_import_result(hash.clone(), number, success); + if !success { + info!("Invalid justification provided by {} for #{}", who, hash); + self.protocol.user_protocol_mut().disconnect_peer(&who); + self.protocol.user_protocol_mut().report_peer(who, i32::min_value()); + } + } + fn request_justification(&mut self, hash: &B::Hash, number: NumberFor) { + self.protocol.user_protocol_mut().request_justification(hash, number) + } + fn request_finality_proof(&mut self, hash: &B::Hash, number: NumberFor) { + self.protocol.user_protocol_mut().request_finality_proof(hash, number) + } + fn finality_proof_imported( + &mut self, + who: PeerId, + request_block: (B::Hash, NumberFor), + finalization_result: Result<(B::Hash, NumberFor), ()>, + ) { + let success = finalization_result.is_ok(); + self.protocol.user_protocol_mut().finality_proof_import_result(request_block, finalization_result); + if !success { + info!("Invalid finality proof provided by {} for #{}", who, request_block.0); + self.protocol.user_protocol_mut().disconnect_peer(&who); + self.protocol.user_protocol_mut().report_peer(who, i32::min_value()); + } + } +} diff --git a/core/network/src/test/block_import.rs b/core/network/src/test/block_import.rs index b5a03ae23a5a3be53c6404252024b55c77b14da1..eb49dbda7ae433f348059aa45bd6058e937052a6 100644 --- a/core/network/src/test/block_import.rs +++ b/core/network/src/test/block_import.rs @@ -16,16 +16,14 @@ //! Testing block import logic. -use consensus::import_queue::{import_single_block, BasicQueue, BlockImportError, BlockImportResult}; +use consensus::import_queue::{ + import_single_block, IncomingBlock, BasicQueue, BlockImportError, BlockImportResult +}; use test_client::{self, prelude::*}; use test_client::runtime::{Block, Hash}; use runtime_primitives::generic::BlockId; use super::*; -struct TestLink {} - -impl Link for TestLink {} - fn prepare_good_block() -> (TestClient, Hash, u64, PeerId, IncomingBlock) { let client = test_client::new(); let block = client.new_block(Default::default()).unwrap().bake().unwrap(); @@ -47,29 +45,30 @@ fn prepare_good_block() -> (TestClient, Hash, u64, PeerId, IncomingBlock) #[test] fn import_single_good_block_works() { let (_, _hash, number, peer_id, block) = prepare_good_block(); - assert_eq!( - import_single_block(&test_client::new(), BlockOrigin::File, block, Arc::new(PassThroughVerifier(true))), - Ok(BlockImportResult::ImportedUnknown(number, Default::default(), Some(peer_id))) - ); + match import_single_block(&mut test_client::new(), BlockOrigin::File, block, Arc::new(PassThroughVerifier(true))) { + Ok(BlockImportResult::ImportedUnknown(ref num, ref aux, ref org)) + if *num == number && *aux == Default::default() && *org == Some(peer_id) => {} + _ => panic!() + } } #[test] fn import_single_good_known_block_is_ignored() { - let (client, _hash, number, _, block) = prepare_good_block(); - assert_eq!( - import_single_block(&client, BlockOrigin::File, block, Arc::new(PassThroughVerifier(true))), - Ok(BlockImportResult::ImportedKnown(number)) - ); + let (mut client, _hash, number, _, block) = prepare_good_block(); + match import_single_block(&mut client, BlockOrigin::File, block, Arc::new(PassThroughVerifier(true))) { + Ok(BlockImportResult::ImportedKnown(ref n)) if *n == number => {} + _ => panic!() + } } #[test] fn import_single_good_block_without_header_fails() { let (_, _, _, peer_id, mut block) = prepare_good_block(); block.header = None; - assert_eq!( - import_single_block(&test_client::new(), BlockOrigin::File, block, Arc::new(PassThroughVerifier(true))), - Err(BlockImportError::IncompleteHeader(Some(peer_id))) - ); + match import_single_block(&mut test_client::new(), BlockOrigin::File, block, Arc::new(PassThroughVerifier(true))) { + Err(BlockImportError::IncompleteHeader(ref org)) if *org == Some(peer_id) => {} + _ => panic!() + } } #[test] @@ -77,7 +76,7 @@ fn async_import_queue_drops() { // Perform this test multiple times since it exhibits non-deterministic behavior. for _ in 0..100 { let verifier = Arc::new(PassThroughVerifier(true)); - let mut queue = BasicQueue::new(verifier, Arc::new(test_client::new()), None, None, None); + let queue = BasicQueue::new(verifier, Box::new(test_client::new()), None, None); drop(queue); } } diff --git a/core/network/src/test/mod.rs b/core/network/src/test/mod.rs index 5bb48c77366b74a777daaec5914199789e896bbd..177aa2e2fde0a05002c9e52ded38a2e4ed86be6e 100644 --- a/core/network/src/test/mod.rs +++ b/core/network/src/test/mod.rs @@ -21,40 +21,40 @@ mod block_import; #[cfg(test)] mod sync; -use std::collections::{HashMap, HashSet, VecDeque}; +use std::collections::HashMap; use std::sync::Arc; -use crate::AlwaysBadChecker; +use crate::config::build_multiaddr; use log::trace; use crate::chain::FinalityProofProvider; use client::{ - self, ClientInfo, BlockchainEvents, FinalityNotifications, + self, ClientInfo, BlockchainEvents, BlockImportNotification, + FinalityNotifications, FinalityNotification, well_known_cache_keys::{self, Id as CacheKeyId}, }; use client::{in_mem::Backend as InMemoryBackend, error::Result as ClientResult}; use client::block_builder::BlockBuilder; use client::backend::AuxStore; use crate::config::Roles; -use consensus::import_queue::{BasicQueue, ImportQueue, IncomingBlock}; +use consensus::import_queue::BasicQueue; use consensus::import_queue::{ - Link, SharedBlockImport, SharedJustificationImport, Verifier, SharedFinalityProofImport, - SharedFinalityProofRequestBuilder, + BoxBlockImport, BoxJustificationImport, Verifier, BoxFinalityProofImport, }; +use consensus::block_import::{BlockImport, ImportResult}; use consensus::Error as ConsensusError; -use consensus::{BlockOrigin, ForkChoiceStrategy, ImportBlock, JustificationImport}; -use crate::consensus_gossip::{ConsensusGossip, MessageRecipient as GossipMessageRecipient, TopicNotification}; -use futures::{prelude::*, sync::{mpsc, oneshot}}; -use log::info; -use crate::message::Message; +use consensus::{BlockOrigin, ForkChoiceStrategy, BlockImportParams, JustificationImport}; +use futures::prelude::*; +use futures03::{StreamExt as _, TryStreamExt as _}; +use crate::{NetworkWorker, NetworkService, config::ProtocolId}; +use crate::config::{NetworkConfiguration, TransportConfig, BoxFinalityProofRequestBuilder}; use libp2p::PeerId; -use parking_lot::{Mutex, RwLock}; +use parking_lot::Mutex; use primitives::{H256, Blake2Hasher}; -use crate::SyncState; -use crate::protocol::{Context, Protocol, ProtocolConfig, CustomMessageOutcome, NetworkOut}; +use crate::protocol::{Context, ProtocolConfig}; use runtime_primitives::generic::{BlockId, OpaqueDigestItemId}; use runtime_primitives::traits::{Block as BlockT, Header, NumberFor}; -use runtime_primitives::{Justification, ConsensusEngineId}; -use crate::service::{NetworkMsg, ProtocolMsg, TransactionPool}; +use runtime_primitives::Justification; +use crate::service::TransactionPool; use crate::specialization::NetworkSpecialization; use test_client::{self, AccountKeyring}; @@ -77,14 +77,14 @@ impl Verifier for PassThroughVerifier { header: B::Header, justification: Option, body: Option> - ) -> Result<(ImportBlock, Option)>>), String> { + ) -> Result<(BlockImportParams, Option)>>), String> { let maybe_keys = header.digest() .log(|l| l.try_as_raw(OpaqueDigestItemId::Consensus(b"aura")) .or_else(|| l.try_as_raw(OpaqueDigestItemId::Consensus(b"babe"))) ) .map(|blob| vec![(well_known_cache_keys::AUTHORITIES, blob.to_vec())]); - Ok((ImportBlock { + Ok((BlockImportParams { origin, header, body, @@ -97,84 +97,6 @@ impl Verifier for PassThroughVerifier { } } -/// A link implementation that does nothing. -pub struct NoopLink { } - -impl Link for NoopLink { } - -/// A link implementation that connects to the network. -#[derive(Clone)] -pub struct NetworkLink> { - /// The protocol sender - pub(crate) protocol_sender: mpsc::UnboundedSender>, - /// The network sender - pub(crate) network_sender: mpsc::UnboundedSender>, -} - -impl> Link for NetworkLink { - fn block_imported(&mut self, hash: &B::Hash, number: NumberFor) { - let _ = self.protocol_sender.unbounded_send(ProtocolMsg::BlockImportedSync(hash.clone(), number)); - } - - fn blocks_processed(&mut self, processed_blocks: Vec, has_error: bool) { - let _ = self.protocol_sender.unbounded_send(ProtocolMsg::BlocksProcessed(processed_blocks, has_error)); - } - - fn justification_imported(&mut self, who: PeerId, hash: &B::Hash, number: NumberFor, success: bool) { - let _ = self.protocol_sender.unbounded_send(ProtocolMsg::JustificationImportResult(hash.clone(), number, success)); - if !success { - info!("Invalid justification provided by {} for #{}", who, hash); - let _ = self.network_sender.unbounded_send(NetworkMsg::ReportPeer(who.clone(), i32::min_value())); - let _ = self.network_sender.unbounded_send(NetworkMsg::DisconnectPeer(who.clone())); - } - } - - fn clear_justification_requests(&mut self) { - let _ = self.protocol_sender.unbounded_send(ProtocolMsg::ClearJustificationRequests); - } - - fn request_justification(&mut self, hash: &B::Hash, number: NumberFor) { - let _ = self.protocol_sender.unbounded_send(ProtocolMsg::RequestJustification(hash.clone(), number)); - } - - fn request_finality_proof(&mut self, hash: &B::Hash, number: NumberFor) { - let _ = self.protocol_sender.unbounded_send(ProtocolMsg::RequestFinalityProof( - hash.clone(), - number, - )); - } - - fn finality_proof_imported( - &mut self, - who: PeerId, - request_block: (B::Hash, NumberFor), - finalization_result: Result<(B::Hash, NumberFor), ()>, - ) { - let success = finalization_result.is_ok(); - let _ = self.protocol_sender.unbounded_send(ProtocolMsg::FinalityProofImportResult( - request_block, - finalization_result, - )); - if !success { - info!("Invalid finality proof provided by {} for #{}", who, request_block.0); - let _ = self.network_sender.unbounded_send(NetworkMsg::ReportPeer(who.clone(), i32::min_value())); - let _ = self.network_sender.unbounded_send(NetworkMsg::DisconnectPeer(who.clone())); - } - } - - fn report_peer(&mut self, who: PeerId, reputation_change: i32) { - let _ = self.network_sender.unbounded_send(NetworkMsg::ReportPeer(who, reputation_change)); - } - - fn restart(&mut self) { - let _ = self.protocol_sender.unbounded_send(ProtocolMsg::RestartSync); - } - - fn set_finality_proof_request_builder(&mut self, request_builder: SharedFinalityProofRequestBuilder) { - let _ = self.protocol_sender.unbounded_send(ProtocolMsg::SetFinalityProofRequestBuilder(request_builder)); - } -} - /// The test specialization. #[derive(Clone)] pub struct DummySpecialization; @@ -197,7 +119,12 @@ impl NetworkSpecialization for DummySpecialization { &mut self, _ctx: &mut dyn Context, _peer_id: PeerId, - _message: &mut Option>, + _message: Vec, + ) {} + + fn on_event( + &mut self, + _event: crate::specialization::Event ) {} } @@ -220,10 +147,10 @@ impl PeersClient { } } - pub fn as_block_import(&self) -> SharedBlockImport { + pub fn as_block_import(&self) -> BoxBlockImport { match *self { - PeersClient::Full(ref client) => client.clone() as _, - PeersClient::Light(ref client) => client.clone() as _, + PeersClient::Full(ref client) => Box::new(client.clone()) as _, + PeersClient::Light(ref client) => Box::new(client.clone()) as _, } } @@ -284,427 +211,48 @@ impl PeersClient { } } -/// A Link that can wait for a block to have been imported. -pub struct TestLink> { - link: NetworkLink, - - #[cfg(any(test, feature = "test-helpers"))] - network_to_protocol_sender: mpsc::UnboundedSender>, -} - -impl> TestLink { - fn new( - protocol_sender: mpsc::UnboundedSender>, - _network_to_protocol_sender: mpsc::UnboundedSender>, - network_sender: mpsc::UnboundedSender> - ) -> TestLink { - TestLink { - #[cfg(any(test, feature = "test-helpers"))] - network_to_protocol_sender: _network_to_protocol_sender, - link: NetworkLink { - protocol_sender, - network_sender, - } - } - } -} - -impl> Link for TestLink { - fn block_imported(&mut self, hash: &Hash, number: NumberFor) { - self.link.block_imported(hash, number); - } - - fn blocks_processed(&mut self, processed_blocks: Vec, has_error: bool) { - self.link.blocks_processed(processed_blocks, has_error); - } - - fn justification_imported(&mut self, who: PeerId, hash: &Hash, number:NumberFor, success: bool) { - self.link.justification_imported(who, hash, number, success); - } - - fn request_justification(&mut self, hash: &Hash, number: NumberFor) { - self.link.request_justification(hash, number); - } - - fn finality_proof_imported( - &mut self, - who: PeerId, - request_block: (Hash, NumberFor), - finalization_result: Result<(Hash, NumberFor), ()>, - ) { - self.link.finality_proof_imported(who, request_block, finalization_result); - } - - fn request_finality_proof(&mut self, hash: &Hash, number: NumberFor) { - self.link.request_finality_proof(hash, number); - } - - fn set_finality_proof_request_builder(&mut self, request_builder: SharedFinalityProofRequestBuilder) { - self.link.set_finality_proof_request_builder(request_builder); - } - - fn report_peer(&mut self, who: PeerId, reputation_change: i32) { - self.link.report_peer(who, reputation_change); - } - - fn restart(&mut self) { - self.link.restart(); - } - - /// Send synchronization request to the block import channel. - /// - /// The caller should wait for the `Link::synchronized` call to ensure that it has synchronized - /// with `ImportQueue`. - #[cfg(any(test, feature = "test-helpers"))] - fn synchronized(&mut self) { - drop(self.network_to_protocol_sender.unbounded_send(FromNetworkMsg::Synchronize)) - } -} - pub struct Peer> { - peer_id: PeerId, - client: PeersClient, - net_proto_channel: ProtocolChannel, - /// This field is used only in test code, but maintaining different - /// instantiation paths or field names is too much hassle, hence - /// we allow it to be unused. - #[cfg_attr(not(test), allow(unused))] - /// `(is_offline, is_major_syncing, num_peers)` - protocol_status: Arc>, - import_queue: Arc>>>, pub data: D, - best_hash: Mutex>, - finalized_hash: Mutex>, -} - -type MessageFilter = dyn Fn(&NetworkMsg) -> bool; - -pub enum FromNetworkMsg { - /// A peer connected. - PeerConnected(PeerId), - /// A peer disconnected. - PeerDisconnected(PeerId), - /// A custom message from another peer. - CustomMessage(PeerId, Message), - /// Synchronization request. - Synchronize, -} - -struct ProtocolChannel> { - /// If true, we expect a tokio executor to be available. If false, we spawn our own. - use_tokio: bool, - buffered_messages: Mutex>>, - network_to_protocol_sender: mpsc::UnboundedSender>, - client_to_protocol_sender: mpsc::UnboundedSender>, - protocol_to_network_receiver: Mutex>>, -} - -impl> ProtocolChannel { - /// Create new buffered network port. - pub fn new( - use_tokio: bool, - network_to_protocol_sender: mpsc::UnboundedSender>, - client_to_protocol_sender: mpsc::UnboundedSender>, - protocol_to_network_receiver: mpsc::UnboundedReceiver>, - ) -> Self { - ProtocolChannel { - use_tokio, - buffered_messages: Mutex::new(VecDeque::new()), - network_to_protocol_sender, - client_to_protocol_sender, - protocol_to_network_receiver: Mutex::new(protocol_to_network_receiver), - } - } - - /// Send message from network to protocol. - pub fn send_from_net(&self, message: FromNetworkMsg) { - let _ = self.network_to_protocol_sender.unbounded_send(message); - - let _ = self.network_to_protocol_sender.unbounded_send(FromNetworkMsg::Synchronize); - let _ = self.wait_sync(); - } - - /// Send message from client to protocol. - pub fn send_from_client(&self, message: ProtocolMsg) { - let _ = self.client_to_protocol_sender.unbounded_send(message); - - let _ = self.client_to_protocol_sender.unbounded_send(ProtocolMsg::Synchronize); - let _ = self.wait_sync(); - } - - /// Wait until synchronization response is generated by the protocol. - pub fn wait_sync(&self) -> Result<(), ()> { - let fut = futures::future::poll_fn(|| { - loop { - let mut protocol_to_network_receiver = self.protocol_to_network_receiver.lock(); - match protocol_to_network_receiver.poll() { - Ok(Async::Ready(Some(NetworkMsg::Synchronized))) => return Ok(Async::Ready(())), - Ok(Async::Ready(None)) | Err(_) => return Err(()), - Ok(Async::NotReady) => return Ok(Async::NotReady), - Ok(Async::Ready(Some(msg))) => self.buffered_messages.lock().push_back(msg), - } - } - }); - - if self.use_tokio { - fut.wait() - } else { - tokio::runtime::current_thread::block_on_all(fut) - } - } - - /// Produce the next pending message to send to another peer. - fn pending_message(&self, message_filter: &MessageFilter) -> Option> { - if let Some(message) = self.buffered_message(message_filter) { - return Some(message); - } - - while let Some(message) = self.channel_message() { - if message_filter(&message) { - return Some(message) - } else { - self.buffered_messages.lock().push_back(message); - } - } - - None - } - - /// Whether this peer is done syncing (has no messages to send). - fn is_done(&self) -> bool { - let mut buffered_messages = self.buffered_messages.lock(); - if let Some(msg) = self.channel_message() { - buffered_messages.push_back(msg); - false - } else { - buffered_messages.is_empty() - } - } - - /// Return oldest buffered message if it exists. - fn buffered_message(&self, message_filter: &MessageFilter) -> Option> { - let mut buffered_messages = self.buffered_messages.lock(); - for i in 0..buffered_messages.len() { - if message_filter(&buffered_messages[i]) { - return buffered_messages.remove(i); - } - } - - None - } - - /// Receive message from the channel. - fn channel_message(&self) -> Option> { - let fut = futures::future::poll_fn(|| -> Result<_, ()> { - Ok(Async::Ready(match self.protocol_to_network_receiver.lock().poll() { - Ok(Async::Ready(Some(m))) => Some(m), - Ok(Async::NotReady) => None, - Err(_) => None, - Ok(Async::Ready(None)) => None, - })) - }); - - if self.use_tokio { - fut.wait() - } else { - tokio::runtime::current_thread::block_on_all(fut) - }.ok().and_then(|a| a) - } + client: PeersClient, + /// We keep a copy of the verifier so that we can invoke it for locally-generated blocks, + /// instead of going through the import queue. + verifier: Arc>, + /// We keep a copy of the block_import so that we can invoke it for locally-generated blocks, + /// instead of going through the import queue. + block_import: Box>, + network: NetworkWorker::Hash>, + imported_blocks_stream: Box, Error = ()> + Send>, + finality_notification_stream: Box, Error = ()> + Send>, } impl> Peer { - fn new( - protocol_status: Arc>, - client: PeersClient, - import_queue: Arc>>>, - use_tokio: bool, - network_to_protocol_sender: mpsc::UnboundedSender>, - protocol_sender: mpsc::UnboundedSender>, - _network_sender: mpsc::UnboundedSender>, - network_port: mpsc::UnboundedReceiver>, - data: D, - ) -> Self { - let net_proto_channel = ProtocolChannel::new( - use_tokio, - network_to_protocol_sender.clone(), - protocol_sender.clone(), - network_port, - ); - Peer { - protocol_status, - peer_id: PeerId::random(), - client, - import_queue, - net_proto_channel, - data, - best_hash: Mutex::new(None), - finalized_hash: Mutex::new(None), - } - } - /// Called after blockchain has been populated to updated current state. - fn start(&self) { - // Update the sync state to the latest chain state. - let info = self.client.info(); - let header = self - .client - .header(&BlockId::Hash(info.chain.best_hash)) - .unwrap() - .unwrap(); - self.net_proto_channel.send_from_client(ProtocolMsg::BlockImported(info.chain.best_hash, header)); - } - - #[cfg(test)] - fn on_block_imported( - &self, - hash: ::Hash, - header: &::Header, - ) { - self.net_proto_channel.send_from_client(ProtocolMsg::BlockImported(hash, header.clone())); - } - - /// SyncOracle: are we connected to any peer? - #[cfg(test)] - fn is_offline(&self) -> bool { - self.protocol_status.read().0 - } - - /// SyncOracle: are we in the process of catching-up with the chain? - #[cfg(test)] - fn is_major_syncing(&self) -> bool { - self.protocol_status.read().1 - } - - /// Get protocol status. - #[cfg(test)] - fn num_peers(&self) -> usize { - self.protocol_status.read().2 - } - - /// Called on connection to other indicated peer. - fn on_connect(&self, other: &Self) { - self.net_proto_channel.send_from_net(FromNetworkMsg::PeerConnected(other.peer_id.clone())); - } - - /// Called on disconnect from other indicated peer. - fn on_disconnect(&self, other: &Self) { - self.net_proto_channel.send_from_net(FromNetworkMsg::PeerDisconnected(other.peer_id.clone())); - } - - /// Receive a message from another peer. Return a set of peers to disconnect. - fn receive_message(&self, from: &PeerId, msg: Message) { - self.net_proto_channel.send_from_net(FromNetworkMsg::CustomMessage(from.clone(), msg)); - } - - /// Produce the next pending message to send to another peer. - fn pending_message(&self, message_filter: &MessageFilter) -> Option> { - self.net_proto_channel.pending_message(message_filter) + /// Returns true if we're major syncing. + pub fn is_major_syncing(&self) -> bool { + self.network.service().is_major_syncing() } - /// Whether this peer is done syncing (has no messages to send). - fn is_done(&self) -> bool { - self.net_proto_channel.is_done() + /// Returns the number of peers we're connected to. + pub fn num_peers(&self) -> usize { + self.network.num_connected_peers() } - /// Synchronize with import queue. - #[cfg(any(test, feature = "test-helpers"))] - pub fn import_queue_sync(&self) { - self.import_queue.lock().synchronize(); - let _ = self.net_proto_channel.wait_sync(); + /// Returns true if we have no peer. + pub fn is_offline(&self) -> bool { + self.num_peers() == 0 } - /// Execute a "sync step". This is called for each peer after it sends a packet. - fn sync_step(&self) { - self.net_proto_channel.send_from_client(ProtocolMsg::Tick); - } - - /// Send block import notifications. - fn send_import_notifications(&self) { - let info = self.client.info(); - - let mut best_hash = self.best_hash.lock(); - match *best_hash { - None => {}, - Some(hash) if hash != info.chain.best_hash => {}, - _ => return, - } - - let header = self.client.header(&BlockId::Hash(info.chain.best_hash)).unwrap().unwrap(); - self.net_proto_channel.send_from_client(ProtocolMsg::BlockImported(info.chain.best_hash, header)); - *best_hash = Some(info.chain.best_hash); - } - - /// Send block finalization notifications. - fn send_finality_notifications(&self) { - let info = self.client.info(); - - let mut finalized_hash = self.finalized_hash.lock(); - match *finalized_hash { - None => {}, - Some(hash) if hash != info.chain.finalized_hash => {}, - _ => return, - } - - let header = self.client.header(&BlockId::Hash(info.chain.finalized_hash)).unwrap().unwrap(); - self.net_proto_channel.send_from_client( - ProtocolMsg::BlockFinalized(info.chain.finalized_hash, header.clone()) - ); - *finalized_hash = Some(info.chain.finalized_hash); - } - - /// Push a message into the gossip network and relay to peers. - /// `TestNet::sync_step` needs to be called to ensure it's propagated. - pub fn gossip_message( - &self, - topic: ::Hash, - engine_id: ConsensusEngineId, - data: Vec, - force: bool, - ) { - let recipient = if force { - GossipMessageRecipient::BroadcastToAll - } else { - GossipMessageRecipient::BroadcastNew - }; - self.net_proto_channel.send_from_client( - ProtocolMsg::GossipConsensusMessage(topic, engine_id, data, recipient), - ); - } - - /// access the underlying consensus gossip handler - pub fn consensus_gossip_messages_for( - &self, - engine_id: ConsensusEngineId, - topic: ::Hash, - ) -> mpsc::UnboundedReceiver { - let (tx, rx) = oneshot::channel(); - self.with_gossip(move |gossip, _| { - let inner_rx = gossip.messages_for(engine_id, topic); - let _ = tx.send(inner_rx); - }); - rx.wait().ok().expect("1. Network is running, 2. it should handle the above closure successfully") - } - - /// Execute a closure with the consensus gossip. - pub fn with_gossip(&self, f: F) - where F: FnOnce(&mut ConsensusGossip, &mut dyn Context) + Send + 'static - { - self.net_proto_channel.send_from_client(ProtocolMsg::ExecuteWithGossip(Box::new(f))); - } - - /// Announce a block to peers. - #[cfg(test)] - fn announce_block(&self, block: Hash) { - self.net_proto_channel.send_from_client(ProtocolMsg::AnnounceBlock(block)); + /// Request a justification for the given block. + pub fn request_justification(&self, hash: &::Hash, number: NumberFor) { + self.network.service().request_justification(hash, number); } - /// Request a justification for the given block. - #[cfg(test)] - fn request_justification(&self, hash: &::primitives::H256, number: NumberFor) { - self.net_proto_channel.send_from_client(ProtocolMsg::RequestJustification(hash.clone(), number)); + /// Announces an important block on the network. + pub fn announce_block(&self, hash: ::Hash) { + self.network.service().announce_block(hash); } /// Add blocks to the peer -- edit the block before adding - pub fn generate_blocks(&self, count: usize, origin: BlockOrigin, edit_block: F) -> H256 + pub fn generate_blocks(&mut self, count: usize, origin: BlockOrigin, edit_block: F) -> H256 where F: FnMut(BlockBuilder) -> Block { let best_hash = self.client.info().chain.best_hash; @@ -714,7 +262,7 @@ impl> Peer { /// Add blocks to the peer -- edit the block before adding. The chain will /// start at the given block iD. fn generate_blocks_at( - &self, + &mut self, at: BlockId, count: usize, origin: BlockOrigin, @@ -735,34 +283,35 @@ impl> Peer { block.header.parent_hash ); let header = block.header.clone(); - at = hash; - self.import_queue.lock().import_blocks( + let (import_block, cache) = self.verifier.verify( origin, - vec![IncomingBlock { - origin: None, - hash, - header: Some(header), - body: Some(block.extrinsics), - justification: None, - }], - ); - - // make sure block import has completed - self.import_queue_sync(); + header.clone(), + None, + Some(block.extrinsics) + ).unwrap(); + let cache = if let Some(cache) = cache { + cache.into_iter().collect() + } else { + Default::default() + }; + self.block_import.import_block(import_block, cache).expect("block_import failed"); + self.network.on_block_imported(hash, header); + at = hash; } + self.network.service().announce_block(at.clone()); at } /// Push blocks to the peer (simplified: with or without a TX) - pub fn push_blocks(&self, count: usize, with_tx: bool) -> H256 { + pub fn push_blocks(&mut self, count: usize, with_tx: bool) -> H256 { let best_hash = self.client.info().chain.best_hash; self.push_blocks_at(BlockId::Hash(best_hash), count, with_tx) } /// Push blocks to the peer (simplified: with or without a TX) starting from /// given hash. - pub fn push_blocks_at(&self, at: BlockId, count: usize, with_tx: bool) -> H256 { + pub fn push_blocks_at(&mut self, at: BlockId, count: usize, with_tx: bool) -> H256 { let mut nonce = 0; if with_tx { self.generate_blocks_at(at, count, BlockOrigin::File, |mut builder| { @@ -781,7 +330,7 @@ impl> Peer { } } - pub fn push_authorities_change_block(&self, new_authorities: Vec) -> H256 { + pub fn push_authorities_change_block(&mut self, new_authorities: Vec) -> H256 { self.generate_blocks(1, BlockOrigin::File, |mut builder| { builder.push(Extrinsic::AuthoritiesChange(new_authorities.clone())).unwrap(); builder.bake().unwrap() @@ -792,6 +341,11 @@ impl> Peer { pub fn client(&self) -> &PeersClient { &self.client } + + /// Get a reference to the network service. + pub fn network_service(&self) -> &Arc::Hash>> { + &self.network.service() + } } pub struct EmptyTransactionPool; @@ -818,6 +372,33 @@ impl SpecializationFactory for DummySpecialization { } } +/// Implements `BlockImport` on an `Arc>`. Used internally. Necessary to overcome the way the +/// `TestNet` trait is designed, more specifically `make_block_import` returning a `Box` makes it +/// impossible to clone the underlying object. +struct BlockImportAdapter(Arc>>); + +impl Clone for BlockImportAdapter { + fn clone(&self) -> Self { + BlockImportAdapter(self.0.clone()) + } +} + +impl> BlockImport for BlockImportAdapter { + type Error = T::Error; + + fn check_block(&mut self, hash: Hash, parent_hash: Hash) -> Result { + self.0.lock().check_block(hash, parent_hash) + } + + fn import_block( + &mut self, + block: BlockImportParams, + cache: HashMap>, + ) -> Result { + self.0.lock().import_block(block, cache) + } +} + pub trait TestNetFactory: Sized { type Specialization: NetworkSpecialization + SpecializationFactory; type Verifier: 'static + Verifier; @@ -828,20 +409,17 @@ pub trait TestNetFactory: Sized { fn make_verifier(&self, client: PeersClient, config: &ProtocolConfig) -> Arc; /// Get reference to peer. - fn peer(&self, i: usize) -> &Peer; - fn peers(&self) -> &Vec>>; - fn mut_peers>>)>(&mut self, closure: F); - - fn started(&self) -> bool; - fn set_started(&mut self, now: bool); + fn peer(&mut self, i: usize) -> &mut Peer; + fn peers(&self) -> &Vec>; + fn mut_peers>)>(&mut self, closure: F); /// Get custom block import handle for fresh client, along with peer data. fn make_block_import(&self, client: PeersClient) -> ( - SharedBlockImport, - Option>, - Option>, - Option>, + BoxBlockImport, + Option>, + Option>, + Option>, Self::PeerData, ) { @@ -857,11 +435,6 @@ pub trait TestNetFactory: Sized { ProtocolConfig::default() } - /// Must return true if the testnet is going to be used from within a tokio context. - fn uses_tokio(&self) -> bool { - false - } - /// Create new test network with this many peers. fn new(n: usize) -> Self { trace!(target: "test_network", "Creating test network"); @@ -875,218 +448,60 @@ pub trait TestNetFactory: Sized { net } - /// Add created peer. - fn add_peer( - &mut self, - protocol_status: Arc>, - import_queue: Arc>>>, - tx_pool: EmptyTransactionPool, - finality_proof_provider: Option>>, - mut protocol: Protocol, - protocol_sender: mpsc::UnboundedSender>, - network_to_protocol_sender: mpsc::UnboundedSender>, - network_sender: mpsc::UnboundedSender>, - mut network_to_protocol_rx: mpsc::UnboundedReceiver>, - mut protocol_rx: mpsc::UnboundedReceiver>, - peer: Arc>, - ) { - std::thread::spawn(move || { - // Implementation of `protocol::NetworkOut` using the available local variables. - struct Ctxt<'a, B: BlockT>(&'a mpsc::UnboundedSender>); - impl<'a, B: BlockT> NetworkOut for Ctxt<'a, B> { - fn report_peer(&mut self, who: PeerId, reputation: i32) { - let _ = self.0.unbounded_send(NetworkMsg::ReportPeer(who, reputation)); - } - fn disconnect_peer(&mut self, who: PeerId) { - let _ = self.0.unbounded_send(NetworkMsg::DisconnectPeer(who)); - } - fn send_message(&mut self, who: PeerId, message: Message) { - let _ = self.0.unbounded_send(NetworkMsg::Outgoing(who, message)); - } - } - - tokio::runtime::current_thread::run(futures::future::poll_fn(move || { - import_queue.lock().poll_actions(&mut TestLink::new( - protocol_sender.clone(), - network_to_protocol_sender.clone(), - network_sender.clone(), - )); - - while let Async::Ready(msg) = network_to_protocol_rx.poll().unwrap() { - let outcome = match msg { - Some(FromNetworkMsg::PeerConnected(peer_id)) => { - protocol.on_peer_connected(&mut Ctxt(&network_sender), peer_id); - CustomMessageOutcome::None - }, - Some(FromNetworkMsg::PeerDisconnected(peer_id)) => { - protocol.on_peer_disconnected(&mut Ctxt(&network_sender), peer_id); - CustomMessageOutcome::None - }, - Some(FromNetworkMsg::CustomMessage(peer_id, message)) => - protocol.on_custom_message( - &mut Ctxt(&network_sender), - &tx_pool, - peer_id, - message, - finality_proof_provider.as_ref().map(|p| &**p) - ), - Some(FromNetworkMsg::Synchronize) => { - let _ = network_sender.unbounded_send(NetworkMsg::Synchronized); - CustomMessageOutcome::None - }, - None => return Ok(Async::Ready(())), - }; - - match outcome { - CustomMessageOutcome::BlockImport(origin, blocks) => - import_queue.lock().import_blocks(origin, blocks), - CustomMessageOutcome::JustificationImport(origin, hash, nb, justification) => - import_queue.lock().import_justification(origin, hash, nb, justification), - CustomMessageOutcome::FinalityProofImport(origin, hash, nb, proof) => - import_queue.lock().import_finality_proof(origin, hash, nb, proof), - CustomMessageOutcome::None => {} - } - } - - loop { - let msg = match protocol_rx.poll() { - Ok(Async::Ready(Some(msg))) => msg, - Ok(Async::Ready(None)) | Err(_) => return Ok(Async::Ready(())), - Ok(Async::NotReady) => break, - }; - - match msg { - ProtocolMsg::BlockImported(hash, header) => - protocol.on_block_imported(&mut Ctxt(&network_sender), hash, &header), - ProtocolMsg::BlockFinalized(hash, header) => - protocol.on_block_finalized(&mut Ctxt(&network_sender), hash, &header), - ProtocolMsg::ExecuteWithSpec(task) => { - let mut ctxt = Ctxt(&network_sender); - let (mut context, spec) = protocol.specialization_lock(&mut ctxt); - task.call_box(spec, &mut context); - }, - ProtocolMsg::ExecuteWithGossip(task) => { - let mut ctxt = Ctxt(&network_sender); - let (mut context, gossip) = protocol.consensus_gossip_lock(&mut ctxt); - task.call_box(gossip, &mut context); - } - ProtocolMsg::GossipConsensusMessage(topic, engine_id, message, recipient) => - protocol.gossip_consensus_message( - &mut Ctxt(&network_sender), - topic, - engine_id, - message, - recipient - ), - ProtocolMsg::BlocksProcessed(hashes, has_error) => - protocol.blocks_processed(&mut Ctxt(&network_sender), hashes, has_error), - ProtocolMsg::RestartSync => - protocol.restart(&mut Ctxt(&network_sender)), - ProtocolMsg::AnnounceBlock(hash) => - protocol.announce_block(&mut Ctxt(&network_sender), hash), - ProtocolMsg::BlockImportedSync(hash, number) => - protocol.block_imported(&hash, number), - ProtocolMsg::ClearJustificationRequests => - protocol.clear_justification_requests(), - ProtocolMsg::RequestJustification(hash, number) => - protocol.request_justification(&mut Ctxt(&network_sender), &hash, number), - ProtocolMsg::JustificationImportResult(hash, number, success) => - protocol.justification_import_result(hash, number, success), - ProtocolMsg::SetFinalityProofRequestBuilder(builder) => - protocol.set_finality_proof_request_builder(builder), - ProtocolMsg::RequestFinalityProof(hash, number) => - protocol.request_finality_proof(&mut Ctxt(&network_sender), &hash, number), - ProtocolMsg::FinalityProofImportResult(requested_block, finalziation_result) => - protocol.finality_proof_import_result(requested_block, finalziation_result), - ProtocolMsg::PropagateExtrinsics => - protocol.propagate_extrinsics(&mut Ctxt(&network_sender), &tx_pool), - #[cfg(any(test, feature = "test-helpers"))] - ProtocolMsg::Tick => protocol.tick(&mut Ctxt(&network_sender)), - #[cfg(any(test, feature = "test-helpers"))] - ProtocolMsg::Synchronize => { - trace!(target: "sync", "handle_client_msg: received Synchronize msg"); - let _ = network_sender.unbounded_send(NetworkMsg::Synchronized); - } - } - } - - if let Async::Ready(_) = protocol.poll(&mut Ctxt(&network_sender), &tx_pool).unwrap() { - return Ok(Async::Ready(())) - } - - *protocol_status.write() = ( - protocol.num_connected_peers() == 0, - protocol.sync_state() == SyncState::Downloading, - protocol.num_connected_peers() - ); - Ok(Async::NotReady) - })); - }); - - if self.started() { - peer.start(); - self.peers().iter().for_each(|other| { - other.on_connect(&*peer); - peer.on_connect(other); - }); - } - - self.mut_peers(|peers| { - peers.push(peer) - }); - } - /// Add a full peer. fn add_full_peer(&mut self, config: &ProtocolConfig) { let client = Arc::new(test_client::new()); let verifier = self.make_verifier(PeersClient::Full(client.clone()), config); let (block_import, justification_import, finality_proof_import, finality_proof_request_builder, data) = self.make_block_import(PeersClient::Full(client.clone())); - let (network_sender, network_port) = mpsc::unbounded(); + let block_import = BlockImportAdapter(Arc::new(Mutex::new(block_import))); - let import_queue = Arc::new(Mutex::new(Box::new(BasicQueue::new( - verifier, - block_import, + let import_queue = Box::new(BasicQueue::new( + verifier.clone(), + Box::new(block_import.clone()), justification_import, finality_proof_import, + )); + + let listen_addr = build_multiaddr![Memory(rand::random::())]; + + let network = NetworkWorker::new(crate::config::Params { + roles: config.roles, + network_config: NetworkConfiguration { + listen_addresses: vec![listen_addr.clone()], + transport: TransportConfig::MemoryOnly, + ..NetworkConfiguration::default() + }, + chain: client.clone(), + finality_proof_provider: self.make_finality_proof_provider(PeersClient::Full(client.clone())), finality_proof_request_builder, - )))); - let specialization = self::SpecializationFactory::create(); - - let (network_to_protocol_sender, network_to_protocol_rx) = mpsc::unbounded(); - let (protocol_sender, protocol_rx) = mpsc::unbounded(); - - let protocol = Protocol::new( - config.clone(), - client.clone(), - Arc::new(AlwaysBadChecker), - specialization, - ).unwrap(); - - let protocol_status = Arc::new(RwLock::new((true, false, 0))); - self.add_peer( - protocol_status.clone(), - import_queue.clone(), - EmptyTransactionPool, - self.make_finality_proof_provider(PeersClient::Full(client.clone())), - protocol, - protocol_sender.clone(), - network_to_protocol_sender.clone(), - network_sender.clone(), - network_to_protocol_rx, - protocol_rx, - Arc::new(Peer::new( - protocol_status, - PeersClient::Full(client), - import_queue, - self.uses_tokio(), - network_to_protocol_sender, - protocol_sender, - network_sender, - network_port, + on_demand: None, + transaction_pool: Arc::new(EmptyTransactionPool), + protocol_id: ProtocolId::from(&b"test-protocol-name"[..]), + import_queue, + specialization: self::SpecializationFactory::create(), + }).unwrap(); + + self.mut_peers(|peers| { + for peer in peers.iter_mut() { + peer.network.add_known_address(network.service().local_peer_id(), listen_addr.clone()); + } + + let imported_blocks_stream = Box::new(client.import_notification_stream() + .map(|v| Ok::<_, ()>(v)).compat().fuse()); + let finality_notification_stream = Box::new(client.finality_notification_stream() + .map(|v| Ok::<_, ()>(v)).compat().fuse()); + + peers.push(Peer { data, - )), - ); + client: PeersClient::Full(client), + imported_blocks_stream, + finality_notification_stream, + block_import: Box::new(block_import), + verifier, + network, + }); + }); } /// Add a light peer. @@ -1098,188 +513,107 @@ pub trait TestNetFactory: Sized { let verifier = self.make_verifier(PeersClient::Light(client.clone()), &config); let (block_import, justification_import, finality_proof_import, finality_proof_request_builder, data) = self.make_block_import(PeersClient::Light(client.clone())); - let (network_sender, network_port) = mpsc::unbounded(); + let block_import = BlockImportAdapter(Arc::new(Mutex::new(block_import))); - let import_queue = Arc::new(Mutex::new(Box::new(BasicQueue::new( - verifier, - block_import, + let import_queue = Box::new(BasicQueue::new( + verifier.clone(), + Box::new(block_import.clone()), justification_import, finality_proof_import, + )); + + let listen_addr = build_multiaddr![Memory(rand::random::())]; + + let network = NetworkWorker::new(crate::config::Params { + roles: config.roles, + network_config: NetworkConfiguration { + listen_addresses: vec![listen_addr.clone()], + transport: TransportConfig::MemoryOnly, + ..NetworkConfiguration::default() + }, + chain: client.clone(), + finality_proof_provider: self.make_finality_proof_provider(PeersClient::Light(client.clone())), finality_proof_request_builder, - )))); - let specialization = self::SpecializationFactory::create(); - - let (network_to_protocol_sender, network_to_protocol_rx) = mpsc::unbounded(); - let (protocol_sender, protocol_rx) = mpsc::unbounded(); - - let protocol = Protocol::new( - config, - client.clone(), - Arc::new(AlwaysBadChecker), - specialization, - ).unwrap(); - - let protocol_status = Arc::new(RwLock::new((true, false, 0))); - self.add_peer( - protocol_status.clone(), - import_queue.clone(), - EmptyTransactionPool, - self.make_finality_proof_provider(PeersClient::Light(client.clone())), - protocol, - protocol_sender.clone(), - network_to_protocol_sender.clone(), - network_sender.clone(), - network_to_protocol_rx, - protocol_rx, - Arc::new(Peer::new( - protocol_status, - PeersClient::Light(client), - import_queue, - self.uses_tokio(), - network_to_protocol_sender, - protocol_sender, - network_sender, - network_port, - data, - )), - ); - } + on_demand: None, + transaction_pool: Arc::new(EmptyTransactionPool), + protocol_id: ProtocolId::from(&b"test-protocol-name"[..]), + import_queue, + specialization: self::SpecializationFactory::create(), + }).unwrap(); - /// Start network. - fn start(&mut self) { - if self.started() { - return; - } - for peer in self.peers() { - peer.start(); - for client in self.peers() { - if peer.peer_id != client.peer_id { - peer.on_connect(client); - } + self.mut_peers(|peers| { + for peer in peers.iter_mut() { + peer.network.add_known_address(network.service().local_peer_id(), listen_addr.clone()); } - } - loop { - // we only deliver Status messages during start - let need_continue = self.route_single(true, None, &|msg| match *msg { - NetworkMsg::Outgoing(_, crate::message::generic::Message::Status(_)) => true, - NetworkMsg::Outgoing(_, _) => false, - NetworkMsg::DisconnectPeer(_) | - NetworkMsg::ReportPeer(_, _) | NetworkMsg::Synchronized => true, - }); - if !need_continue { - break; - } - } + let imported_blocks_stream = Box::new(client.import_notification_stream() + .map(|v| Ok::<_, ()>(v)).compat().fuse()); + let finality_notification_stream = Box::new(client.finality_notification_stream() + .map(|v| Ok::<_, ()>(v)).compat().fuse()); - self.set_started(true); + peers.push(Peer { + data, + verifier, + block_import: Box::new(block_import), + client: PeersClient::Light(client), + imported_blocks_stream, + finality_notification_stream, + network, + }); + }); } - /// Do single round of message routing: single message from every peer is routed. - fn route_single( - &mut self, - disconnect: bool, - disconnected: Option>, - message_filter: &MessageFilter, - ) -> bool { - let mut had_messages = false; - let mut to_disconnect = HashSet::new(); - let peers = self.peers(); - for peer in peers { - if let Some(message) = peer.pending_message(message_filter) { - match message { - NetworkMsg::Outgoing(recipient_id, packet) => { - had_messages = true; - - let sender_pos = peers.iter().position(|p| p.peer_id == peer.peer_id).unwrap(); - let recipient_pos = peers.iter().position(|p| p.peer_id == recipient_id).unwrap(); - if disconnect { - if let Some(ref disconnected) = disconnected { - let mut current = HashSet::new(); - current.insert(sender_pos); - current.insert(recipient_pos); - // Not routing message between "disconnected" nodes. - if disconnected.is_subset(¤t) { - continue; - } - } - } - - peers[recipient_pos].receive_message(&peer.peer_id, packet); - }, - NetworkMsg::DisconnectPeer(who) => { - if disconnect { - to_disconnect.insert(who); - } - }, - _ => (), - } - } - } - - for d in to_disconnect { - if let Some(d) = peers.iter().find(|p| p.peer_id == d) { - for peer in 0..peers.len() { - peers[peer].on_disconnect(d); - } + /// Polls the testnet until all nodes are in sync. + /// + /// Must be executed in a task context. + fn poll_until_sync(&mut self) -> Async<()> { + self.poll(); + + // Return `NotReady` if there's a mismatch in the highest block number. + let mut highest = None; + for peer in self.peers().iter() { + match (highest, peer.client.info().chain.best_number) { + (None, b) => highest = Some(b), + (Some(ref a), ref b) if a == b => {}, + (Some(_), _) => return Async::NotReady, } } - - // make sure that the protocol(s) has processed all messages that have been queued - self.peers().iter().for_each(|peer| peer.import_queue_sync()); - - had_messages - } - - /// Send block import notifications for all peers. - fn send_import_notifications(&mut self) { - self.peers().iter().for_each(|peer| peer.send_import_notifications()) - } - - /// Send block finalization notifications for all peers. - fn send_finality_notifications(&mut self) { - self.peers().iter().for_each(|peer| peer.send_finality_notifications()) + Async::Ready(()) } - /// Perform synchronization until complete, if provided the - /// given nodes set are excluded from sync. - fn sync_with(&mut self, disconnect: bool, disconnected: Option>) { - self.start(); - while self.route_single(disconnect, disconnected.clone(), &|_| true) { - // give protocol a chance to do its maintain procedures - self.peers().iter().for_each(|peer| peer.sync_step()); - } - } - - /// Deliver at most 1 pending message from every peer. - fn sync_step(&mut self) { - self.route_single(true, None, &|_| true); - } - - /// Maintain sync for a peer. - fn tick_peer(&mut self, i: usize) { - self.peers()[i].sync_step(); + /// Blocks the current thread until we are sync'ed. + /// + /// Calls `poll_until_sync` repeatidely with the runtime passed as parameter. + fn block_until_sync(&mut self, runtime: &mut tokio::runtime::current_thread::Runtime) { + runtime.block_on(futures::future::poll_fn::<(), (), _>(|| Ok(self.poll_until_sync()))).unwrap(); } - /// Deliver pending messages until there are no more. - fn sync(&mut self) { - self.sync_with(true, None) - } + /// Polls the testnet. Processes all the pending actions and returns `NotReady`. + fn poll(&mut self) { + self.mut_peers(|peers| { + for peer in peers { + peer.network.poll().unwrap(); - /// Deliver pending messages until there are no more. Do not disconnect nodes. - fn sync_without_disconnects(&mut self) { - self.sync_with(false, None) - } + // We poll `imported_blocks_stream`. + while let Ok(Async::Ready(Some(notification))) = peer.imported_blocks_stream.poll() { + peer.network.on_block_imported(notification.hash, notification.header); + } - /// Whether all peers have no pending outgoing messages. - fn done(&self) -> bool { - self.peers().iter().all(|p| p.is_done()) + // We poll `finality_notification_stream`, but we only take the last event. + let mut last = None; + while let Ok(Async::Ready(Some(item))) = peer.finality_notification_stream.poll() { + last = Some(item); + } + if let Some(notification) = last { + peer.network.on_block_finalized(notification.hash, notification.header); + } + } + }); } } pub struct TestNet { - peers: Vec>>, - started: bool, + peers: Vec>, } impl TestNetFactory for TestNet { @@ -1291,7 +625,6 @@ impl TestNetFactory for TestNet { fn from_config(_config: &ProtocolConfig) -> Self { TestNet { peers: Vec::new(), - started: false } } @@ -1301,25 +634,17 @@ impl TestNetFactory for TestNet { Arc::new(PassThroughVerifier(false)) } - fn peer(&self, i: usize) -> &Peer<(), Self::Specialization> { - &self.peers[i] + fn peer(&mut self, i: usize) -> &mut Peer<(), Self::Specialization> { + &mut self.peers[i] } - fn peers(&self) -> &Vec>> { + fn peers(&self) -> &Vec> { &self.peers } - fn mut_peers>>)>(&mut self, closure: F) { + fn mut_peers>)>(&mut self, closure: F) { closure(&mut self.peers); } - - fn started(&self) -> bool { - self.started - } - - fn set_started(&mut self, new: bool) { - self.started = new; - } } pub struct ForceFinalized(PeersClient); @@ -1328,7 +653,7 @@ impl JustificationImport for ForceFinalized { type Error = ConsensusError; fn import_justification( - &self, + &mut self, hash: H256, _number: NumberFor, justification: Justification, @@ -1355,35 +680,27 @@ impl TestNetFactory for JustificationTestNet { self.0.make_verifier(client, config) } - fn peer(&self, i: usize) -> &Peer { + fn peer(&mut self, i: usize) -> &mut Peer { self.0.peer(i) } - fn peers(&self) -> &Vec>> { + fn peers(&self) -> &Vec> { self.0.peers() } - fn mut_peers>>)>(&mut self, closure: F) { + fn mut_peers>)>(&mut self, closure: F) { self.0.mut_peers(closure) } - fn started(&self) -> bool { - self.0.started() - } - - fn set_started(&mut self, new: bool) { - self.0.set_started(new) - } - fn make_block_import(&self, client: PeersClient) -> ( - SharedBlockImport, - Option>, - Option>, - Option>, + BoxBlockImport, + Option>, + Option>, + Option>, Self::PeerData, ) { - (client.as_block_import(), Some(Arc::new(ForceFinalized(client))), None, None, Default::default()) + (client.as_block_import(), Some(Box::new(ForceFinalized(client))), None, None, Default::default()) } } diff --git a/core/network/src/test/sync.rs b/core/network/src/test/sync.rs index 15b866c168b45baf29ecded770db553f98e439c9..f3a8f0c8ea4281cf1fdf054d892fc1441ab2a93d 100644 --- a/core/network/src/test/sync.rs +++ b/core/network/src/test/sync.rs @@ -16,13 +16,15 @@ use client::{backend::Backend, blockchain::HeaderBackend}; use crate::config::Roles; -use crate::message; use consensus::BlockOrigin; -use std::collections::HashSet; +use futures03::TryFutureExt as _; +use std::time::Duration; +use tokio::runtime::current_thread; use super::*; fn test_ancestor_search_when_common_is(n: usize) { let _ = ::env_logger::try_init(); + let mut runtime = current_thread::Runtime::new().unwrap(); let mut net = TestNet::new(3); net.peer(0).push_blocks(n, false); @@ -33,7 +35,7 @@ fn test_ancestor_search_when_common_is(n: usize) { net.peer(1).push_blocks(100, false); net.peer(2).push_blocks(100, false); - net.sync(); + net.block_until_sync(&mut runtime); assert!(net.peer(0).client.as_in_memory_backend().blockchain() .canon_equals_to(net.peer(1).client.as_in_memory_backend().blockchain())); } @@ -41,28 +43,24 @@ fn test_ancestor_search_when_common_is(n: usize) { #[test] fn sync_peers_works() { let _ = ::env_logger::try_init(); + let mut runtime = current_thread::Runtime::new().unwrap(); let mut net = TestNet::new(3); - net.sync(); - for peer in 0..3 { - // Assert peers is up to date. - assert_eq!(net.peer(peer).num_peers(), 2); - // And then disconnect. - for other in 0..3 { - if other != peer { - net.peer(peer).on_disconnect(net.peer(other)); + + runtime.block_on(futures::future::poll_fn::<(), (), _>(|| -> Result<_, ()> { + net.poll(); + for peer in 0..3 { + if net.peer(peer).num_peers() != 2 { + return Ok(Async::NotReady) } } - } - net.sync(); - // Now peers are disconnected. - for peer in 0..3 { - assert_eq!(net.peer(peer).num_peers(), 0); - } + Ok(Async::Ready(())) + })).unwrap(); } #[test] fn sync_cycle_from_offline_to_syncing_to_offline() { let _ = ::env_logger::try_init(); + let mut runtime = current_thread::Runtime::new().unwrap(); let mut net = TestNet::new(3); for peer in 0..3 { // Offline, and not major syncing. @@ -72,63 +70,92 @@ fn sync_cycle_from_offline_to_syncing_to_offline() { // Generate blocks. net.peer(2).push_blocks(100, false); - net.start(); - for peer in 0..3 { - // Online - assert!(!net.peer(peer).is_offline()); - if peer < 2 { - // Major syncing. - assert!(net.peer(peer).is_major_syncing()); - } - } - net.sync(); - for peer in 0..3 { - // All done syncing. - assert!(!net.peer(peer).is_major_syncing()); - } - // Now disconnect them all. - for peer in 0..3 { - for other in 0..3 { - if other != peer { - net.peer(peer).on_disconnect(net.peer(other)); + // Block until all nodes are online and nodes 0 and 1 and major syncing. + runtime.block_on(futures::future::poll_fn::<(), (), _>(|| -> Result<_, ()> { + net.poll(); + for peer in 0..3 { + // Online + if net.peer(peer).is_offline() { + return Ok(Async::NotReady) + } + if peer < 2 { + // Major syncing. + if !net.peer(peer).is_major_syncing() { + return Ok(Async::NotReady) + } } } - net.sync(); - assert!(net.peer(peer).is_offline()); - assert!(!net.peer(peer).is_major_syncing()); - } + Ok(Async::Ready(())) + })).unwrap(); + + // Block until all nodes are done syncing. + runtime.block_on(futures::future::poll_fn::<(), (), _>(|| -> Result<_, ()> { + net.poll(); + for peer in 0..3 { + if net.peer(peer).is_major_syncing() { + return Ok(Async::NotReady) + } + } + Ok(Async::Ready(())) + })).unwrap(); + + // Now drop nodes 1 and 2, and check that node 0 is offline. + net.peers.remove(2); + net.peers.remove(1); + runtime.block_on(futures::future::poll_fn::<(), (), _>(|| -> Result<_, ()> { + net.poll(); + if !net.peer(0).is_offline() { + Ok(Async::NotReady) + } else { + Ok(Async::Ready(())) + } + })).unwrap(); } #[test] fn syncing_node_not_major_syncing_when_disconnected() { let _ = ::env_logger::try_init(); + let mut runtime = current_thread::Runtime::new().unwrap(); let mut net = TestNet::new(3); // Generate blocks. net.peer(2).push_blocks(100, false); - net.start(); - net.sync_step(); - - // Peer 1 is major-syncing. - assert!(net.peer(1).is_major_syncing()); - - // Disconnect peer 1 form everyone else. - net.peer(1).on_disconnect(net.peer(0)); - net.peer(1).on_disconnect(net.peer(2)); - // Peer 1 is not major-syncing. - net.sync(); + // Check that we're not major syncing when disconnected. assert!(!net.peer(1).is_major_syncing()); + + // Check that we switch to major syncing. + runtime.block_on(futures::future::poll_fn::<(), (), _>(|| -> Result<_, ()> { + net.poll(); + if !net.peer(1).is_major_syncing() { + Ok(Async::NotReady) + } else { + Ok(Async::Ready(())) + } + })).unwrap(); + + // Destroy two nodes, and check that we switch to non-major syncing. + net.peers.remove(2); + net.peers.remove(0); + runtime.block_on(futures::future::poll_fn::<(), (), _>(|| -> Result<_, ()> { + net.poll(); + if net.peer(0).is_major_syncing() { + Ok(Async::NotReady) + } else { + Ok(Async::Ready(())) + } + })).unwrap(); } #[test] fn sync_from_two_peers_works() { let _ = ::env_logger::try_init(); + let mut runtime = current_thread::Runtime::new().unwrap(); let mut net = TestNet::new(3); net.peer(1).push_blocks(100, false); net.peer(2).push_blocks(100, false); - net.sync(); + net.block_until_sync(&mut runtime); assert!(net.peer(0).client.as_in_memory_backend().blockchain() .equals_to(net.peer(1).client.as_in_memory_backend().blockchain())); assert!(!net.peer(0).is_major_syncing()); @@ -137,11 +164,12 @@ fn sync_from_two_peers_works() { #[test] fn sync_from_two_peers_with_ancestry_search_works() { let _ = ::env_logger::try_init(); + let mut runtime = current_thread::Runtime::new().unwrap(); let mut net = TestNet::new(3); net.peer(0).push_blocks(10, true); net.peer(1).push_blocks(100, false); net.peer(2).push_blocks(100, false); - net.sync(); + net.block_until_sync(&mut runtime); assert!(net.peer(0).client.as_in_memory_backend().blockchain() .canon_equals_to(net.peer(1).client.as_in_memory_backend().blockchain())); } @@ -149,13 +177,14 @@ fn sync_from_two_peers_with_ancestry_search_works() { #[test] fn ancestry_search_works_when_backoff_is_one() { let _ = ::env_logger::try_init(); + let mut runtime = current_thread::Runtime::new().unwrap(); let mut net = TestNet::new(3); net.peer(0).push_blocks(1, false); net.peer(1).push_blocks(2, false); net.peer(2).push_blocks(2, false); - net.sync(); + net.block_until_sync(&mut runtime); assert!(net.peer(0).client.as_in_memory_backend().blockchain() .canon_equals_to(net.peer(1).client.as_in_memory_backend().blockchain())); } @@ -163,13 +192,14 @@ fn ancestry_search_works_when_backoff_is_one() { #[test] fn ancestry_search_works_when_ancestor_is_genesis() { let _ = ::env_logger::try_init(); + let mut runtime = current_thread::Runtime::new().unwrap(); let mut net = TestNet::new(3); net.peer(0).push_blocks(13, true); net.peer(1).push_blocks(100, false); net.peer(2).push_blocks(100, false); - net.sync(); + net.block_until_sync(&mut runtime); assert!(net.peer(0).client.as_in_memory_backend().blockchain() .canon_equals_to(net.peer(1).client.as_in_memory_backend().blockchain())); } @@ -191,9 +221,11 @@ fn ancestry_search_works_when_common_is_hundred() { #[test] fn sync_long_chain_works() { + let _ = ::env_logger::try_init(); + let mut runtime = current_thread::Runtime::new().unwrap(); let mut net = TestNet::new(2); net.peer(1).push_blocks(500, false); - net.sync(); + net.block_until_sync(&mut runtime); assert!(net.peer(0).client.as_in_memory_backend().blockchain() .equals_to(net.peer(1).client.as_in_memory_backend().blockchain())); } @@ -201,10 +233,11 @@ fn sync_long_chain_works() { #[test] fn sync_no_common_longer_chain_fails() { let _ = ::env_logger::try_init(); + let mut runtime = current_thread::Runtime::new().unwrap(); let mut net = TestNet::new(3); net.peer(0).push_blocks(20, true); net.peer(1).push_blocks(20, false); - net.sync(); + net.block_until_sync(&mut runtime); assert!(!net.peer(0).client.as_in_memory_backend().blockchain() .canon_equals_to(net.peer(1).client.as_in_memory_backend().blockchain())); } @@ -212,9 +245,10 @@ fn sync_no_common_longer_chain_fails() { #[test] fn sync_justifications() { let _ = ::env_logger::try_init(); + let mut runtime = current_thread::Runtime::new().unwrap(); let mut net = JustificationTestNet::new(3); net.peer(0).push_blocks(20, false); - net.sync(); + net.block_until_sync(&mut runtime); // there's currently no justification for block #10 assert_eq!(net.peer(0).client().justification(&BlockId::Number(10)).unwrap(), None); @@ -234,17 +268,26 @@ fn sync_justifications() { net.peer(1).request_justification(&h2.hash().into(), 15); net.peer(1).request_justification(&h3.hash().into(), 20); - net.sync(); + runtime.block_on(futures::future::poll_fn::<(), (), _>(|| { + net.poll(); - for height in (10..21).step_by(5) { - assert_eq!(net.peer(0).client().justification(&BlockId::Number(height)).unwrap(), Some(Vec::new())); - assert_eq!(net.peer(1).client().justification(&BlockId::Number(height)).unwrap(), Some(Vec::new())); - } + for height in (10..21).step_by(5) { + if net.peer(0).client().justification(&BlockId::Number(height)).unwrap() != Some(Vec::new()) { + return Ok(Async::NotReady); + } + if net.peer(1).client().justification(&BlockId::Number(height)).unwrap() != Some(Vec::new()) { + return Ok(Async::NotReady); + } + } + + Ok(Async::Ready(())) + })).unwrap(); } #[test] fn sync_justifications_across_forks() { let _ = ::env_logger::try_init(); + let mut runtime = current_thread::Runtime::new().unwrap(); let mut net = JustificationTestNet::new(3); // we push 5 blocks net.peer(0).push_blocks(5, false); @@ -254,24 +297,31 @@ fn sync_justifications_across_forks() { // peer 1 will only see the longer fork. but we'll request justifications // for both and finalize the small fork instead. - net.sync(); + net.block_until_sync(&mut runtime); net.peer(0).client().finalize_block(BlockId::Hash(f1_best), Some(Vec::new()), true).unwrap(); net.peer(1).request_justification(&f1_best, 10); net.peer(1).request_justification(&f2_best, 11); - net.sync(); + runtime.block_on(futures::future::poll_fn::<(), (), _>(|| { + net.poll(); - assert_eq!(net.peer(0).client().justification(&BlockId::Number(10)).unwrap(), Some(Vec::new())); - assert_eq!(net.peer(1).client().justification(&BlockId::Number(10)).unwrap(), Some(Vec::new())); + if net.peer(0).client().justification(&BlockId::Number(10)).unwrap() == Some(Vec::new()) && + net.peer(1).client().justification(&BlockId::Number(10)).unwrap() == Some(Vec::new()) + { + Ok(Async::Ready(())) + } else { + Ok(Async::NotReady) + } + })).unwrap(); } #[test] fn sync_after_fork_works() { let _ = ::env_logger::try_init(); + let mut runtime = current_thread::Runtime::new().unwrap(); let mut net = TestNet::new(3); - net.sync_step(); net.peer(0).push_blocks(30, false); net.peer(1).push_blocks(30, false); net.peer(2).push_blocks(30, false); @@ -285,7 +335,7 @@ fn sync_after_fork_works() { // peer 1 has the best chain let peer1_chain = net.peer(1).client.as_in_memory_backend().blockchain().clone(); - net.sync(); + net.block_until_sync(&mut runtime); assert!(net.peer(0).client.as_in_memory_backend().blockchain().canon_equals_to(&peer1_chain)); assert!(net.peer(1).client.as_in_memory_backend().blockchain().canon_equals_to(&peer1_chain)); assert!(net.peer(2).client.as_in_memory_backend().blockchain().canon_equals_to(&peer1_chain)); @@ -294,15 +344,15 @@ fn sync_after_fork_works() { #[test] fn syncs_all_forks() { let _ = ::env_logger::try_init(); + let mut runtime = current_thread::Runtime::new().unwrap(); let mut net = TestNet::new(4); - net.sync_step(); net.peer(0).push_blocks(2, false); net.peer(1).push_blocks(2, false); net.peer(0).push_blocks(2, true); net.peer(1).push_blocks(4, false); - net.sync(); + net.block_until_sync(&mut runtime); // Check that all peers have all of the blocks. assert_eq!(9, net.peer(0).client.as_in_memory_backend().blockchain().blocks_count()); assert_eq!(9, net.peer(1).client.as_in_memory_backend().blockchain().blocks_count()); @@ -311,13 +361,12 @@ fn syncs_all_forks() { #[test] fn own_blocks_are_announced() { let _ = ::env_logger::try_init(); + let mut runtime = current_thread::Runtime::new().unwrap(); let mut net = TestNet::new(3); - net.sync(); // connect'em + net.block_until_sync(&mut runtime); // connect'em net.peer(0).generate_blocks(1, BlockOrigin::Own, |builder| builder.bake().unwrap()); - let header = net.peer(0).client().header(&BlockId::Number(1)).unwrap().unwrap(); - net.peer(0).on_block_imported(header.hash(), &header); - net.sync(); + net.block_until_sync(&mut runtime); assert_eq!(net.peer(0).client.as_in_memory_backend().blockchain().info().best_number, 1); assert_eq!(net.peer(1).client.as_in_memory_backend().blockchain().info().best_number, 1); @@ -329,6 +378,7 @@ fn own_blocks_are_announced() { #[test] fn blocks_are_not_announced_by_light_nodes() { let _ = ::env_logger::try_init(); + let mut runtime = current_thread::Runtime::new().unwrap(); let mut net = TestNet::new(0); // full peer0 is connected to light peer @@ -336,35 +386,32 @@ fn blocks_are_not_announced_by_light_nodes() { let mut light_config = ProtocolConfig::default(); light_config.roles = Roles::LIGHT; net.add_full_peer(&ProtocolConfig::default()); - net.add_full_peer(&light_config); - net.add_full_peer(&ProtocolConfig::default()); + net.add_light_peer(&light_config); + // Sync between 0 and 1. net.peer(0).push_blocks(1, false); - net.peer(0).start(); - net.peer(1).start(); - net.peer(2).start(); - net.peer(0).on_connect(net.peer(1)); - net.peer(1).on_connect(net.peer(2)); - - // Only sync between 0 -> 1, and 1 -> 2 - let mut disconnected = HashSet::new(); - disconnected.insert(0); - disconnected.insert(2); - net.sync_with(true, Some(disconnected)); - - // peer 0 has the best chain - // peer 1 has the best chain - // peer 2 has genesis-chain only assert_eq!(net.peer(0).client.info().chain.best_number, 1); + net.block_until_sync(&mut runtime); assert_eq!(net.peer(1).client.info().chain.best_number, 1); - assert_eq!(net.peer(2).client.info().chain.best_number, 0); + + // Add another node and remove node 0. + net.add_full_peer(&ProtocolConfig::default()); + net.peers.remove(0); + + // Poll for a few seconds and make sure 1 and 2 (now 0 and 1) don't sync together. + let mut delay = futures_timer::Delay::new(Duration::from_secs(5)).compat(); + runtime.block_on(futures::future::poll_fn::<(), (), _>(|| { + net.poll(); + delay.poll().map_err(|_| ()) + })).unwrap(); + assert_eq!(net.peer(1).client.info().chain.best_number, 0); } #[test] fn can_sync_small_non_best_forks() { let _ = ::env_logger::try_init(); + let mut runtime = current_thread::Runtime::new().unwrap(); let mut net = TestNet::new(2); - net.sync_step(); net.peer(0).push_blocks(30, false); net.peer(1).push_blocks(30, false); @@ -381,7 +428,15 @@ fn can_sync_small_non_best_forks() { assert!(net.peer(0).client().header(&BlockId::Hash(small_hash)).unwrap().is_some()); assert!(net.peer(1).client().header(&BlockId::Hash(small_hash)).unwrap().is_none()); - net.sync(); + // poll until the two nodes connect, otherwise announcing the block will not work + runtime.block_on(futures::future::poll_fn::<(), (), _>(|| -> Result<_, ()> { + net.poll(); + if net.peer(0).num_peers() == 0 { + Ok(Async::NotReady) + } else { + Ok(Async::Ready(())) + } + })).unwrap(); // synchronization: 0 synced to longer chain and 1 didn't sync to small chain. @@ -391,17 +446,24 @@ fn can_sync_small_non_best_forks() { assert!(!net.peer(1).client().header(&BlockId::Hash(small_hash)).unwrap().is_some()); net.peer(0).announce_block(small_hash); - net.sync(); // after announcing, peer 1 downloads the block. - assert!(net.peer(0).client().header(&BlockId::Hash(small_hash)).unwrap().is_some()); - assert!(net.peer(1).client().header(&BlockId::Hash(small_hash)).unwrap().is_some()); + runtime.block_on(futures::future::poll_fn::<(), (), _>(|| -> Result<_, ()> { + net.poll(); + + assert!(net.peer(0).client().header(&BlockId::Hash(small_hash)).unwrap().is_some()); + if net.peer(1).client().header(&BlockId::Hash(small_hash)).unwrap().is_none() { + return Ok(Async::NotReady) + } + Ok(Async::Ready(())) + })).unwrap(); } #[test] fn can_not_sync_from_light_peer() { let _ = ::env_logger::try_init(); + let mut runtime = current_thread::Runtime::new().unwrap(); // given the network with 1 full nodes (#0) and 1 light node (#1) let mut net = TestNet::new(1); @@ -411,8 +473,7 @@ fn can_not_sync_from_light_peer() { net.peer(0).push_blocks(1, false); // and let the light client sync from this node - // (mind the #1 is disconnected && not syncing) - net.sync(); + net.block_until_sync(&mut runtime); // ensure #0 && #1 have the same best block let full0_info = net.peer(0).client.info().chain; @@ -421,52 +482,34 @@ fn can_not_sync_from_light_peer() { assert_eq!(light_info.best_number, 1); assert_eq!(light_info.best_hash, full0_info.best_hash); - // add new full client (#2) && sync without #0 + // add new full client (#2) && remove #0 net.add_full_peer(&Default::default()); - net.peer(1).on_connect(net.peer(2)); - net.peer(2).on_connect(net.peer(1)); - net.peer(1).announce_block(light_info.best_hash); - net.sync_with(true, Some(vec![0].into_iter().collect())); - - // ensure that the #2 has failed to sync block #1 - assert_eq!(net.peer(2).client.info().chain.best_number, 0); - // and that the #1 is still connected to #2 - // (because #2 has not tried to fetch block data from the #1 light node) - assert_eq!(net.peer(1).num_peers(), 2); - - // and now try to fetch block data from light peer #1 - // (this should result in disconnect) - net.peer(1).receive_message( - &net.peer(2).peer_id, - message::generic::Message::BlockRequest(message::generic::BlockRequest { - id: 0, - fields: message::BlockAttributes::HEADER, - from: message::FromBlock::Hash(light_info.best_hash), - to: None, - direction: message::Direction::Ascending, - max: Some(1), - }), - ); - net.sync(); - // check that light #1 has disconnected from #2 - assert_eq!(net.peer(1).num_peers(), 1); + net.peers.remove(0); + + // ensure that the #2 (now #1) fails to sync block #1 even after 5 seconds + let mut test_finished = futures_timer::Delay::new(Duration::from_secs(5)).compat(); + runtime.block_on(futures::future::poll_fn::<(), (), _>(|| -> Result<_, ()> { + net.poll(); + test_finished.poll().map_err(|_| ()) + })).unwrap(); } #[test] fn light_peer_imports_header_from_announce() { let _ = ::env_logger::try_init(); + let mut runtime = current_thread::Runtime::new().unwrap(); - fn import_with_announce(net: &mut TestNet, hash: H256) { - let header = net.peer(0).client().header(&BlockId::Hash(hash)).unwrap().unwrap(); - net.peer(1).receive_message( - &net.peer(0).peer_id, - message::generic::Message::BlockAnnounce(message::generic::BlockAnnounce { - header, - }), - ); - - net.peer(1).import_queue_sync(); - assert!(net.peer(1).client().header(&BlockId::Hash(hash)).unwrap().is_some()); + fn import_with_announce(net: &mut TestNet, runtime: &mut current_thread::Runtime, hash: H256) { + net.peer(0).announce_block(hash); + + runtime.block_on(futures::future::poll_fn::<(), (), _>(|| { + net.poll(); + if net.peer(1).client().header(&BlockId::Hash(hash)).unwrap().is_some() { + Ok(Async::Ready(())) + } else { + Ok(Async::NotReady) + } + })).unwrap(); } // given the network with 1 full nodes (#0) and 1 light node (#1) @@ -474,13 +517,13 @@ fn light_peer_imports_header_from_announce() { net.add_light_peer(&Default::default()); // let them connect to each other - net.sync(); + net.block_until_sync(&mut runtime); // check that NEW block is imported from announce message let new_hash = net.peer(0).push_blocks(1, false); - import_with_announce(&mut net, new_hash); + import_with_announce(&mut net, &mut runtime, new_hash); // check that KNOWN STALE block is imported from announce message let known_stale_hash = net.peer(0).push_blocks_at(BlockId::Number(0), 1, true); - import_with_announce(&mut net, known_stale_hash); + import_with_announce(&mut net, &mut runtime, known_stale_hash); } diff --git a/core/network/src/transport.rs b/core/network/src/transport.rs index ac6cc633a8c017c60bafb47d8f8db14258664ec7..901ec18581e1d3e851034bb8a97e9154c786890d 100644 --- a/core/network/src/transport.rs +++ b/core/network/src/transport.rs @@ -30,10 +30,14 @@ pub use self::bandwidth::BandwidthSinks; /// Builds the transport that serves as a common ground for all connections. /// +/// If `memory_only` is true, then only communication within the same process are allowed. Only +/// addresses with the format `/memory/...` are allowed. +/// /// Returns a `BandwidthSinks` object that allows querying the average bandwidth produced by all /// the connections spawned with this transport. pub fn build_transport( keypair: identity::Keypair, + memory_only: bool, wasm_external_transport: Option ) -> (Boxed<(PeerId, StreamMuxerBox), io::Error>, Arc) { // Build configuration objects for encryption mechanisms. @@ -63,12 +67,21 @@ pub fn build_transport( OptionalTransport::none() }; #[cfg(not(target_os = "unknown"))] - let transport = { + let transport = transport.or_transport(if !memory_only { let desktop_trans = tcp::TcpConfig::new(); let desktop_trans = websocket::WsConfig::new(desktop_trans.clone()) .or_transport(desktop_trans); - transport.or_transport(dns::DnsConfig::new(desktop_trans)) - }; + OptionalTransport::some(dns::DnsConfig::new(desktop_trans)) + } else { + OptionalTransport::none() + }); + + let transport = transport.or_transport(if memory_only { + OptionalTransport::some(libp2p::core::transport::MemoryTransport::default()) + } else { + OptionalTransport::none() + }); + let (transport, sinks) = bandwidth::BandwidthLogging::new(transport, Duration::from_secs(5)); // Encryption diff --git a/core/offchain/Cargo.toml b/core/offchain/Cargo.toml index a7b94bdc567e81c797dafe055d85a8d161e0393f..ecc098ae084077ceb8ba6e81a1254d5f4f240176 100644 --- a/core/offchain/Cargo.toml +++ b/core/offchain/Cargo.toml @@ -8,20 +8,21 @@ edition = "2018" [dependencies] client = { package = "substrate-client", path = "../../core/client" } -consensus = { package = "substrate-consensus-common", path = "../../core/consensus/common" } futures = "0.1.25" log = "0.4" offchain-primitives = { package = "substrate-offchain-primitives", path = "./primitives" } -parity-codec = { version = "3.3", features = ["derive"] } +parity-codec = { version = "4.1.1", features = ["derive"] } parking_lot = "0.8.0" primitives = { package = "substrate-primitives", path = "../../core/primitives" } runtime_primitives = { package = "sr-primitives", path = "../../core/sr-primitives" } -tokio = "0.1.7" transaction_pool = { package = "substrate-transaction-pool", path = "../../core/transaction-pool" } +network = { package = "substrate-network", path = "../../core/network" } [dev-dependencies] env_logger = "0.6" +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.7" [features] default = [] diff --git a/core/offchain/src/api.rs b/core/offchain/src/api.rs index d2c7630c249d8adaebd770899b5d0a802e85f5c6..949602f3f72439991d7a2f1f3d3d342d7825b8fb 100644 --- a/core/offchain/src/api.rs +++ b/core/offchain/src/api.rs @@ -14,91 +14,363 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use std::sync::Arc; +use std::{ + str::FromStr, + sync::Arc, + convert::{TryFrom, TryInto}, + time::{SystemTime, Duration}, + thread::sleep, +}; +use client::backend::OffchainStorage; +use crate::AuthorityKeyProvider; use futures::{Stream, Future, sync::mpsc}; use log::{info, debug, warn, error}; -use parity_codec::Decode; +use parity_codec::{Encode, Decode}; use primitives::offchain::{ - Timestamp, HttpRequestId, HttpRequestStatus, HttpError, + Timestamp, + HttpRequestId, HttpRequestStatus, HttpError, Externalities as OffchainExt, - CryptoKind, CryptoKeyId, + CryptoKind, CryptoKey, + StorageKind, + OpaqueNetworkState, OpaquePeerId, OpaqueMultiaddr, }; +use primitives::crypto::{Pair, Public, Protected}; +use primitives::{ed25519, sr25519}; use runtime_primitives::{ generic::BlockId, traits::{self, Extrinsic}, }; use transaction_pool::txpool::{Pool, ChainApi}; +use network::NetworkStateInfo; +use network::{PeerId, Multiaddr}; /// A message between the offchain extension and the processing thread. enum ExtMessage { SubmitExtrinsic(Vec), } +/// A persisted key seed. +#[derive(Encode, Decode)] +struct StoredKey { + kind: CryptoKind, + phrase: String, +} + +impl StoredKey { + fn generate_with_phrase(kind: CryptoKind, password: Option<&str>) -> Self { + match kind { + CryptoKind::Ed25519 => { + let phrase = ed25519::Pair::generate_with_phrase(password).1; + Self { kind, phrase } + } + CryptoKind::Sr25519 => { + let phrase = sr25519::Pair::generate_with_phrase(password).1; + Self { kind, phrase } + } + } + } + + fn to_local_key(&self, password: Option<&str>) -> Result { + match self.kind { + CryptoKind::Ed25519 => { + ed25519::Pair::from_phrase(&self.phrase, password) + .map(|x| LocalKey::Ed25519(x.0)) + } + CryptoKind::Sr25519 => { + sr25519::Pair::from_phrase(&self.phrase, password) + .map(|x| LocalKey::Sr25519(x.0)) + } + } + .map_err(|e| { + warn!("Error recovering Offchain Worker key. Password invalid? {:?}", e); + () + }) + } +} + +enum LocalKey { + Ed25519(ed25519::Pair), + Sr25519(sr25519::Pair), +} + +impl LocalKey { + fn public(&self) -> Result, ()> { + match self { + LocalKey::Ed25519(pair) => Ok(pair.public().to_raw_vec()), + LocalKey::Sr25519(pair) => Ok(pair.public().to_raw_vec()), + } + } + + fn sign(&self, data: &[u8]) -> Result, ()> { + match self { + LocalKey::Ed25519(pair) => { + let sig = pair.sign(data); + let bytes: &[u8] = sig.as_ref(); + Ok(bytes.to_vec()) + } + LocalKey::Sr25519(pair) => { + let sig = pair.sign(data); + let bytes: &[u8] = sig.as_ref(); + Ok(bytes.to_vec()) + } + } + } + + fn verify(&self, msg: &[u8], signature: &[u8]) -> Result { + match self { + LocalKey::Ed25519(pair) => { + Ok(ed25519::Pair::verify_weak(signature, msg, pair.public())) + } + LocalKey::Sr25519(pair) => { + Ok(sr25519::Pair::verify_weak(signature, msg, pair.public())) + } + } + } +} + +/// A key. +enum Key { + LocalKey(LocalKey), + AuthorityKey(ConsensusPair), + FgAuthorityKey(FinalityPair), +} + +impl Key { + fn public(&self) -> Result, ()> { + match self { + Key::LocalKey(local) => { + local.public() + } + Key::AuthorityKey(pair) => { + Ok(pair.public().to_raw_vec()) + } + Key::FgAuthorityKey(pair) => { + Ok(pair.public().to_raw_vec()) + } + } + } + + fn sign(&self, data: &[u8]) -> Result, ()> { + match self { + Key::LocalKey(local) => { + local.sign(data) + } + Key::AuthorityKey(pair) => { + Ok(pair.sign(data).as_ref().to_vec()) + } + Key::FgAuthorityKey(pair) => { + Ok(pair.sign(data).as_ref().to_vec()) + } + } + } + + fn verify(&self, msg: &[u8], signature: &[u8]) -> Result { + match self { + Key::LocalKey(local) => { + local.verify(msg, signature) + } + Key::AuthorityKey(pair) => { + Ok(ConsensusPair::verify_weak(signature, msg, pair.public())) + } + Key::FgAuthorityKey(pair) => { + Ok(FinalityPair::verify_weak(signature, msg, pair.public())) + } + } + } +} + /// Asynchronous offchain API. /// /// NOTE this is done to prevent recursive calls into the runtime (which are not supported currently). -pub(crate) struct AsyncApi(mpsc::UnboundedSender); +pub(crate) struct Api { + sender: mpsc::UnboundedSender, + db: Storage, + keys_password: Protected, + key_provider: KeyProvider, + network_state: Arc, + at: BlockId, +} fn unavailable_yet(name: &str) -> R { - error!("This {:?} API is not available for offchain workers yet. Follow + error!("The {:?} API is not available for offchain workers yet. Follow \ https://github.com/paritytech/substrate/issues/1458 for details", name); Default::default() } -impl OffchainExt for AsyncApi { +const LOCAL_DB: &str = "LOCAL (fork-aware) DB"; +const STORAGE_PREFIX: &[u8] = b"storage"; +const KEYS_PREFIX: &[u8] = b"keys"; + +const NEXT_ID: &[u8] = b"crypto_key_id"; + +impl Api where + Storage: OffchainStorage, + KeyProvider: AuthorityKeyProvider, + Block: traits::Block, +{ + fn password(&self) -> Option<&str> { + Some(self.keys_password.as_ref().as_str()) + } + + fn read_key( + &self, + key: CryptoKey, + ) -> Result, ()> { + match key { + CryptoKey::LocalKey { id, kind } => { + let key = self.db.get(KEYS_PREFIX, &id.encode()) + .and_then(|key| StoredKey::decode(&mut &*key)) + .ok_or(())?; + if key.kind != kind { + warn!( + "Invalid crypto kind (got: {:?}, expected: {:?}), when requesting key {:?}", + key.kind, + kind, + id + ); + return Err(()) + } + Ok(Key::LocalKey(key.to_local_key(self.password())?)) + } + CryptoKey::AuthorityKey => { + let key = self.key_provider + .authority_key(&self.at) + .ok_or(())?; + Ok(Key::AuthorityKey(key)) + } + CryptoKey::FgAuthorityKey => { + let key = self.key_provider + .fg_authority_key(&self.at) + .ok_or(())?; + Ok(Key::FgAuthorityKey(key)) + } + } + } +} + +impl OffchainExt for Api +where + Storage: OffchainStorage, + KeyProvider: AuthorityKeyProvider, + Block: traits::Block, +{ fn submit_transaction(&mut self, ext: Vec) -> Result<(), ()> { - self.0.unbounded_send(ExtMessage::SubmitExtrinsic(ext)) + self.sender + .unbounded_send(ExtMessage::SubmitExtrinsic(ext)) .map(|_| ()) .map_err(|_| ()) } - fn new_crypto_key(&mut self, _crypto: CryptoKind) -> Result { - unavailable_yet::<()>("new_crypto_key"); - Err(()) + fn new_crypto_key(&mut self, kind: CryptoKind) -> Result { + let key = StoredKey::generate_with_phrase(kind, self.password()); + let (id, id_encoded) = loop { + let encoded = self.db.get(KEYS_PREFIX, NEXT_ID); + let encoded_slice = encoded.as_ref().map(|x| x.as_slice()); + let new_id = encoded_slice.and_then(|mut x| u16::decode(&mut x)).unwrap_or_default() + .checked_add(1) + .ok_or(())?; + let new_id_encoded = new_id.encode(); + + if self.db.compare_and_set(KEYS_PREFIX, NEXT_ID, encoded_slice, &new_id_encoded) { + break (new_id, new_id_encoded); + } + }; + + self.db.set(KEYS_PREFIX, &id_encoded, &key.encode()); + + Ok(CryptoKey::LocalKey { id, kind }) + } + + fn pubkey(&self, key: CryptoKey) -> Result, ()> { + self.read_key(key)?.public() } - fn encrypt(&mut self, _key: Option, _data: &[u8]) -> Result, ()> { + fn network_state(&self) -> Result { + let external_addresses = self.network_state.external_addresses(); + + let state = NetworkState::new( + self.network_state.peer_id(), + external_addresses, + ); + Ok(OpaqueNetworkState::from(state)) + } + + fn encrypt(&mut self, _key: CryptoKey, _data: &[u8]) -> Result, ()> { unavailable_yet::<()>("encrypt"); Err(()) } - fn decrypt(&mut self, _key: Option, _data: &[u8]) -> Result, ()> { + fn decrypt(&mut self, _key: CryptoKey, _data: &[u8]) -> Result, ()> { unavailable_yet::<()>("decrypt"); Err(()) + } - fn sign(&mut self, _key: Option, _data: &[u8]) -> Result, ()> { - unavailable_yet::<()>("sign"); - Err(()) + fn sign(&mut self, key: CryptoKey, data: &[u8]) -> Result, ()> { + self.read_key(key)?.sign(data) } - fn verify(&mut self, _key: Option, _msg: &[u8], _signature: &[u8]) -> Result { - unavailable_yet::<()>("verify"); - Err(()) + fn verify(&mut self, key: CryptoKey, msg: &[u8], signature: &[u8]) -> Result { + self.read_key(key)?.verify(msg, signature) } fn timestamp(&mut self) -> Timestamp { - unavailable_yet("timestamp") + let now = SystemTime::now(); + let epoch_duration = now.duration_since(SystemTime::UNIX_EPOCH); + match epoch_duration { + Err(_) => { + // Current time is earlier than UNIX_EPOCH. + Timestamp::from_unix_millis(0) + }, + Ok(d) => { + let duration = d.as_millis(); + // Assuming overflow won't happen for a few hundred years. + Timestamp::from_unix_millis(duration.try_into() + .expect("epoch milliseconds won't overflow u64 for hundreds of years; qed")) + } + } } - fn sleep_until(&mut self, _deadline: Timestamp) { - unavailable_yet::<()>("sleep_until") + fn sleep_until(&mut self, deadline: Timestamp) { + // Get current timestamp. + let now = self.timestamp(); + // Calculate the diff with the deadline. + let diff = deadline.diff(&now); + // Call thread::sleep for the diff duration. + sleep(Duration::from_millis(diff.millis())); } fn random_seed(&mut self) -> [u8; 32] { unavailable_yet("random_seed") } - fn local_storage_set(&mut self, _key: &[u8], _value: &[u8]) { - unavailable_yet("local_storage_set") + fn local_storage_set(&mut self, kind: StorageKind, key: &[u8], value: &[u8]) { + match kind { + StorageKind::PERSISTENT => self.db.set(STORAGE_PREFIX, key, value), + StorageKind::LOCAL => unavailable_yet(LOCAL_DB), + } } - fn local_storage_compare_and_set(&mut self, _key: &[u8], _old_value: &[u8], _new_value: &[u8]) { - unavailable_yet("local_storage_compare_and_set") + fn local_storage_compare_and_set( + &mut self, + kind: StorageKind, + key: &[u8], + old_value: &[u8], + new_value: &[u8], + ) -> bool { + match kind { + StorageKind::PERSISTENT => { + self.db.compare_and_set(STORAGE_PREFIX, key, Some(old_value), new_value) + }, + StorageKind::LOCAL => unavailable_yet(LOCAL_DB), + } } - fn local_storage_get(&mut self, _key: &[u8]) -> Option> { - unavailable_yet("local_storage_get") + fn local_storage_get(&mut self, kind: StorageKind, key: &[u8]) -> Option> { + match kind { + StorageKind::PERSISTENT => self.db.get(STORAGE_PREFIX, key), + StorageKind::LOCAL => unavailable_yet(LOCAL_DB), + } } fn http_request_start( @@ -158,25 +430,108 @@ impl OffchainExt for AsyncApi { } } +/// Information about the local node's network state. +#[derive(Clone, Eq, PartialEq, Debug)] +pub struct NetworkState { + peer_id: PeerId, + external_addresses: Vec, +} + +impl NetworkState { + fn new(peer_id: PeerId, external_addresses: Vec) -> Self { + NetworkState { + peer_id, + external_addresses, + } + } +} + +impl From for OpaqueNetworkState { + fn from(state: NetworkState) -> OpaqueNetworkState { + let enc = Encode::encode(&state.peer_id.into_bytes()); + let peer_id = OpaquePeerId::new(enc); + + let external_addresses: Vec = state + .external_addresses + .iter() + .map(|multiaddr| { + let e = Encode::encode(&multiaddr.to_string()); + OpaqueMultiaddr::new(e) + }) + .collect(); + + OpaqueNetworkState { + peer_id, + external_addresses, + } + } +} + +impl TryFrom for NetworkState { + type Error = (); + + fn try_from(state: OpaqueNetworkState) -> Result { + let inner_vec = state.peer_id.0; + + let bytes: Vec = Decode::decode(&mut &inner_vec[..]).ok_or(())?; + let peer_id = PeerId::from_bytes(bytes).map_err(|_| ())?; + + let external_addresses: Result, Self::Error> = state.external_addresses + .iter() + .map(|enc_multiaddr| -> Result { + let inner_vec = &enc_multiaddr.0; + let bytes = >::decode(&mut &inner_vec[..]).ok_or(())?; + let multiaddr_str = String::from_utf8(bytes).map_err(|_| ())?; + let multiaddr = Multiaddr::from_str(&multiaddr_str).map_err(|_| ())?; + Ok(multiaddr) + }) + .collect(); + let external_addresses = external_addresses?; + + Ok(NetworkState { + peer_id, + external_addresses, + }) + } +} + /// Offchain extensions implementation API -pub(crate) struct Api { +/// +/// This is the asynchronous processing part of the API. +pub(crate) struct AsyncApi { receiver: Option>, transaction_pool: Arc>, at: BlockId, } -impl Api { - pub fn new( +impl AsyncApi { + /// Creates new Offchain extensions API implementation an the asynchronous processing part. + pub fn new>( transaction_pool: Arc>, + db: S, + keys_password: Protected, + key_provider: P, at: BlockId, - ) -> (AsyncApi, Self) { - let (tx, rx) = mpsc::unbounded(); - let api = Self { + network_state: Arc, + ) -> (Api, AsyncApi) { + let (sender, rx) = mpsc::unbounded(); + + let api = Api { + sender, + db, + keys_password, + key_provider, + network_state, + at, + }; + + let async_api = AsyncApi { receiver: Some(rx), transaction_pool, at, }; - (AsyncApi(tx), api) + + (api, async_api) } /// Run a processing task for the API @@ -209,3 +564,166 @@ impl Api { } } } + +#[cfg(test)] +mod tests { + use super::*; + use std::convert::TryFrom; + use runtime_primitives::traits::Zero; + use client_db::offchain::LocalStorage; + use crate::tests::TestProvider; + use network::PeerId; + use test_client::runtime::Block; + + struct MockNetworkStateInfo(); + + impl NetworkStateInfo for MockNetworkStateInfo { + fn external_addresses(&self) -> Vec { + Vec::new() + } + + fn peer_id(&self) -> PeerId { + PeerId::random() + } + } + + fn offchain_api() -> (Api, Block>, AsyncApi) { + let _ = env_logger::try_init(); + let db = LocalStorage::new_test(); + let client = Arc::new(test_client::new()); + let pool = Arc::new( + Pool::new(Default::default(), transaction_pool::ChainApi::new(client.clone())) + ); + + let mock = Arc::new(MockNetworkStateInfo()); + AsyncApi::new(pool, db, "pass".to_owned().into(), TestProvider::default(), BlockId::Number(Zero::zero()), mock) + } + + #[test] + fn should_get_timestamp() { + let mut api = offchain_api().0; + + // Get timestamp from std. + let now = SystemTime::now(); + let d: u64 = now.duration_since(SystemTime::UNIX_EPOCH).unwrap().as_millis().try_into().unwrap(); + + // Get timestamp from offchain api. + let timestamp = api.timestamp(); + + // Compare. + assert!(timestamp.unix_millis() > 0); + assert_eq!(timestamp.unix_millis(), d); + } + + #[test] + fn should_sleep() { + let mut api = offchain_api().0; + + // Arrange. + let now = api.timestamp(); + let delta = primitives::offchain::Duration::from_millis(100); + let deadline = now.add(delta); + + // Act. + api.sleep_until(deadline); + let new_now = api.timestamp(); + + // Assert. + // The diff could be more than the sleep duration. + assert!(new_now.unix_millis() - 100 >= now.unix_millis()); + } + + #[test] + fn should_set_and_get_local_storage() { + // given + let kind = StorageKind::PERSISTENT; + let mut api = offchain_api().0; + let key = b"test"; + + // when + assert_eq!(api.local_storage_get(kind, key), None); + api.local_storage_set(kind, key, b"value"); + + // then + assert_eq!(api.local_storage_get(kind, key), Some(b"value".to_vec())); + } + + #[test] + fn should_compare_and_set_local_storage() { + // given + let kind = StorageKind::PERSISTENT; + let mut api = offchain_api().0; + let key = b"test"; + api.local_storage_set(kind, key, b"value"); + + // when + assert_eq!(api.local_storage_compare_and_set(kind, key, b"val", b"xxx"), false); + assert_eq!(api.local_storage_get(kind, key), Some(b"value".to_vec())); + + // when + assert_eq!(api.local_storage_compare_and_set(kind, key, b"value", b"xxx"), true); + assert_eq!(api.local_storage_get(kind, key), Some(b"xxx".to_vec())); + } + + #[test] + fn should_create_a_new_key_and_sign_and_verify_stuff() { + let test = |kind: CryptoKind| { + // given + let mut api = offchain_api().0; + let msg = b"Hello world!"; + + // when + let key = api.new_crypto_key(kind).unwrap(); + let signature = api.sign(key, msg).unwrap(); + + // then + let res = api.verify(key, msg, &signature).unwrap(); + assert_eq!(res, true); + let res = api.verify(key, msg, &[]).unwrap(); + assert_eq!(res, false); + let res = api.verify(key, b"Different msg", &signature).unwrap(); + assert_eq!(res, false); + }; + + test(CryptoKind::Ed25519); + test(CryptoKind::Sr25519); + } + + #[test] + fn should_sign_and_verify_with_authority_key() { + // given + let mut api = offchain_api().0; + api.key_provider.ed_key = Some(ed25519::Pair::generate().0); + let msg = b"Hello world!"; + + // when + let signature = api.sign(CryptoKey::AuthorityKey, msg).unwrap(); + + // then + let res = api.verify(CryptoKey::AuthorityKey, msg, &signature).unwrap(); + assert_eq!(res, true); + let res = api.verify(CryptoKey::AuthorityKey, msg, &[]).unwrap(); + assert_eq!(res, false); + let res = api.verify(CryptoKey::AuthorityKey, b"Different msg", &signature).unwrap(); + assert_eq!(res, false); + } + + #[test] + fn should_convert_network_states() { + // given + let state = NetworkState::new( + PeerId::random(), + vec![ + Multiaddr::try_from("/ip4/127.0.0.1/tcp/1234".to_string()).unwrap(), + Multiaddr::try_from("/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21").unwrap(), + ], + ); + + // when + let opaque_state = OpaqueNetworkState::from(state.clone()); + let converted_back_state = NetworkState::try_from(opaque_state).unwrap(); + + // then + assert_eq!(state, converted_back_state); + } +} diff --git a/core/offchain/src/lib.rs b/core/offchain/src/lib.rs index 081ae61a5bcaeecc975bab74f911a4f2cfa7a41f..d4947e271f5734d2eebb4dede9e559b6032f9919 100644 --- a/core/offchain/src/lib.rs +++ b/core/offchain/src/lib.rs @@ -34,19 +34,24 @@ #![warn(missing_docs)] use std::{ + fmt, marker::PhantomData, sync::Arc, }; use client::runtime_api::ApiExt; use log::{debug, warn}; -use primitives::ExecutionContext; +use primitives::{ + ExecutionContext, + crypto, +}; use runtime_primitives::{ generic::BlockId, traits::{self, ProvideRuntimeApi}, }; -use tokio::runtime::TaskExecutor; +use futures::future::Future; use transaction_pool::txpool::{Pool, ChainApi}; +use network::NetworkStateInfo; mod api; @@ -54,39 +59,88 @@ pub mod testing; pub use offchain_primitives::OffchainWorkerApi; +/// Provides currently configured authority key. +pub trait AuthorityKeyProvider: Clone + 'static { + /// The crypto used by the block authoring algorithm. + type ConsensusPair: crypto::Pair; + /// The crypto used by the finality gadget. + type FinalityPair: crypto::Pair; + + /// Returns currently configured authority key. + fn authority_key(&self, block_id: &BlockId) -> Option; + + /// Returns currently configured finality gadget authority key. + fn fg_authority_key(&self, block_id: &BlockId) -> Option; +} + /// An offchain workers manager. -#[derive(Debug)] -pub struct OffchainWorkers { - client: Arc, - executor: TaskExecutor, +pub struct OffchainWorkers< + Client, + Storage, + KeyProvider, + Block: traits::Block, +> { + client: Arc, + db: Storage, + authority_key: KeyProvider, + keys_password: crypto::Protected, _block: PhantomData, } -impl OffchainWorkers { +impl OffchainWorkers< + Client, + Storage, + KeyProvider, + Block, +> { /// Creates new `OffchainWorkers`. pub fn new( - client: Arc, - executor: TaskExecutor, + client: Arc, + db: Storage, + authority_key: KeyProvider, + keys_password: crypto::Protected, ) -> Self { Self { client, - executor, + db, + authority_key, + keys_password, _block: PhantomData, } } } -impl OffchainWorkers where +impl fmt::Debug for OffchainWorkers< + Client, + Storage, + KeyProvider, + Block, +> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("OffchainWorkers").finish() + } +} + +impl OffchainWorkers< + Client, + Storage, + KeyProvider, + Block, +> where Block: traits::Block, - C: ProvideRuntimeApi, - C::Api: OffchainWorkerApi, + Client: ProvideRuntimeApi + Send + Sync + 'static, + Client::Api: OffchainWorkerApi, + KeyProvider: AuthorityKeyProvider + Send, + Storage: client::backend::OffchainStorage + 'static, { /// Start the offchain workers after given block. + #[must_use] pub fn on_block_imported( &self, number: &::Number, pool: &Arc>, - ) where + network_state: Arc, + ) -> impl Future where A: ChainApi + 'static, { let runtime = self.client.runtime_api(); @@ -95,20 +149,98 @@ impl OffchainWorkers where debug!("Checking offchain workers at {:?}: {:?}", at, has_api); if has_api.unwrap_or(false) { - let (api, runner) = api::Api::new(pool.clone(), at.clone()); - self.executor.spawn(runner.process()); - - debug!("Running offchain workers at {:?}", at); - let api = Box::new(api); - runtime.offchain_worker_with_context(&at, ExecutionContext::OffchainWorker(api), *number).unwrap(); + let (api, runner) = api::AsyncApi::new( + pool.clone(), + self.db.clone(), + self.keys_password.clone(), + self.authority_key.clone(), + at.clone(), + network_state.clone(), + ); + debug!("Spawning offchain workers at {:?}", at); + let number = *number; + let client = self.client.clone(); + spawn_worker(move || { + let runtime = client.runtime_api(); + let api = Box::new(api); + debug!("Running offchain workers at {:?}", at); + let run = runtime.offchain_worker_with_context( + &at, + ExecutionContext::OffchainWorker(api), + number + ); + if let Err(e) = run { + log::error!("Error running offchain workers at {:?}: {:?}", at, e); + } + }); + futures::future::Either::A(runner.process()) + } else { + futures::future::Either::B(futures::future::ok(())) } } } +/// 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); +} + #[cfg(test)] mod tests { use super::*; use futures::Future; + use primitives::{ed25519, sr25519}; + use network::{Multiaddr, PeerId}; + + struct MockNetworkStateInfo(); + + impl NetworkStateInfo for MockNetworkStateInfo { + fn external_addresses(&self) -> Vec { + Vec::new() + } + + fn peer_id(&self) -> PeerId { + PeerId::random() + } + } + + #[derive(Clone)] + pub(crate) struct TestProvider { + _marker: PhantomData, + pub(crate) sr_key: Option, + pub(crate) ed_key: Option, + } + + impl Default for TestProvider { + fn default() -> Self { + Self { + _marker: PhantomData, + sr_key: None, + ed_key: None, + } + } + } + + impl AuthorityKeyProvider for TestProvider { + type ConsensusPair = ed25519::Pair; + type FinalityPair = sr25519::Pair; + + fn authority_key(&self, _: &BlockId) -> Option { + self.ed_key.clone() + } + + fn fg_authority_key(&self, _: &BlockId) -> Option { + self.sr_key.clone() + } + } #[test] fn should_call_into_runtime_and_produce_extrinsic() { @@ -117,10 +249,12 @@ mod tests { let runtime = tokio::runtime::Runtime::new().unwrap(); let client = Arc::new(test_client::new()); let pool = Arc::new(Pool::new(Default::default(), ::transaction_pool::ChainApi::new(client.clone()))); + let db = client_db::offchain::LocalStorage::new_test(); + let mock = Arc::new(MockNetworkStateInfo()); // when - let offchain = OffchainWorkers::new(client, runtime.executor()); - offchain.on_block_imported(&0u64, &pool); + let offchain = OffchainWorkers::new(client, db, TestProvider::default(), "".to_owned().into()); + runtime.executor().spawn(offchain.on_block_imported(&0u64, &pool, mock.clone())); // then runtime.shutdown_on_idle().wait().unwrap(); diff --git a/core/offchain/src/testing.rs b/core/offchain/src/testing.rs index 3419665d0a2f2873693168d89950eceb7eb363d8..6f473a9cd49a0127b30b0253ce23ad7844ce4102 100644 --- a/core/offchain/src/testing.rs +++ b/core/offchain/src/testing.rs @@ -20,6 +20,7 @@ use std::{ collections::BTreeMap, sync::Arc, }; +use client::backend::OffchainStorage; use parking_lot::RwLock; use primitives::offchain::{ self, @@ -28,7 +29,9 @@ use primitives::offchain::{ HttpRequestStatus as RequestStatus, Timestamp, CryptoKind, - CryptoKeyId, + CryptoKey, + StorageKind, + OpaqueNetworkState, }; /// Pending request. @@ -61,6 +64,11 @@ pub struct PendingRequest { pub struct State { /// A list of pending requests. pub requests: BTreeMap, + expected_requests: BTreeMap, + /// Persistent local storage + pub persistent_storage: client::in_mem::OffchainStorage, + /// Local storage + pub local_storage: client::in_mem::OffchainStorage, } impl State { @@ -74,7 +82,7 @@ impl State { ) { match self.requests.get_mut(&RequestId(id)) { None => { - panic!("Missing expected request: {:?}.\n\nAll: {:?}", id, self.requests); + panic!("Missing pending request: {:?}.\n\nAll: {:?}", id, self.requests); } Some(req) => { assert_eq!( @@ -86,34 +94,94 @@ impl State { } } } + + fn fulfill_expected(&mut self, id: u16) { + if let Some(mut req) = self.expected_requests.remove(&RequestId(id)) { + let response = std::mem::replace(&mut req.response, vec![]); + let headers = std::mem::replace(&mut req.response_headers, vec![]); + self.fulfill_pending_request(id, req, response, headers); + } + } + + /// Add expected HTTP request. + /// + /// This method can be used to initialize expected HTTP requests and their responses + /// before running the actual code that utilizes them (for instance before calling into runtime). + /// Expected request has to be fulfilled before this struct is dropped, + /// the `response` and `response_headers` fields will be used to return results to the callers. + pub fn expect_request(&mut self, id: u16, expected: PendingRequest) { + self.expected_requests.insert(RequestId(id), expected); + } +} + +impl Drop for State { + fn drop(&mut self) { + if !self.expected_requests.is_empty() { + panic!("Unfulfilled expected requests: {:?}", self.expected_requests); + } + } } /// Implementation of offchain externalities used for tests. #[derive(Clone, Default, Debug)] pub struct TestOffchainExt(pub Arc>); +impl TestOffchainExt { + /// Create new `TestOffchainExt` and a reference to the internal state. + pub fn new() -> (Self, Arc>) { + let ext = Self::default(); + let state = ext.0.clone(); + (ext, state) + } +} + impl offchain::Externalities for TestOffchainExt { fn submit_transaction(&mut self, _ex: Vec) -> Result<(), ()> { unimplemented!("not needed in tests so far") } - fn new_crypto_key(&mut self, _crypto: CryptoKind) -> Result { + fn network_state(&self) -> Result { + unimplemented!("not needed in tests so far") + } + + fn pubkey(&self, _key: CryptoKey) -> Result, ()> { unimplemented!("not needed in tests so far") } - fn encrypt(&mut self, _key: Option, _data: &[u8]) -> Result, ()> { + fn new_crypto_key(&mut self, _crypto: CryptoKind) -> Result { unimplemented!("not needed in tests so far") } - fn decrypt(&mut self, _key: Option, _data: &[u8]) -> Result, ()> { + fn encrypt( + &mut self, + _key: CryptoKey, + _data: &[u8], + ) -> Result, ()> { unimplemented!("not needed in tests so far") } - fn sign(&mut self, _key: Option, _data: &[u8]) -> Result, ()> { + fn decrypt( + &mut self, + _key: CryptoKey, + _data: &[u8], + ) -> Result, ()> { unimplemented!("not needed in tests so far") } - fn verify(&mut self, _key: Option, _msg: &[u8], _signature: &[u8]) -> Result { + fn sign( + &mut self, + _key: CryptoKey, + _data: &[u8], + ) -> Result, ()> { + unimplemented!("not needed in tests so far") + } + + fn verify( + &mut self, + _key: CryptoKey, + _msg: &[u8], + _signature: &[u8], + ) -> Result { unimplemented!("not needed in tests so far") } @@ -129,21 +197,34 @@ impl offchain::Externalities for TestOffchainExt { unimplemented!("not needed in tests so far") } - fn local_storage_set(&mut self, _key: &[u8], _value: &[u8]) { - unimplemented!("not needed in tests so far") + fn local_storage_set(&mut self, kind: StorageKind, key: &[u8], value: &[u8]) { + let mut state = self.0.write(); + match kind { + StorageKind::LOCAL => &mut state.local_storage, + StorageKind::PERSISTENT => &mut state.persistent_storage, + }.set(b"", key, value); } fn local_storage_compare_and_set( &mut self, - _key: &[u8], - _old_value: &[u8], - _new_value: &[u8] - ) { - unimplemented!("not needed in tests so far") + kind: StorageKind, + key: &[u8], + old_value: &[u8], + new_value: &[u8] + ) -> bool { + let mut state = self.0.write(); + match kind { + StorageKind::LOCAL => &mut state.local_storage, + StorageKind::PERSISTENT => &mut state.persistent_storage, + }.compare_and_set(b"", key, Some(old_value), new_value) } - fn local_storage_get(&mut self, _key: &[u8]) -> Option> { - unimplemented!("not needed in tests so far") + fn local_storage_get(&mut self, kind: StorageKind, key: &[u8]) -> Option> { + let state = self.0.read(); + match kind { + StorageKind::LOCAL => &state.local_storage, + StorageKind::PERSISTENT => &state.persistent_storage, + }.get(b"", key) } fn http_request_start(&mut self, method: &str, uri: &str, meta: &[u8]) -> Result { @@ -180,15 +261,21 @@ impl offchain::Externalities for TestOffchainExt { _deadline: Option ) -> Result<(), HttpError> { let mut state = self.0.write(); - if let Some(req) = state.requests.get_mut(&request_id) { + + let sent = { + let req = state.requests.get_mut(&request_id).ok_or(HttpError::IoError)?; + req.body.extend(chunk); if chunk.is_empty() { req.sent = true; } - req.body.extend(chunk); - Ok(()) - } else { - Err(HttpError::IoError) + req.sent + }; + + if sent { + state.fulfill_expected(request_id.0); } + + Ok(()) } fn http_response_wait( @@ -241,4 +328,3 @@ impl offchain::Externalities for TestOffchainExt { } } } - diff --git a/core/peerset/Cargo.toml b/core/peerset/Cargo.toml index aa08c8ca997a5d32546567bbb806cf778bac6d7a..91e9d58e0a6b42d74690dce7cbbf1fc65309d6e5 100644 --- a/core/peerset/Cargo.toml +++ b/core/peerset/Cargo.toml @@ -8,8 +8,8 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -futures = "0.1" -libp2p = { version = "0.9.0", default-features = false } +futures-preview = "0.3.0-alpha.17" +libp2p = { version = "0.10.0", default-features = false } linked-hash-map = "0.5" log = "0.4" lru-cache = "0.1.2" @@ -17,4 +17,3 @@ serde_json = "1.0.24" [dev-dependencies] rand = "0.6" -tokio = "0.1" diff --git a/core/peerset/src/lib.rs b/core/peerset/src/lib.rs index aa3ce02076d56d5cf42605d6a8aa76cd444df581..c7c7850f16087799a38fb455e349bf3642ad6f10 100644 --- a/core/peerset/src/lib.rs +++ b/core/peerset/src/lib.rs @@ -20,10 +20,11 @@ mod peersstate; use std::{collections::{HashSet, HashMap}, collections::VecDeque, time::Instant}; -use futures::{prelude::*, sync::mpsc, try_ready}; +use futures::{prelude::*, channel::mpsc}; use libp2p::PeerId; use log::{debug, error, trace}; use serde_json::json; +use std::{pin::Pin, task::Context, task::Poll}; /// We don't accept nodes whose reputation is under this value. const BANNED_THRESHOLD: i32 = 82 * (i32::min_value() / 100); @@ -155,7 +156,11 @@ pub struct Peerset { data: peersstate::PeersState, /// If true, we only accept reserved nodes. reserved_only: bool, + /// Receiver for messages from the `PeersetHandle` and from `tx`. rx: mpsc::UnboundedReceiver, + /// Sending side of `rx`. + tx: mpsc::UnboundedSender, + /// Queue of messages to be emitted when the `Peerset` is polled. message_queue: VecDeque, /// When the `Peerset` was created. created: Instant, @@ -169,11 +174,12 @@ impl Peerset { let (tx, rx) = mpsc::unbounded(); let handle = PeersetHandle { - tx, + tx: tx.clone(), }; let mut peerset = Peerset { data: peersstate::PeersState::new(config.in_peers, config.out_peers), + tx, rx, reserved_only: config.reserved_only, message_queue: VecDeque::new(), @@ -353,7 +359,7 @@ impl Peerset { /// a corresponding `Accept` or `Reject`, except if we were already connected to this peer. /// /// Note that this mechanism is orthogonal to `Connect`/`Drop`. Accepting an incoming - /// connection implicitely means `Connect`, but incoming connections aren't cancelled by + /// connection implicitly means `Connect`, but incoming connections aren't cancelled by /// `dropped`. /// // Implementation note: because of concurrency issues, it is possible that we push a `Connect` @@ -423,6 +429,14 @@ impl Peerset { } } + /// Reports an adjustment to the reputation of the given peer. + pub fn report_peer(&mut self, peer_id: PeerId, score_diff: i32) { + // We don't immediately perform the adjustments in order to have state consistency. We + // don't want the reporting here to take priority over messages sent using the + // `PeersetHandle`. + let _ = self.tx.unbounded_send(Action::ReportPeer(peer_id, score_diff)); + } + /// Produces a JSON object containing the state of the peerset manager, for debugging purposes. pub fn debug_info(&mut self) -> serde_json::Value { self.update_time(); @@ -457,24 +471,34 @@ impl Peerset { impl Stream for Peerset { type Item = Message; - type Error = (); - fn poll(&mut self) -> Poll, Self::Error> { + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { loop { if let Some(message) = self.message_queue.pop_front() { - return Ok(Async::Ready(Some(message))); + return Poll::Ready(Some(message)); } - match try_ready!(self.rx.poll()) { - None => return Ok(Async::NotReady), - Some(action) => match action { - Action::AddReservedPeer(peer_id) => self.on_add_reserved_peer(peer_id), - Action::RemoveReservedPeer(peer_id) => self.on_remove_reserved_peer(peer_id), - Action::SetReservedOnly(reserved) => self.on_set_reserved_only(reserved), - Action::ReportPeer(peer_id, score_diff) => self.on_report_peer(peer_id, score_diff), - Action::SetPriorityGroup(group_id, peers) => self.on_set_priority_group(&group_id, peers), - Action::AddToPriorityGroup(group_id, peer_id) => self.on_add_to_priority_group(&group_id, peer_id), - Action::RemoveFromPriorityGroup(group_id, peer_id) => self.on_remove_from_priority_group(&group_id, peer_id), - } + + let action = match Stream::poll_next(Pin::new(&mut self.rx), cx) { + Poll::Pending => return Poll::Pending, + Poll::Ready(Some(event)) => event, + Poll::Ready(None) => return Poll::Pending, + }; + + match action { + Action::AddReservedPeer(peer_id) => + self.on_add_reserved_peer(peer_id), + Action::RemoveReservedPeer(peer_id) => + self.on_remove_reserved_peer(peer_id), + Action::SetReservedOnly(reserved) => + self.on_set_reserved_only(reserved), + Action::ReportPeer(peer_id, score_diff) => + self.on_report_peer(peer_id, score_diff), + Action::SetPriorityGroup(group_id, peers) => + self.on_set_priority_group(&group_id, peers), + Action::AddToPriorityGroup(group_id, peer_id) => + self.on_add_to_priority_group(&group_id, peer_id), + Action::RemoveFromPriorityGroup(group_id, peer_id) => + self.on_remove_from_priority_group(&group_id, peer_id), } } } @@ -485,7 +509,7 @@ mod tests { use libp2p::PeerId; use futures::prelude::*; use super::{PeersetConfig, Peerset, Message, IncomingIndex, BANNED_THRESHOLD}; - use std::{thread, time::Duration}; + use std::{pin::Pin, task::Poll, thread, time::Duration}; fn assert_messages(mut peerset: Peerset, messages: Vec) -> Peerset { for expected_message in messages { @@ -497,10 +521,8 @@ mod tests { peerset } - fn next_message(peerset: Peerset) -> Result<(Message, Peerset), ()> { - let (next, peerset) = peerset.into_future() - .wait() - .map_err(|_| ())?; + fn next_message(mut peerset: Peerset) -> Result<(Message, Peerset), ()> { + let next = futures::executor::block_on_stream(&mut peerset).next(); let message = next.ok_or_else(|| ())?; Ok((message, peerset)) } @@ -598,13 +620,13 @@ mod tests { let peer_id = PeerId::random(); handle.report_peer(peer_id.clone(), BANNED_THRESHOLD - 1); - let fut = futures::future::poll_fn(move || -> Result<_, ()> { + let fut = futures::future::poll_fn(move |cx| { // We need one polling for the message to be processed. - assert_eq!(peerset.poll().unwrap(), Async::NotReady); + assert_eq!(Stream::poll_next(Pin::new(&mut peerset), cx), Poll::Pending); // Check that an incoming connection from that node gets refused. peerset.incoming(peer_id.clone(), IncomingIndex(1)); - if let Async::Ready(msg) = peerset.poll().unwrap() { + if let Poll::Ready(msg) = Stream::poll_next(Pin::new(&mut peerset), cx) { assert_eq!(msg.unwrap(), Message::Reject(IncomingIndex(1))); } else { panic!() @@ -615,14 +637,14 @@ mod tests { // Try again. This time the node should be accepted. peerset.incoming(peer_id.clone(), IncomingIndex(2)); - while let Async::Ready(msg) = peerset.poll().unwrap() { + while let Poll::Ready(msg) = Stream::poll_next(Pin::new(&mut peerset), cx) { assert_eq!(msg.unwrap(), Message::Accept(IncomingIndex(2))); } - Ok(Async::Ready(())) + Poll::Ready(()) }); - tokio::runtime::current_thread::Runtime::new().unwrap().block_on(fut).unwrap(); + futures::executor::block_on(fut); } } diff --git a/core/peerset/tests/fuzz.rs b/core/peerset/tests/fuzz.rs index 42a7f2770cc9ccfe64d5d455a1a1046be254bfd1..be29916c34f2057fe37edd2496b243998b65f38d 100644 --- a/core/peerset/tests/fuzz.rs +++ b/core/peerset/tests/fuzz.rs @@ -18,7 +18,7 @@ use futures::prelude::*; use libp2p::PeerId; use rand::distributions::{Distribution, Uniform, WeightedIndex}; use rand::seq::IteratorRandom; -use std::{collections::HashMap, collections::HashSet, iter}; +use std::{collections::HashMap, collections::HashSet, iter, pin::Pin, task::Poll}; use substrate_peerset::{IncomingIndex, Message, PeersetConfig, Peerset}; #[test] @@ -54,7 +54,7 @@ fn test_once() { out_peers: Uniform::new_inclusive(0, 25).sample(&mut rng), }); - tokio::runtime::current_thread::Runtime::new().unwrap().block_on(futures::future::poll_fn(move || -> Result<_, ()> { + futures::executor::block_on(futures::future::poll_fn(move |cx| { // List of nodes the user of `peerset` assumes it's connected to. Always a subset of // `known_nodes`. let mut connected_nodes = HashSet::::new(); @@ -71,20 +71,20 @@ fn test_once() { let action_weights = [150, 90, 90, 30, 30, 1, 1, 4, 4]; match WeightedIndex::new(&action_weights).unwrap().sample(&mut rng) { // If we generate 0, poll the peerset. - 0 => match peerset.poll().unwrap() { - Async::Ready(Some(Message::Connect(id))) => { + 0 => match Stream::poll_next(Pin::new(&mut peerset), cx) { + Poll::Ready(Some(Message::Connect(id))) => { if let Some(id) = incoming_nodes.iter().find(|(_, v)| **v == id).map(|(&id, _)| id) { incoming_nodes.remove(&id); } assert!(connected_nodes.insert(id)); } - Async::Ready(Some(Message::Drop(id))) => { connected_nodes.remove(&id); } - Async::Ready(Some(Message::Accept(n))) => + Poll::Ready(Some(Message::Drop(id))) => { connected_nodes.remove(&id); } + Poll::Ready(Some(Message::Accept(n))) => assert!(connected_nodes.insert(incoming_nodes.remove(&n).unwrap())), - Async::Ready(Some(Message::Reject(n))) => + Poll::Ready(Some(Message::Reject(n))) => assert!(!connected_nodes.contains(&incoming_nodes.remove(&n).unwrap())), - Async::Ready(None) => panic!(), - Async::NotReady => {} + Poll::Ready(None) => panic!(), + Poll::Pending => {} } // If we generate 1, discover a new node. @@ -133,6 +133,6 @@ fn test_once() { } } - Ok(Async::Ready(())) - })).unwrap(); + Poll::Ready(()) + })); } diff --git a/core/primitives/Cargo.toml b/core/primitives/Cargo.toml index ab34cfcd1336081448f37778d98e888931c248af..1d55f82fab230515b9a3a96ce2a276ec3e5e0f5a 100644 --- a/core/primitives/Cargo.toml +++ b/core/primitives/Cargo.toml @@ -6,27 +6,30 @@ edition = "2018" [dependencies] rstd = { package = "sr-std", path = "../sr-std", default-features = false } -parity-codec = { version = "3.4.0", default-features = false, features = ["derive"] } +parity-codec = { version = "4.1.1", default-features = false, features = ["derive"] } rustc-hex = { version = "2.0", default-features = false } serde = { version = "1.0", optional = true, features = ["derive"] } twox-hash = { version = "1.2.0", optional = true } byteorder = { version = "1.3.1", default-features = false } -primitive-types = { version = "0.2.3", default-features = false, features = ["codec"] } +primitive-types = { version = "0.4.0", default-features = false, features = ["codec"] } impl-serde = { version = "0.1", optional = true } wasmi = { version = "0.4.3", optional = true } -hash-db = { version = "0.12", default-features = false } -hash256-std-hasher = { version = "0.12", default-features = false } +hash-db = { version = "0.14.0", default-features = false } +hash256-std-hasher = { version = "0.14.0", default-features = false } ed25519-dalek = { version = "1.0.0-pre.1", optional = true } base58 = { version = "0.1", optional = true } blake2-rfc = { version = "0.2.18", optional = true } -schnorrkel = { version = "0.1", optional = true } +schnorrkel = { version = "0.1.1", optional = true } rand = { version = "0.6", optional = true } sha2 = { version = "0.8", optional = true } -substrate-bip39 = { git = "https://github.com/paritytech/substrate-bip39", optional = true } +substrate-bip39 = { version = "0.2.2", optional = true } tiny-bip39 = { version = "0.6.1", optional = true } hex = { version = "0.3", optional = true } regex = { version = "1.1", optional = true } num-traits = { version = "0.2", default-features = false } +zeroize = { version = "0.9.2", default-features = false } +lazy_static = { version = "1.3", optional = true } +parking_lot = { version = "0.8", optional = true } [dev-dependencies] substrate-serializer = { path = "../serializer" } @@ -46,9 +49,10 @@ bench = false default = ["std"] std = [ "wasmi", + "lazy_static", + "parking_lot", "primitive-types/std", "primitive-types/serde", - "primitive-types/heapsize", "primitive-types/byteorder", "primitive-types/rustc-hex", "primitive-types/libc", @@ -73,4 +77,5 @@ std = [ "schnorrkel", "regex", "num-traits/std", + "zeroize/std" ] diff --git a/core/primitives/src/crypto.rs b/core/primitives/src/crypto.rs index 9ddc9a93f773c9e083138dc529a7274443399e08..327a8a3eb12542c62d8b382c99f8aba407949df0 100644 --- a/core/primitives/src/crypto.rs +++ b/core/primitives/src/crypto.rs @@ -18,6 +18,10 @@ //! Cryptographic utilities. // end::description[] +#[cfg(feature = "std")] +use std::convert::{TryFrom, TryInto}; +#[cfg(feature = "std")] +use parking_lot::Mutex; #[cfg(feature = "std")] use rand::{RngCore, rngs::OsRng}; #[cfg(feature = "std")] @@ -26,6 +30,9 @@ use parity_codec::{Encode, Decode}; use regex::Regex; #[cfg(feature = "std")] use base58::{FromBase58, ToBase58}; +#[cfg(feature = "std")] +use std::hash::Hash; +use zeroize::Zeroize; /// The root phrase for our publicly known keys. pub const DEV_PHRASE: &str = "bottom drive obey lake curtain smoke basket hold race lonely fit walk"; @@ -64,6 +71,43 @@ impl> UncheckedInto for S { } } +/// A store for sensitive data. +/// +/// Calls `Zeroize::zeroize` upon `Drop`. +#[derive(Clone)] +pub struct Protected(T); + +impl AsRef for Protected { + fn as_ref(&self) -> &T { + &self.0 + } +} + +#[cfg(feature = "std")] +impl std::fmt::Debug for Protected { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(fmt, "") + } +} + +impl From for Protected { + fn from(t: T) -> Self { + Protected(t) + } +} + +impl Zeroize for Protected { + fn zeroize(&mut self) { + self.0.zeroize() + } +} + +impl Drop for Protected { + fn drop(&mut self) { + self.zeroize() + } +} + /// An error with the interpretation of a secret. #[derive(Debug, Clone, PartialEq, Eq)] #[cfg(feature = "std")] @@ -203,12 +247,36 @@ pub enum PublicError { #[cfg(feature = "std")] pub trait Ss58Codec: Sized { /// Some if the string is a properly encoded SS58Check address. - fn from_ss58check(s: &str) -> Result; + fn from_ss58check(s: &str) -> Result { + Self::from_ss58check_with_version(s) + .and_then(|(r, v)| match v { + Ss58AddressFormat::SubstrateAccountDirect => Ok(r), + v if v == *DEFAULT_VERSION.lock() => Ok(r), + _ => Err(PublicError::UnknownVersion), + }) + } + /// Some if the string is a properly encoded SS58Check address. + fn from_ss58check_with_version(s: &str) -> Result<(Self, Ss58AddressFormat), PublicError>; /// Some if the string is a properly encoded SS58Check address, optionally with /// a derivation path following. - fn from_string(s: &str) -> Result { Self::from_ss58check(s) } + fn from_string(s: &str) -> Result { + Self::from_string_with_version(s) + .and_then(|(r, v)| match v { + Ss58AddressFormat::SubstrateAccountDirect => Ok(r), + v if v == *DEFAULT_VERSION.lock() => Ok(r), + _ => Err(PublicError::UnknownVersion), + }) + } + /// Return the ss58-check string for this key. - fn to_ss58check(&self) -> String; + fn to_ss58check_with_version(&self, version: Ss58AddressFormat) -> String; + /// 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 + /// a derivation path following. + fn from_string_with_version(s: &str) -> Result<(Self, Ss58AddressFormat), PublicError> { + Self::from_ss58check_with_version(s) + } } #[cfg(feature = "std")] @@ -233,9 +301,92 @@ fn ss58hash(data: &[u8]) -> blake2_rfc::blake2b::Blake2bResult { context.finalize() } +#[cfg(feature = "std")] +lazy_static::lazy_static! { + static ref DEFAULT_VERSION: Mutex + = Mutex::new(Ss58AddressFormat::SubstrateAccountDirect); +} + +/// A known address (sub)format/network ID for SS58. +#[cfg(feature = "std")] +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum Ss58AddressFormat { + /// Any Substrate network, direct checksum, standard account (*25519). + SubstrateAccountDirect, + /// Polkadot Relay-chain, direct checksum, standard account (*25519). + PolkadotAccountDirect, + /// Kusama Relay-chain, direct checksum, standard account (*25519). + KusamaAccountDirect, + /// Use a manually provided numeric value. + Custom(u8), +} + +#[cfg(feature = "std")] +impl From for u8 { + fn from(x: Ss58AddressFormat) -> u8 { + match x { + Ss58AddressFormat::SubstrateAccountDirect => 42, + Ss58AddressFormat::PolkadotAccountDirect => 0, + Ss58AddressFormat::KusamaAccountDirect => 2, + Ss58AddressFormat::Custom(n) => n, + } + } +} + +#[cfg(feature = "std")] +impl TryFrom for Ss58AddressFormat { + type Error = (); + fn try_from(x: u8) -> Result { + match x { + 42 => Ok(Ss58AddressFormat::SubstrateAccountDirect), + 0 => Ok(Ss58AddressFormat::PolkadotAccountDirect), + 2 => Ok(Ss58AddressFormat::KusamaAccountDirect), + _ => Err(()), + } + } +} + +#[cfg(feature = "std")] +impl<'a> TryFrom<&'a str> for Ss58AddressFormat { + type Error = (); + fn try_from(x: &'a str) -> Result { + match x { + "substrate" => Ok(Ss58AddressFormat::SubstrateAccountDirect), + "polkadot" => Ok(Ss58AddressFormat::PolkadotAccountDirect), + "kusama" => Ok(Ss58AddressFormat::KusamaAccountDirect), + a => a.parse::().map(Ss58AddressFormat::Custom).map_err(|_| ()), + } + } +} + +#[cfg(feature = "std")] +impl From for String { + fn from(x: Ss58AddressFormat) -> String { + match x { + Ss58AddressFormat::SubstrateAccountDirect => "substrate".into(), + Ss58AddressFormat::PolkadotAccountDirect => "polkadot".into(), + Ss58AddressFormat::KusamaAccountDirect => "kusama".into(), + Ss58AddressFormat::Custom(x) => x.to_string(), + } + } +} + +/// Set the default "version" (actually, this is a bit of a misnomer and the version byte is +/// typically used not just to encode format/version but also network identity) that is used for +/// encoding and decoding SS58 addresses. If an unknown version is provided then it fails. +/// +/// Current known "versions" are: +/// - 0 direct (payload) checksum for 32-byte *25519 Polkadot addresses. +/// - 2 direct (payload) checksum for 32-byte *25519 Polkadot Milestone 'K' addresses. +/// - 42 direct (payload) checksum for 32-byte *25519 addresses on any Substrate-based network. +#[cfg(feature = "std")] +pub fn set_default_ss58_version(version: Ss58AddressFormat) { + *DEFAULT_VERSION.lock() = version +} + #[cfg(feature = "std")] impl + AsRef<[u8]> + Default + Derive> Ss58Codec for T { - fn from_ss58check(s: &str) -> Result { + 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. @@ -243,21 +394,18 @@ impl + AsRef<[u8]> + Default + Derive> Ss58Codec for T { // Invalid length. return Err(PublicError::BadLength); } - if d[0] != 42 { - // Invalid version. - return Err(PublicError::UnknownVersion); - } + 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) + Ok((res, ver)) } - fn to_ss58check(&self) -> String { - let mut v = vec![42u8]; + 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]); @@ -284,15 +432,53 @@ impl + AsRef<[u8]> + Default + Derive> Ss58Codec for T { .ok_or(PublicError::InvalidPath) } } + + fn from_string_with_version(s: &str) -> Result<(Self, Ss58AddressFormat), PublicError> { + 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, v) = Self::from_ss58check_with_version( + cap.name("ss58") + .map(|r| r.as_str()) + .unwrap_or(DEV_ADDRESS) + )?; + if cap["path"].is_empty() { + Ok((addr, v)) + } else { + let path = re_junction.captures_iter(&cap["path"]) + .map(|f| DeriveJunction::from(&f[1])); + addr.derive(path) + .ok_or(PublicError::InvalidPath) + .map(|a| (a, v)) + } + } +} + +/// Trait suitable for typical cryptographic PKI key public type. +pub trait Public: AsRef<[u8]> + TypedKey + PartialEq + Eq + Clone + Send + Sync { + /// A new instance from the given slice that should be 32 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; + + /// Return a `Vec` filled with raw data. + #[cfg(feature = "std")] + fn to_raw_vec(&self) -> Vec; + + /// Return a slice filled with raw data. + fn as_slice(&self) -> &[u8]; } /// Trait suitable for typical cryptographic PKI key pair type. /// /// For now it just specifies how to create a key from a phrase and derivation path. #[cfg(feature = "std")] -pub trait Pair: Sized + 'static { - /// TThe type which is used to encode a public key. - type Public; +pub trait Pair: TypedKey + Sized + Clone + Send + Sync + 'static { + /// The type which is used to encode a public key. + type Public: Public + Hash; /// The type used to (minimally) encode the data required to securely create /// a new key pair. @@ -300,7 +486,7 @@ pub trait Pair: Sized + 'static { /// The type used to represent a signature. Can be created from a key pair and a message /// and verified with the message and a public key. - type Signature; + type Signature: AsRef<[u8]>; /// Error returned from the `derive` function. type DeriveError; @@ -412,6 +598,31 @@ pub trait Pair: Sized + 'static { path, ) } + + /// Return a vec filled with raw data. + fn to_raw_vec(&self) -> Vec; +} + +/// An identifier for a type of cryptographic key. +/// +/// 0-1024 are reserved. +pub type KeyTypeId = u32; + +/// Constant key types. +pub mod key_types { + use super::KeyTypeId; + + /// ED25519 public key. + pub const ED25519: KeyTypeId = 10; + + /// SR25519 public key. + pub const SR25519: KeyTypeId = 20; +} + +/// A trait for something that has a key type ID. +pub trait TypedKey { + /// The type ID of this key. + const KEY_TYPE: KeyTypeId; } #[cfg(test)] @@ -420,7 +631,7 @@ mod tests { use hex_literal::hex; use super::*; - #[derive(Eq, PartialEq, Debug)] + #[derive(Clone, Eq, PartialEq, Debug)] enum TestPair { Generated, GeneratedWithPhrase, @@ -429,10 +640,31 @@ mod tests { Seed(Vec), } + #[derive(Clone, PartialEq, Eq, Hash)] + struct TestPublic; + impl AsRef<[u8]> for TestPublic { + fn as_ref(&self) -> &[u8] { + &[] + } + } + impl Public for TestPublic { + fn from_slice(_bytes: &[u8]) -> Self { + Self + } + fn as_slice(&self) -> &[u8] { + &[] + } + fn to_raw_vec(&self) -> Vec { + vec![] + } + } + impl TypedKey for TestPublic { + const KEY_TYPE: u32 = 4242; + } impl Pair for TestPair { - type Public = (); + type Public = TestPublic; type Seed = [u8; 0]; - type Signature = (); + type Signature = [u8; 0]; type DeriveError = (); fn generate() -> (Self, ::Seed) { (TestPair::Generated, []) } @@ -453,7 +685,7 @@ mod tests { Err(()) } fn from_seed(_seed: &::Seed) -> Self { TestPair::Seed(vec![]) } - fn sign(&self, _message: &[u8]) -> Self::Signature { () } + fn sign(&self, _message: &[u8]) -> Self::Signature { [] } fn verify, M: AsRef<[u8]>>( _sig: &Self::Signature, _message: M, @@ -464,7 +696,7 @@ mod tests { _message: M, _pubkey: P ) -> bool { true } - fn public(&self) -> Self::Public { () } + fn public(&self) -> Self::Public { TestPublic } fn from_standard_components>( phrase: &str, password: Option<&str>, @@ -481,6 +713,12 @@ mod tests { { Ok(TestPair::Seed(seed.to_owned())) } + fn to_raw_vec(&self) -> Vec { + vec![] + } + } + impl TypedKey for TestPair { + const KEY_TYPE: u32 = 4242; } #[test] diff --git a/core/primitives/src/ed25519.rs b/core/primitives/src/ed25519.rs index 26086816a3e5a23335e31f423101a4a22544d8d1..5d1fe884a5180efa406a07392449594e8411d582 100644 --- a/core/primitives/src/ed25519.rs +++ b/core/primitives/src/ed25519.rs @@ -32,7 +32,7 @@ use bip39::{Mnemonic, Language, MnemonicType}; use crate::crypto::{Pair as TraitPair, DeriveJunction, SecretStringError, Derive, Ss58Codec}; #[cfg(feature = "std")] use serde::{de, Serializer, Serialize, Deserializer, Deserialize}; -use crate::crypto::UncheckedFrom; +use crate::crypto::{key_types, KeyTypeId, Public as TraitPublic, TypedKey, UncheckedFrom}; /// A secret seed. It's not called a "secret key" because ring doesn't expose the secret keys /// of the key pair (yeah, dumb); as such we're forced to remember the seed manually if we @@ -282,41 +282,43 @@ impl Public { Public(data) } - /// A new instance from the given slice that should be 32 bytes long. + /// A new instance from an H256. /// /// 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_slice(data: &[u8]) -> Self { - let mut r = [0u8; 32]; - r.copy_from_slice(data); - Public(r) + pub fn from_h256(x: H256) -> Self { + Public(x.into()) } - /// A new instance from an H256. + /// Return a slice filled with raw data. + pub fn as_array_ref(&self) -> &[u8; 32] { + self.as_ref() + } +} + +impl TraitPublic for Public { + /// A new instance from the given slice that should be 32 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! - pub fn from_h256(x: H256) -> Self { - Public(x.into()) + fn from_slice(data: &[u8]) -> Self { + let mut r = [0u8; 32]; + r.copy_from_slice(data); + Public(r) } /// Return a `Vec` filled with raw data. #[cfg(feature = "std")] - pub fn to_raw_vec(self) -> Vec { + fn to_raw_vec(&self) -> Vec { let r: &[u8; 32] = self.as_ref(); r.to_vec() } /// Return a slice filled with raw data. - pub fn as_slice(&self) -> &[u8] { + fn as_slice(&self) -> &[u8] { let r: &[u8; 32] = self.as_ref(); &r[..] } - - /// Return a slice filled with raw data. - pub fn as_array_ref(&self) -> &[u8; 32] { - self.as_ref() - } } #[cfg(feature = "std")] @@ -460,6 +462,11 @@ impl TraitPair for Pair { _ => false, } } + + /// Return a vec filled with raw data. + fn to_raw_vec(&self) -> Vec { + self.seed().to_vec() + } } #[cfg(feature = "std")] @@ -481,6 +488,19 @@ impl Pair { } } +impl TypedKey for Public { + const KEY_TYPE: KeyTypeId = key_types::ED25519; +} + +impl TypedKey for Signature { + const KEY_TYPE: KeyTypeId = key_types::ED25519; +} + +#[cfg(feature = "std")] +impl TypedKey for Pair { + const KEY_TYPE: KeyTypeId = key_types::ED25519; +} + #[cfg(test)] mod test { use super::*; diff --git a/core/primitives/src/lib.rs b/core/primitives/src/lib.rs index 7c0fd324fe0e1aaa25403dd6a920f2f526205d19..4fabb04ccb0383aa30ed99399639dd98bbe01d11 100644 --- a/core/primitives/src/lib.rs +++ b/core/primitives/src/lib.rs @@ -69,7 +69,7 @@ pub use self::hash::{H160, H256, H512, convert_hash}; pub use self::uint::U256; pub use changes_trie::ChangesTrieConfiguration; #[cfg(feature = "std")] -pub use crypto::{DeriveJunction, Pair}; +pub use crypto::{DeriveJunction, Pair, Public}; pub use hash_db::Hasher; // Switch back to Blake after PoC-3 is out diff --git a/core/primitives/src/offchain.rs b/core/primitives/src/offchain.rs index 7d54c9d61eb31d47d8e3bca8232544ca915203b2..fc1d4f0bdabeef2f60bac9a5da5e0aed10d42b14 100644 --- a/core/primitives/src/offchain.rs +++ b/core/primitives/src/offchain.rs @@ -16,18 +16,57 @@ //! Offchain workers types +use crate::crypto; +use parity_codec::{Encode, Decode}; use rstd::prelude::{Vec, Box}; use rstd::convert::TryFrom; /// A type of supported crypto. -#[derive(Clone, Copy, PartialEq, Eq)] +#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode)] +#[cfg_attr(feature = "std", derive(Debug))] +#[repr(C)] +pub enum StorageKind { + /// Persistent storage is non-revertible and not fork-aware. It means that any value + /// set by the offchain worker triggered at block `N(hash1)` is persisted even + /// if that block is reverted as non-canonical and is available for the worker + /// that is re-run at block `N(hash2)`. + /// This storage can be used by offchain workers to handle forks + /// and coordinate offchain workers running on different forks. + PERSISTENT = 1, + /// Local storage is revertible and fork-aware. It means that any value + /// set by the offchain worker triggered at block `N(hash1)` is reverted + /// if that block is reverted as non-canonical and is NOT available for the worker + /// that is re-run at block `N(hash2)`. + LOCAL = 2, +} + +impl TryFrom for StorageKind { + type Error = (); + + fn try_from(kind: u32) -> Result { + match kind { + e if e == u32::from(StorageKind::PERSISTENT as u8) => Ok(StorageKind::PERSISTENT), + e if e == u32::from(StorageKind::LOCAL as u8) => Ok(StorageKind::LOCAL), + _ => Err(()), + } + } +} + +impl From for u32 { + fn from(c: StorageKind) -> Self { + c as u8 as u32 + } +} + +/// A type of supported crypto. +#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode)] #[cfg_attr(feature = "std", derive(Debug))] #[repr(C)] pub enum CryptoKind { /// SR25519 crypto (Schnorrkel) - Sr25519 = 1, + Sr25519 = crypto::key_types::SR25519 as isize, /// ED25519 crypto (Edwards) - Ed25519 = 2, + Ed25519 = crypto::key_types::ED25519 as isize, } impl TryFrom for CryptoKind { @@ -35,23 +74,76 @@ impl TryFrom for CryptoKind { fn try_from(kind: u32) -> Result { match kind { - e if e == u32::from(CryptoKind::Sr25519 as u8) => Ok(CryptoKind::Sr25519), - e if e == u32::from(CryptoKind::Ed25519 as u8) => Ok(CryptoKind::Ed25519), - _ => Err(()) + e if e == CryptoKind::Sr25519 as isize as u32 => Ok(CryptoKind::Sr25519), + e if e == CryptoKind::Ed25519 as isize as u32 => Ok(CryptoKind::Ed25519), + _ => Err(()), } } } -/// Opaque type for created crypto keys. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +impl From for u32 { + fn from(c: CryptoKind) -> Self { + c as isize as u32 + } +} + +/// Key to use in the offchain worker crypto api. +#[derive(Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "std", derive(Debug))] -pub struct CryptoKeyId(pub u16); +pub enum CryptoKey { + /// Use a key from the offchain workers local storage. + LocalKey { + /// The id of the key. + id: u16, + /// The kind of the key. + kind: CryptoKind, + }, + /// Use the key the block authoring algorithm uses. + AuthorityKey, + /// Use the key the finality gadget uses. + FgAuthorityKey, +} + +impl TryFrom for CryptoKey { + type Error = (); + + fn try_from(key: u64) -> Result { + match key & 0xFF { + 0 => { + let id = (key >> 8 & 0xFFFF) as u16; + let kind = CryptoKind::try_from((key >> 32) as u32)?; + Ok(CryptoKey::LocalKey { id, kind }) + } + 1 => Ok(CryptoKey::AuthorityKey), + 2 => Ok(CryptoKey::FgAuthorityKey), + _ => Err(()), + } + } +} + +impl From for u64 { + fn from(key: CryptoKey) -> u64 { + match key { + CryptoKey::LocalKey { id, kind } => { + ((kind as u64) << 32) | ((id as u64) << 8) + } + CryptoKey::AuthorityKey => 1, + CryptoKey::FgAuthorityKey => 2, + } + } +} /// Opaque type for offchain http requests. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "std", derive(Debug))] pub struct HttpRequestId(pub u16); +impl From for u32 { + fn from(c: HttpRequestId) -> Self { + c.0 as u32 + } +} + /// An error enum returned by some http methods. #[derive(Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "std", derive(Debug))] @@ -75,6 +167,12 @@ impl TryFrom for HttpError { } } +impl From for u32 { + fn from(c: HttpError) -> Self { + c as u8 as u32 + } +} + /// Status of the HTTP request #[derive(Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "std", derive(Debug))] @@ -122,6 +220,41 @@ 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))] +pub struct OpaqueNetworkState { + /// PeerId of the local node. + pub peer_id: OpaquePeerId, + /// List of addresses the node knows it can be reached as. + pub external_addresses: Vec, +} + +/// Simple blob to hold a `PeerId` without committing to its format. +#[derive(Clone, Eq, PartialEq, Encode, Decode)] +#[cfg_attr(feature = "std", derive(Debug))] +pub struct OpaquePeerId(pub Vec); + +impl OpaquePeerId { + /// Create new `OpaquePeerId` + pub fn new(vec: Vec) -> Self { + OpaquePeerId(vec) + } +} + +/// Simple blob to hold a `Multiaddr` without committing to its format. +#[derive(Clone, Eq, PartialEq, Encode, Decode)] +#[cfg_attr(feature = "std", derive(Debug))] +pub struct OpaqueMultiaddr(pub Vec); + +impl OpaqueMultiaddr { + /// Create new `OpaqueMultiaddr` + pub fn new(vec: Vec) -> Self { + OpaqueMultiaddr(vec) + } +} + /// Opaque timestamp type #[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Default)] #[cfg_attr(feature = "std", derive(Debug))] @@ -178,38 +311,47 @@ pub trait Externalities { /// The transaction will end up in the pool and be propagated to others. fn submit_transaction(&mut self, extrinsic: Vec) -> Result<(), ()>; + /// Returns information about the local node's network state. + fn network_state(&self) -> Result; + /// Create new key(pair) for signing/encryption/decryption. /// /// Returns an error if given crypto kind is not supported. - fn new_crypto_key(&mut self, crypto: CryptoKind) -> Result; + fn new_crypto_key(&mut self, crypto: CryptoKind) -> Result; + + /// Returns the locally configured authority public key, if available. + fn pubkey(&self, key: CryptoKey) -> Result, ()>; /// Encrypt a piece of data using given crypto key. /// - /// If `key` is `None`, it will attempt to use current authority key. + /// If `key` is `None`, it will attempt to use current authority key of `CryptoKind`. /// - /// Returns an error if `key` is not available or does not exist. - fn encrypt(&mut self, key: Option, data: &[u8]) -> Result, ()>; + /// Returns an error if `key` is not available or does not exist, + /// or the expected `CryptoKind` does not match. + fn encrypt(&mut self, key: CryptoKey, data: &[u8]) -> Result, ()>; /// Decrypt a piece of data using given crypto key. /// - /// If `key` is `None`, it will attempt to use current authority key. + /// If `key` is `None`, it will attempt to use current authority key of `CryptoKind`. /// - /// Returns an error if data cannot be decrypted or the `key` is not available or does not exist. - fn decrypt(&mut self, key: Option, data: &[u8]) -> Result, ()>; + /// Returns an error if data cannot be decrypted or the `key` is not available or does not exist, + /// or the expected `CryptoKind` does not match. + fn decrypt(&mut self, key: CryptoKey, data: &[u8]) -> Result, ()>; /// Sign a piece of data using given crypto key. /// - /// If `key` is `None`, it will attempt to use current authority key. + /// If `key` is `None`, it will attempt to use current authority key of `CryptoKind`. /// - /// Returns an error if `key` is not available or does not exist. - fn sign(&mut self, key: Option, data: &[u8]) -> Result, ()>; + /// Returns an error if `key` is not available or does not exist, + /// or the expected `CryptoKind` does not match. + fn sign(&mut self, key: CryptoKey, data: &[u8]) -> Result, ()>; /// Verifies that `signature` for `msg` matches given `key`. /// /// Returns an `Ok` with `true` in case it does, `false` in case it doesn't. /// Returns an error in case the key is not available or does not exist or the parameters - /// lengths are incorrect. - fn verify(&mut self, key: Option, msg: &[u8], signature: &[u8]) -> Result; + /// lengths are incorrect or `CryptoKind` does not match. + fn verify(&mut self, key: CryptoKey, msg: &[u8], signature: &[u8]) -> Result; /// Returns current UNIX timestamp (in millis) fn timestamp(&mut self) -> Timestamp; @@ -227,25 +369,33 @@ pub trait Externalities { /// /// Note this storage is not part of the consensus, it's only accessible by /// offchain worker tasks running on the same machine. It IS persisted between runs. - fn local_storage_set(&mut self, key: &[u8], value: &[u8]); + fn local_storage_set(&mut self, kind: StorageKind, key: &[u8], value: &[u8]); /// Sets a value in the local storage if it matches current value. /// /// Since multiple offchain workers may be running concurrently, to prevent /// data races use CAS to coordinate between them. /// + /// Returns `true` if the value has been set, `false` otherwise. + /// /// Note this storage is not part of the consensus, it's only accessible by /// offchain worker tasks running on the same machine. It IS persisted between runs. - fn local_storage_compare_and_set(&mut self, key: &[u8], old_value: &[u8], new_value: &[u8]); + fn local_storage_compare_and_set( + &mut self, + kind: StorageKind, + key: &[u8], + old_value: &[u8], + new_value: &[u8], + ) -> bool; /// Gets a value from the local storage. /// /// If the value does not exist in the storage `None` will be returned. /// Note this storage is not part of the consensus, it's only accessible by /// offchain worker tasks running on the same machine. It IS persisted between runs. - fn local_storage_get(&mut self, key: &[u8]) -> Option>; + fn local_storage_get(&mut self, kind: StorageKind, key: &[u8]) -> Option>; - /// Initiaties a http request given HTTP verb and the URL. + /// Initiates a http request given HTTP verb and the URL. /// /// Meta is a future-reserved field containing additional, parity-codec encoded parameters. /// Returns the id of newly started request. @@ -316,23 +466,31 @@ impl Externalities for Box { (&mut **self).submit_transaction(ex) } - fn new_crypto_key(&mut self, crypto: CryptoKind) -> Result { + fn new_crypto_key(&mut self, crypto: CryptoKind) -> Result { (&mut **self).new_crypto_key(crypto) } - fn encrypt(&mut self, key: Option, data: &[u8]) -> Result, ()> { + fn encrypt(&mut self, key: CryptoKey, data: &[u8]) -> Result, ()> { (&mut **self).encrypt(key, data) } - fn decrypt(&mut self, key: Option, data: &[u8]) -> Result, ()> { + fn network_state(&self) -> Result { + (& **self).network_state() + } + + fn pubkey(&self, key: CryptoKey) -> Result, ()> { + (&**self).pubkey(key) + } + + fn decrypt(&mut self, key: CryptoKey, data: &[u8]) -> Result, ()> { (&mut **self).decrypt(key, data) } - fn sign(&mut self, key: Option, data: &[u8]) -> Result, ()> { + fn sign(&mut self, key: CryptoKey, data: &[u8]) -> Result, ()> { (&mut **self).sign(key, data) } - fn verify(&mut self, key: Option, msg: &[u8], signature: &[u8]) -> Result { + fn verify(&mut self, key: CryptoKey, msg: &[u8], signature: &[u8]) -> Result { (&mut **self).verify(key, msg, signature) } @@ -348,16 +506,22 @@ impl Externalities for Box { (&mut **self).random_seed() } - fn local_storage_set(&mut self, key: &[u8], value: &[u8]) { - (&mut **self).local_storage_set(key, value) + fn local_storage_set(&mut self, kind: StorageKind, key: &[u8], value: &[u8]) { + (&mut **self).local_storage_set(kind, key, value) } - fn local_storage_compare_and_set(&mut self, key: &[u8], old_value: &[u8], new_value: &[u8]) { - (&mut **self).local_storage_compare_and_set(key, old_value, new_value) + fn local_storage_compare_and_set( + &mut self, + kind: StorageKind, + key: &[u8], + old_value: &[u8], + new_value: &[u8], + ) -> bool { + (&mut **self).local_storage_compare_and_set(kind, key, old_value, new_value) } - fn local_storage_get(&mut self, key: &[u8]) -> Option> { - (&mut **self).local_storage_get(key) + fn local_storage_get(&mut self, kind: StorageKind, key: &[u8]) -> Option> { + (&mut **self).local_storage_get(kind, key) } fn http_request_start(&mut self, method: &str, uri: &str, meta: &[u8]) -> Result { @@ -407,4 +571,27 @@ mod tests { assert_eq!(t.sub(Duration::from_millis(10)), Timestamp(0)); assert_eq!(t.diff(&Timestamp(3)), Duration(2)); } + + #[test] + fn crypto_key_to_from_u64() { + let key = CryptoKey::AuthorityKey; + let uint: u64 = key.clone().into(); + let key2 = CryptoKey::try_from(uint).unwrap(); + assert_eq!(key, key2); + + let key = CryptoKey::FgAuthorityKey; + let uint: u64 = key.clone().into(); + let key2 = CryptoKey::try_from(uint).unwrap(); + assert_eq!(key, key2); + + let key = CryptoKey::LocalKey { id: 0, kind: CryptoKind::Ed25519 }; + let uint: u64 = key.clone().into(); + let key2 = CryptoKey::try_from(uint).unwrap(); + assert_eq!(key, key2); + + let key = CryptoKey::LocalKey { id: 10, kind: CryptoKind::Sr25519 }; + let uint: u64 = key.clone().into(); + let key2 = CryptoKey::try_from(uint).unwrap(); + assert_eq!(key, key2); + } } diff --git a/core/primitives/src/sr25519.rs b/core/primitives/src/sr25519.rs index aa2db2dc1263cb8a3897ad51a1848b161c8e0f1b..e01d989143c6b8491680bb18c1a95a11e7540e32 100644 --- a/core/primitives/src/sr25519.rs +++ b/core/primitives/src/sr25519.rs @@ -31,13 +31,14 @@ use substrate_bip39::mini_secret_from_entropy; use bip39::{Mnemonic, Language, MnemonicType}; #[cfg(feature = "std")] use crate::crypto::{Pair as TraitPair, DeriveJunction, Infallible, SecretStringError, Derive, Ss58Codec}; -use crate::{hash::{H256, H512}, crypto::UncheckedFrom}; +use crate::crypto::{key_types, KeyTypeId, Public as TraitPublic, TypedKey, UncheckedFrom}; +use crate::hash::{H256, H512}; use parity_codec::{Encode, Decode}; #[cfg(feature = "std")] use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; #[cfg(feature = "std")] -use schnorrkel::keys::MINI_SECRET_KEY_LENGTH; +use schnorrkel::keys::{MINI_SECRET_KEY_LENGTH, SECRET_KEY_LENGTH}; // signing context #[cfg(feature = "std")] @@ -51,6 +52,17 @@ pub struct Public(pub [u8; 32]); #[cfg(feature = "std")] pub struct Pair(Keypair); +#[cfg(feature = "std")] +impl Clone for Pair { + fn clone(&self) -> Self { + Pair(schnorrkel::Keypair { + public: self.0.public.clone(), + secret: schnorrkel::SecretKey::from_bytes(&self.0.secret.to_bytes()[..]) + .expect("key is always the correct size; qed") + }) + } +} + impl AsRef for Public { fn as_ref(&self) -> &Public { &self @@ -282,39 +294,41 @@ impl Public { Public(data) } - /// A new instance from the given slice that should be 32 bytes long. + /// A new instance from an H256. /// /// 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_slice(data: &[u8]) -> Self { - let mut r = [0u8; 32]; - r.copy_from_slice(data); - Public(r) + pub fn from_h256(x: H256) -> Self { + Public(x.into()) } - /// A new instance from an H256. + /// Return a slice filled with raw data. + pub fn as_array_ref(&self) -> &[u8; 32] { + self.as_ref() + } +} + +impl TraitPublic for Public { + /// A new instance from the given slice that should be 32 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! - pub fn from_h256(x: H256) -> Self { - Public(x.into()) + fn from_slice(data: &[u8]) -> Self { + let mut r = [0u8; 32]; + r.copy_from_slice(data); + Public(r) } /// Return a `Vec` filled with raw data. #[cfg(feature = "std")] - pub fn into_raw_vec(self) -> Vec { + fn to_raw_vec(&self) -> Vec { self.0.to_vec() } /// Return a slice filled with raw data. - pub fn as_slice(&self) -> &[u8] { + fn as_slice(&self) -> &[u8] { &self.0 } - - /// Return a slice filled with raw data. - pub fn as_array_ref(&self) -> &[u8; 32] { - self.as_ref() - } } #[cfg(feature = "std")] @@ -365,6 +379,7 @@ fn derive_hard_junction(secret: &SecretKey, cc: &[u8; CHAIN_CODE_LENGTH]) -> Sec secret.hard_derive_mini_secret_key(Some(ChainCode(cc.clone())), b"").0.expand() } +/// The raw secret seed, which can be used to recreate the `Pair`. #[cfg(feature = "std")] type Seed = [u8; MINI_SECRET_KEY_LENGTH]; @@ -397,14 +412,22 @@ impl TraitPair for Pair { /// /// You should never need to use this; generate(), generate_with_phrase(), from_phrase() fn from_seed_slice(seed: &[u8]) -> Result { - if seed.len() != MINI_SECRET_KEY_LENGTH { - Err(SecretStringError::InvalidSeedLength) - } else { - Ok(Pair( - MiniSecretKey::from_bytes(seed) - .map_err(|_| SecretStringError::InvalidSeed)? - .expand_to_keypair() - )) + match seed.len() { + MINI_SECRET_KEY_LENGTH => { + Ok(Pair( + MiniSecretKey::from_bytes(seed) + .map_err(|_| SecretStringError::InvalidSeed)? + .expand_to_keypair() + )) + } + SECRET_KEY_LENGTH => { + Ok(Pair( + SecretKey::from_bytes(seed) + .map_err(|_| SecretStringError::InvalidSeed)? + .to_keypair() + )) + } + _ => Err(SecretStringError::InvalidSeedLength) } } @@ -478,6 +501,11 @@ impl TraitPair for Pair { Err(_) => false, } } + + /// Return a vec filled with raw data. + fn to_raw_vec(&self) -> Vec { + self.0.secret.to_bytes().to_vec() + } } #[cfg(feature = "std")] @@ -495,6 +523,19 @@ impl Pair { } } +impl TypedKey for Public { + const KEY_TYPE: KeyTypeId = key_types::SR25519; +} + +impl TypedKey for Signature { + const KEY_TYPE: KeyTypeId = key_types::SR25519; +} + +#[cfg(feature = "std")] +impl TypedKey for Pair { + const KEY_TYPE: KeyTypeId = key_types::SR25519; +} + #[cfg(test)] mod test { use super::*; diff --git a/core/rpc-servers/Cargo.toml b/core/rpc-servers/Cargo.toml index bca094b572d5f1d6ed6f5a143f8eaa27d1168bed..64a494f65ffe74b870720c2b0feb02de6e0ff46d 100644 --- a/core/rpc-servers/Cargo.toml +++ b/core/rpc-servers/Cargo.toml @@ -5,10 +5,12 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -http = { package = "jsonrpc-http-server", version = "12.0.0" } pubsub = { package = "jsonrpc-pubsub", version = "12.0.0" } -ws = { package = "jsonrpc-ws-server", version = "12.0.0" } log = "0.4" serde = "1.0" substrate-rpc = { path = "../rpc" } sr-primitives = { path = "../sr-primitives" } + +[target.'cfg(not(target_os = "unknown"))'.dependencies] +http = { package = "jsonrpc-http-server", version = "12.0.0" } +ws = { package = "jsonrpc-ws-server", version = "12.0.0" } diff --git a/core/rpc-servers/src/lib.rs b/core/rpc-servers/src/lib.rs index adf560ce5a6375eeee780ecace7ef6c42908959a..a33f726747149414ebc8e90dd59e0f04791e332c 100644 --- a/core/rpc-servers/src/lib.rs +++ b/core/rpc-servers/src/lib.rs @@ -30,10 +30,10 @@ const MAX_PAYLOAD: usize = 15 * 1024 * 1024; /// Default maximum number of connections for WS RPC servers. const WS_MAX_CONNECTIONS: usize = 100; -type Metadata = apis::metadata::Metadata; -type RpcHandler = pubsub::PubSubHandler; -pub type HttpServer = http::Server; -pub type WsServer = ws::Server; +pub type Metadata = apis::metadata::Metadata; +pub type RpcHandler = pubsub::PubSubHandler; + +pub use self::inner::*; /// Construct rpc `IoHandler` pub fn rpc_handler( @@ -57,49 +57,78 @@ pub fn rpc_handler( io } -/// Start HTTP server listening on given address. -pub fn start_http( - addr: &std::net::SocketAddr, - cors: Option<&Vec>, - io: RpcHandler, -) -> io::Result { - http::ServerBuilder::new(io) - .threads(4) - .health_api(("/health", "system_health")) - .rest_api(if cors.is_some() { - http::RestApi::Secure - } else { - http::RestApi::Unsecure - }) - .cors(map_cors::(cors)) - .max_request_body_size(MAX_PAYLOAD) - .start_http(addr) -} +#[cfg(not(target_os = "unknown"))] +mod inner { + use super::*; + + pub type HttpServer = http::Server; + pub type WsServer = ws::Server; -/// Start WS server listening on given address. -pub fn start_ws( - addr: &std::net::SocketAddr, - max_connections: Option, - cors: Option<&Vec>, - io: RpcHandler, -) -> io::Result { - ws::ServerBuilder::with_meta_extractor(io, |context: &ws::RequestContext| Metadata::new(context.sender())) - .max_payload(MAX_PAYLOAD) - .max_connections(max_connections.unwrap_or(WS_MAX_CONNECTIONS)) - .allowed_origins(map_cors(cors)) - .start(addr) - .map_err(|err| match err { - ws::Error::Io(io) => io, - ws::Error::ConnectionClosed => io::ErrorKind::BrokenPipe.into(), - e => { - error!("{}", e); - io::ErrorKind::Other.into() - } - }) + /// Start HTTP server listening on given address. + /// + /// **Note**: Only available if `not(target_os = "unknown")`. + pub fn start_http( + addr: &std::net::SocketAddr, + cors: Option<&Vec>, + io: RpcHandler, + ) -> io::Result { + http::ServerBuilder::new(io) + .threads(4) + .health_api(("/health", "system_health")) + .allowed_hosts(hosts_filtering(cors.is_some())) + .rest_api(if cors.is_some() { + http::RestApi::Secure + } else { + http::RestApi::Unsecure + }) + .cors(map_cors::(cors)) + .max_request_body_size(MAX_PAYLOAD) + .start_http(addr) + } + + /// Start WS server listening on given address. + /// + /// **Note**: Only available if `not(target_os = "unknown")`. + pub fn start_ws( + addr: &std::net::SocketAddr, + max_connections: Option, + cors: Option<&Vec>, + io: RpcHandler, + ) -> io::Result { + ws::ServerBuilder::with_meta_extractor(io, |context: &ws::RequestContext| Metadata::new(context.sender())) + .max_payload(MAX_PAYLOAD) + .max_connections(max_connections.unwrap_or(WS_MAX_CONNECTIONS)) + .allowed_origins(map_cors(cors)) + .allowed_hosts(hosts_filtering(cors.is_some())) + .start(addr) + .map_err(|err| match err { + ws::Error::Io(io) => io, + ws::Error::ConnectionClosed => io::ErrorKind::BrokenPipe.into(), + e => { + error!("{}", e); + io::ErrorKind::Other.into() + } + }) + } + + fn map_cors From<&'a str>>( + cors: Option<&Vec> + ) -> http::DomainsValidation { + cors.map(|x| x.iter().map(AsRef::as_ref).map(Into::into).collect::>()).into() + } + + fn hosts_filtering(enable: bool) -> http::DomainsValidation { + if enable { + // NOTE The listening address is whitelisted by default. + // Setting an empty vector here enables the validation + // and allows only the listening address. + http::DomainsValidation::AllowOnly(vec![]) + } else { + http::DomainsValidation::Disabled + } + } } -fn map_cors From<&'a str>>( - cors: Option<&Vec> -) -> http::DomainsValidation { - cors.map(|x| x.iter().map(AsRef::as_ref).map(Into::into).collect::>()).into() +#[cfg(target_os = "unknown")] +mod inner { } diff --git a/core/rpc/Cargo.toml b/core/rpc/Cargo.toml index e72f62e5000ca26c80a8e42761a6f1e44e43bdd0..181cbdfd8ea5f28ab7a361161e7a6859192aba6a 100644 --- a/core/rpc/Cargo.toml +++ b/core/rpc/Cargo.toml @@ -7,13 +7,14 @@ edition = "2018" [dependencies] derive_more = "0.14.0" futures = "0.1" +futures03 = { package = "futures-preview", version = "0.3.0-alpha.17", features = ["compat"] } jsonrpc-core = "12.0.0" jsonrpc-core-client = "12.0.0" jsonrpc-pubsub = "12.0.0" jsonrpc-derive = "12.0.0" log = "0.4" parking_lot = "0.8.0" -parity-codec = "3.3" +parity-codec = "4.1.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" client = { package = "substrate-client", path = "../client" } @@ -24,7 +25,6 @@ state_machine = { package = "substrate-state-machine", path = "../state-machine" transaction_pool = { package = "substrate-transaction-pool", path = "../transaction-pool" } runtime_primitives = { package = "sr-primitives", path = "../sr-primitives" } runtime_version = { package = "sr-version", path = "../sr-version" } -tokio = "0.1.7" [dev-dependencies] assert_matches = "1.1" @@ -32,3 +32,4 @@ futures = "0.1.17" sr-io = { path = "../sr-io" } test-client = { package = "substrate-test-runtime-client", path = "../test-runtime/client" } rustc-hex = "2.0" +tokio = "0.1.17" diff --git a/core/rpc/src/author/tests.rs b/core/rpc/src/author/tests.rs index 4c6a724acd5ae35bbf4ccbfbe71e7999a7d684e6..cf320ee1442e7aff950850143d65155137c62d49 100644 --- a/core/rpc/src/author/tests.rs +++ b/core/rpc/src/author/tests.rs @@ -44,7 +44,7 @@ fn submit_transaction_should_not_cause_error() { let p = Author { client: client.clone(), pool: Arc::new(Pool::new(Default::default(), ChainApi::new(client))), - subscriptions: Subscriptions::new(runtime.executor()), + subscriptions: Subscriptions::new(Arc::new(runtime.executor())), }; let xt = uxt(AccountKeyring::Alice, 1).encode(); let h: H256 = blake2_256(&xt).into(); @@ -65,7 +65,7 @@ fn submit_rich_transaction_should_not_cause_error() { let p = Author { client: client.clone(), pool: Arc::new(Pool::new(Default::default(), ChainApi::new(client.clone()))), - subscriptions: Subscriptions::new(runtime.executor()), + subscriptions: Subscriptions::new(Arc::new(runtime.executor())), }; let xt = uxt(AccountKeyring::Alice, 0).encode(); let h: H256 = blake2_256(&xt).into(); @@ -88,7 +88,7 @@ fn should_watch_extrinsic() { let p = Author { client, pool: pool.clone(), - subscriptions: Subscriptions::new(runtime.executor()), + subscriptions: Subscriptions::new(Arc::new(runtime.executor())), }; let (subscriber, id_rx, data) = ::jsonrpc_pubsub::typed::Subscriber::new_test("test"); @@ -128,7 +128,7 @@ fn should_return_pending_extrinsics() { let p = Author { client, pool: pool.clone(), - subscriptions: Subscriptions::new(runtime.executor()), + subscriptions: Subscriptions::new(Arc::new(runtime.executor())), }; let ex = uxt(AccountKeyring::Alice, 0); AuthorApi::submit_extrinsic(&p, ex.encode().into()).unwrap(); @@ -146,7 +146,7 @@ fn should_remove_extrinsics() { let p = Author { client, pool: pool.clone(), - subscriptions: Subscriptions::new(runtime.executor()), + subscriptions: Subscriptions::new(Arc::new(runtime.executor())), }; let ex1 = uxt(AccountKeyring::Alice, 0); p.submit_extrinsic(ex1.encode().into()).unwrap(); diff --git a/core/rpc/src/chain/mod.rs b/core/rpc/src/chain/mod.rs index 3594ed48e3aaf990919131c3779c096c400cb779..d1d476c3ab43dbf3656b4eedbff4112bb71f0da6 100644 --- a/core/rpc/src/chain/mod.rs +++ b/core/rpc/src/chain/mod.rs @@ -23,6 +23,7 @@ pub mod number; mod tests; use std::sync::Arc; +use futures03::{future, StreamExt as _, TryStreamExt as _}; use client::{self, Client, BlockchainEvents}; use crate::rpc::Result as RpcResult; @@ -203,8 +204,9 @@ impl ChainApi, Block::Hash, Block::Header, Sig subscriber, || self.block_hash(None.into()), || self.client.import_notification_stream() - .filter(|notification| notification.is_new_best) - .map(|notification| notification.header), + .filter(|notification| future::ready(notification.is_new_best)) + .map(|notification| Ok::<_, ()>(notification.header)) + .compat(), ) } @@ -217,7 +219,8 @@ impl ChainApi, Block::Hash, Block::Header, Sig subscriber, || Ok(Some(self.client.info().chain.finalized_hash)), || self.client.finality_notification_stream() - .map(|notification| notification.header), + .map(|notification| Ok::<_, ()>(notification.header)) + .compat(), ) } diff --git a/core/rpc/src/chain/tests.rs b/core/rpc/src/chain/tests.rs index eed9ae836b8b5cbdb88da859cb58a54b9ae16207..e6fa4d94e5b0dfa96bfebc615eeb9aede6e40082 100644 --- a/core/rpc/src/chain/tests.rs +++ b/core/rpc/src/chain/tests.rs @@ -29,7 +29,7 @@ fn should_return_header() { let client = Chain { client: Arc::new(test_client::new()), - subscriptions: Subscriptions::new(remote), + subscriptions: Subscriptions::new(Arc::new(remote)), }; assert_matches!( @@ -67,7 +67,7 @@ fn should_return_a_block() { let api = Chain { client: Arc::new(test_client::new()), - subscriptions: Subscriptions::new(remote), + subscriptions: Subscriptions::new(Arc::new(remote)), }; let block = api.client.new_block(Default::default()).unwrap().bake().unwrap(); @@ -121,7 +121,7 @@ fn should_return_block_hash() { let client = Chain { client: Arc::new(test_client::new()), - subscriptions: Subscriptions::new(remote), + subscriptions: Subscriptions::new(Arc::new(remote)), }; assert_matches!( @@ -165,7 +165,7 @@ fn should_return_finalized_hash() { let client = Chain { client: Arc::new(test_client::new()), - subscriptions: Subscriptions::new(remote), + subscriptions: Subscriptions::new(Arc::new(remote)), }; assert_matches!( @@ -199,7 +199,7 @@ fn should_notify_about_latest_block() { { let api = Chain { client: Arc::new(test_client::new()), - subscriptions: Subscriptions::new(remote), + subscriptions: Subscriptions::new(Arc::new(remote)), }; api.subscribe_new_head(Default::default(), subscriber); @@ -230,7 +230,7 @@ fn should_notify_about_finalized_block() { { let api = Chain { client: Arc::new(test_client::new()), - subscriptions: Subscriptions::new(remote), + subscriptions: Subscriptions::new(Arc::new(remote)), }; api.subscribe_finalized_heads(Default::default(), subscriber); diff --git a/core/rpc/src/state/mod.rs b/core/rpc/src/state/mod.rs index 0b3b93885e16cf4f1dc3338f172643fcb73d2698..40ee94fdb292e6b6217e14d44bd351661c2c73ab 100644 --- a/core/rpc/src/state/mod.rs +++ b/core/rpc/src/state/mod.rs @@ -26,6 +26,7 @@ use std::{ ops::Range, sync::Arc, }; +use futures03::{future, StreamExt as _, TryStreamExt as _}; use client::{self, Client, CallExecutor, BlockchainEvents, runtime_api::Metadata}; use crate::rpc::Result as RpcResult; @@ -484,14 +485,14 @@ impl StateApi for State where self.subscriptions.add(subscriber, |sink| { let stream = stream - .map_err(|e| warn!("Error creating storage notification stream: {:?}", e)) - .map(|(block, changes)| Ok(StorageChangeSet { + .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)) @@ -530,7 +531,6 @@ impl StateApi for State where let mut previous_version = version.clone(); let stream = stream - .map_err(|e| warn!("Error creating storage notification stream: {:?}", e)) .filter_map(move |_| { let info = client.info(); let version = client @@ -539,11 +539,12 @@ impl StateApi for State where .map_err(Into::into); if previous_version != version { previous_version = version.clone(); - Some(version) + future::ready(Some(Ok::<_, ()>(version))) } else { - None + future::ready(None) } - }); + }) + .compat(); sink .sink_map_err(|e| warn!("Error sending notifications: {:?}", e)) diff --git a/core/rpc/src/state/tests.rs b/core/rpc/src/state/tests.rs index f8cb19451337aae09b0c475aa67a9cb04f386791..6a8eefa10b660d2785e16740e7e064a959963e83 100644 --- a/core/rpc/src/state/tests.rs +++ b/core/rpc/src/state/tests.rs @@ -32,7 +32,7 @@ fn should_return_storage() { let core = tokio::runtime::Runtime::new().unwrap(); let client = Arc::new(test_client::new()); let genesis_hash = client.genesis_hash(); - let client = State::new(client, Subscriptions::new(core.executor())); + let client = State::new(client, Subscriptions::new(Arc::new(core.executor()))); let key = StorageKey(b":code".to_vec()); assert_eq!( @@ -57,7 +57,7 @@ fn should_return_child_storage() { .add_child_storage("test", "key", vec![42_u8]) .build()); let genesis_hash = client.genesis_hash(); - let client = State::new(client, Subscriptions::new(core.executor())); + let client = State::new(client, Subscriptions::new(Arc::new(core.executor()))); let child_key = StorageKey(well_known_keys::CHILD_STORAGE_KEY_PREFIX.iter().chain(b"test").cloned().collect()); let key = StorageKey(b"key".to_vec()); @@ -82,7 +82,7 @@ fn should_call_contract() { let core = tokio::runtime::Runtime::new().unwrap(); let client = Arc::new(test_client::new()); let genesis_hash = client.genesis_hash(); - let client = State::new(client, Subscriptions::new(core.executor())); + let client = State::new(client, Subscriptions::new(Arc::new(core.executor()))); assert_matches!( client.call("balanceOf".into(), Bytes(vec![1,2,3]), Some(genesis_hash).into()), @@ -97,7 +97,7 @@ fn should_notify_about_storage_changes() { let (subscriber, id, transport) = Subscriber::new_test("test"); { - let api = State::new(Arc::new(test_client::new()), Subscriptions::new(remote)); + let api = State::new(Arc::new(test_client::new()), Subscriptions::new(Arc::new(remote))); api.subscribe_storage(Default::default(), subscriber, None.into()); @@ -128,7 +128,7 @@ fn should_send_initial_storage_changes_and_notifications() { let (subscriber, id, transport) = Subscriber::new_test("test"); { - let api = State::new(Arc::new(test_client::new()), Subscriptions::new(remote)); + let api = State::new(Arc::new(test_client::new()), Subscriptions::new(Arc::new(remote))); let alice_balance_key = blake2_256(&runtime::system::balance_of_key(AccountKeyring::Alice.into())); @@ -163,7 +163,7 @@ fn should_send_initial_storage_changes_and_notifications() { fn should_query_storage() { fn run_tests(client: Arc) { let core = tokio::runtime::Runtime::new().unwrap(); - let api = State::new(client.clone(), Subscriptions::new(core.executor())); + let api = State::new(client.clone(), Subscriptions::new(Arc::new(core.executor()))); let add_block = |nonce| { let mut builder = client.new_block(Default::default()).unwrap(); @@ -254,7 +254,7 @@ fn should_return_runtime_version() { let core = tokio::runtime::Runtime::new().unwrap(); let client = Arc::new(test_client::new()); - let api = State::new(client.clone(), Subscriptions::new(core.executor())); + let api = State::new(client.clone(), Subscriptions::new(Arc::new(core.executor()))); let result = "{\"specName\":\"test\",\"implName\":\"parity-test\",\"authoringVersion\":1,\ \"specVersion\":1,\"implVersion\":1,\"apis\":[[\"0xdf6acb689907609b\",2],\ @@ -274,7 +274,7 @@ fn should_notify_on_runtime_version_initially() { { let client = Arc::new(test_client::new()); - let api = State::new(client.clone(), Subscriptions::new(core.executor())); + let api = State::new(client.clone(), Subscriptions::new(Arc::new(core.executor()))); api.subscribe_runtime_version(Default::default(), subscriber); diff --git a/core/rpc/src/subscriptions.rs b/core/rpc/src/subscriptions.rs index 500f3dac4545ccf966cf558f0dc94ccbb6f192de..77e1d958f683fed3f639f8093d048bd946984677 100644 --- a/core/rpc/src/subscriptions.rs +++ b/core/rpc/src/subscriptions.rs @@ -17,12 +17,11 @@ use std::collections::HashMap; use std::sync::{Arc, atomic::{self, AtomicUsize}}; -use log::warn; +use log::{error, warn}; use jsonrpc_pubsub::{SubscriptionId, typed::{Sink, Subscriber}}; use parking_lot::Mutex; use crate::rpc::futures::sync::oneshot; use crate::rpc::futures::{Future, future}; -use tokio::runtime::TaskExecutor; type Id = u64; @@ -50,16 +49,16 @@ impl IdProvider { /// /// Takes care of assigning unique subscription ids and /// driving the sinks into completion. -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Subscriptions { next_id: IdProvider, active_subscriptions: Arc>>>, - executor: TaskExecutor, + executor: Arc + Send>> + Send + Sync>, } impl Subscriptions { /// Creates new `Subscriptions` object. - pub fn new(executor: TaskExecutor) -> Self { + pub fn new(executor: Arc + Send>> + Send + Sync>) -> Self { Subscriptions { next_id: Default::default(), active_subscriptions: Default::default(), @@ -86,7 +85,9 @@ impl Subscriptions { .then(|_| Ok(())); self.active_subscriptions.lock().insert(id, tx); - self.executor.spawn(future); + if self.executor.execute(Box::new(future)).is_err() { + error!("Failed to spawn RPC subscription task"); + } } } diff --git a/core/service/Cargo.toml b/core/service/Cargo.toml index 5a81022362872c8dd9ec1e9316ce2d2215d5bde6..6a15e2eeb6c3186ee93523fe8bbbc4c4c906d678 100644 --- a/core/service/Cargo.toml +++ b/core/service/Cargo.toml @@ -7,16 +7,17 @@ edition = "2018" [dependencies] derive_more = "0.14.0" futures = "0.1.17" +futures03 = { package = "futures-preview", version = "0.3.0-alpha.17", features = ["compat"] } parking_lot = "0.8.0" lazy_static = "1.0" log = "0.4" slog = {version = "^2", features = ["nested-values"]} -tokio = "0.1.7" +tokio-executor = "0.1.7" tokio-timer = "0.2" exit-future = "0.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -sysinfo = "0.8.0" +sysinfo = "0.9.0" target_info = "0.1" keystore = { package = "substrate-keystore", path = "../../core/keystore" } sr-io = { path = "../../core/sr-io" } @@ -26,12 +27,13 @@ consensus_common = { package = "substrate-consensus-common", path = "../../core/ network = { package = "substrate-network", path = "../../core/network" } client = { package = "substrate-client", path = "../../core/client" } client_db = { package = "substrate-client-db", path = "../../core/client/db", features = ["kvdb-rocksdb"] } -parity-codec = "3.3" +parity-codec = "4.1.1" substrate-executor = { path = "../../core/executor" } transaction_pool = { package = "substrate-transaction-pool", path = "../../core/transaction-pool" } rpc = { package = "substrate-rpc-servers", path = "../../core/rpc-servers" } tel = { package = "substrate-telemetry", path = "../../core/telemetry" } offchain = { package = "substrate-offchain", path = "../../core/offchain" } +parity-multiaddr = { package = "parity-multiaddr", version = "0.5.0" } [dev-dependencies] substrate-test-runtime-client = { path = "../test-runtime/client" } diff --git a/core/service/src/chain_ops.rs b/core/service/src/chain_ops.rs index 6c4a03ee7b003e38d39fc33b85504d4d9725b9ac..dd2d26f8a3d8d55786d6713c8bb512710c916147 100644 --- a/core/service/src/chain_ops.rs +++ b/core/service/src/chain_ops.rs @@ -22,7 +22,7 @@ use log::{info, warn}; use runtime_primitives::generic::{SignedBlock, BlockId}; use runtime_primitives::traits::{SaturatedConversion, Zero, One, Block, Header, NumberFor}; -use consensus_common::import_queue::{ImportQueue, IncomingBlock, Link}; +use consensus_common::import_queue::{ImportQueue, IncomingBlock, Link, BlockImportError, BlockImportResult}; use network::message; use consensus_common::BlockOrigin; @@ -111,23 +111,35 @@ impl WaitLink { } impl Link for WaitLink { - fn block_imported(&mut self, _hash: &B::Hash, _number: NumberFor) { - self.imported_blocks += 1; + fn blocks_processed( + &mut self, + imported: usize, + count: usize, + results: Vec<(Result>, BlockImportError>, B::Hash)> + ) { + self.imported_blocks += imported as u64; + if results.iter().any(|(r, _)| r.is_err()) { + warn!("There was an error importing {} blocks", count); + } } } -/// Import blocks from a binary stream. +/// Returns a future that import blocks from a binary stream. pub fn import_blocks( mut config: FactoryFullConfiguration, exit: E, mut input: R -) -> error::Result<()> +) -> error::Result> where F: ServiceFactory, E: Future + Send + 'static, R: Read, { let client = new_client::(&config)?; // FIXME #1134 this shouldn't need a mutable config. let select_chain = components::FullComponents::::build_select_chain(&mut config, client.clone())?; - let mut queue = components::FullComponents::::build_import_queue(&mut config, client.clone(), select_chain)?; + let (mut queue, _) = components::FullComponents::::build_import_queue( + &mut config, + client.clone(), + select_chain + )?; let (exit_send, exit_recv) = std::sync::mpsc::channel(); ::std::thread::spawn(move || { @@ -169,13 +181,17 @@ pub fn import_blocks( } block_count = b; - if b % 1000 == 0 { + if b % 1000 == 0 && b != 0 { info!("#{} blocks were added to the queue", b); } } let mut link = WaitLink::new(); - tokio::run(futures::future::poll_fn(move || { + Ok(futures::future::poll_fn(move || { + if exit_recv.try_recv().is_ok() { + return Ok(Async::Ready(())); + } + let blocks_before = link.imported_blocks; queue.poll_actions(&mut link); if link.imported_blocks / 1000 != blocks_before / 1000 { @@ -186,15 +202,12 @@ pub fn import_blocks( ); } if link.imported_blocks >= count { + info!("Imported {} blocks. Best: #{}", block_count, client.info().chain.best_number); Ok(Async::Ready(())) } else { Ok(Async::NotReady) } - })); - - info!("Imported {} blocks. Best: #{}", block_count, client.info().chain.best_number); - - Ok(()) + })) } /// Revert the chain. diff --git a/core/service/src/components.rs b/core/service/src/components.rs index a41f9e94fff40f647d1ac37bbb3cc9f4eb0642ee..e81380b3202fd23b48e0a079576828826fafc8ab 100644 --- a/core/service/src/components.rs +++ b/core/service/src/components.rs @@ -16,25 +16,23 @@ //! Substrate service components. -use std::{sync::Arc, net::SocketAddr, ops::Deref, ops::DerefMut}; +use std::{sync::Arc, ops::Deref, ops::DerefMut}; use serde::{Serialize, de::DeserializeOwned}; -use tokio::runtime::TaskExecutor; use crate::chain_spec::ChainSpec; use client_db; use client::{self, Client, runtime_api}; -use crate::{error, Service, maybe_start_server}; +use crate::{error, Service, AuthorityKeyProvider}; use consensus_common::{import_queue::ImportQueue, SelectChain}; -use network::{self, OnDemand, FinalityProofProvider}; +use network::{self, OnDemand, FinalityProofProvider, NetworkStateInfo, config::BoxFinalityProofRequestBuilder}; use substrate_executor::{NativeExecutor, NativeExecutionDispatch}; use transaction_pool::txpool::{self, Options as TransactionPoolOptions, Pool as TransactionPool}; use runtime_primitives::{ BuildStorage, traits::{Block as BlockT, Header as HeaderT, ProvideRuntimeApi}, generic::BlockId }; use crate::config::Configuration; -use primitives::{Blake2Hasher, H256}; +use primitives::{Blake2Hasher, H256, Pair}; use rpc::{self, apis::system::SystemInfo}; -use parking_lot::Mutex; -use futures::sync::mpsc; +use futures::{prelude::*, future::Executor, sync::mpsc}; // Type aliases. // These exist mainly to avoid typing `::Foo` all over the code. @@ -122,9 +120,24 @@ pub type ComponentClient = Client< ::RuntimeApi, >; +/// A offchain workers storage backend type. +pub type ComponentOffchainStorage = < + ::Backend as client::backend::Backend, Blake2Hasher> +>::OffchainStorage; + /// Block type for `Components` pub type ComponentBlock = <::Factory as ServiceFactory>::Block; +/// ConsensusPair type for `Components` +pub type ComponentConsensusPair = <::Factory as ServiceFactory>::ConsensusPair; + +/// FinalityPair type for `Components` +pub type ComponentFinalityPair = <::Factory as ServiceFactory>::FinalityPair; + +/// AuthorityKeyProvider type for `Components` +pub type ComponentAuthorityKeyProvider = + AuthorityKeyProvider, ComponentConsensusPair, ComponentFinalityPair>; + /// Extrinsic hash type for `Components` pub type ComponentExHash = <::TransactionPoolApi as txpool::ChainApi>::Hash; @@ -140,72 +153,37 @@ impl RuntimeGenesis for T {} /// Something that can start the RPC service. pub trait StartRPC { - type ServersHandle: Send + Sync; - fn start_rpc( client: Arc>, system_send_back: mpsc::UnboundedSender>>, system_info: SystemInfo, - rpc_http: Option, - rpc_ws: Option, - rpc_ws_max_connections: Option, - rpc_cors: Option>, task_executor: TaskExecutor, transaction_pool: Arc>, - ) -> error::Result; + ) -> rpc::RpcHandler; } impl StartRPC for C where ComponentClient: ProvideRuntimeApi, as ProvideRuntimeApi>::Api: runtime_api::Metadata>, { - type ServersHandle = (Option, Option>); - fn start_rpc( client: Arc>, system_send_back: mpsc::UnboundedSender>>, rpc_system_info: SystemInfo, - rpc_http: Option, - rpc_ws: Option, - rpc_ws_max_connections: Option, - rpc_cors: Option>, task_executor: TaskExecutor, transaction_pool: Arc>, - ) -> error::Result { - let handler = || { - let client = client.clone(); - let subscriptions = rpc::apis::Subscriptions::new(task_executor.clone()); - let chain = rpc::apis::chain::Chain::new(client.clone(), subscriptions.clone()); - let state = rpc::apis::state::State::new(client.clone(), subscriptions.clone()); - let author = rpc::apis::author::Author::new( - client.clone(), transaction_pool.clone(), subscriptions - ); - let system = rpc::apis::system::System::new( - rpc_system_info.clone(), system_send_back.clone() - ); - rpc::rpc_handler::, ComponentExHash, _, _, _, _>( - state, - chain, - author, - system, - ) - }; - - Ok(( - maybe_start_server( - rpc_http, - |address| rpc::start_http(address, rpc_cors.as_ref(), handler()), - )?, - maybe_start_server( - rpc_ws, - |address| rpc::start_ws( - address, - rpc_ws_max_connections, - rpc_cors.as_ref(), - handler(), - ), - )?.map(Mutex::new), - )) + ) -> rpc::RpcHandler { + let subscriptions = rpc::apis::Subscriptions::new(task_executor.clone()); + let chain = rpc::apis::chain::Chain::new(client.clone(), subscriptions.clone()); + let state = rpc::apis::state::State::new(client.clone(), subscriptions.clone()); + let author = rpc::apis::author::Author::new(client, transaction_pool, subscriptions); + let system = rpc::apis::system::System::new(rpc_system_info, system_send_back); + rpc::rpc_handler::, ComponentExHash, _, _, _, _>( + state, + chain, + author, + system, + ) } } @@ -223,7 +201,7 @@ fn maintain_transaction_pool( client: &Client, transaction_pool: &TransactionPool, ) -> error::Result<()> where - Block: BlockT::Out>, + Block: BlockT::Out>, Backend: client::backend::Backend, Client: ProvideRuntimeApi, as ProvideRuntimeApi>::Api: runtime_api::TaggedTransactionQueue, @@ -260,9 +238,15 @@ impl MaintainTransactionPool for C where pub trait OffchainWorker { fn offchain_workers( number: &FactoryBlockNumber, - offchain: &offchain::OffchainWorkers, ComponentBlock>, + offchain: &offchain::OffchainWorkers< + ComponentClient, + ComponentOffchainStorage, + ComponentAuthorityKeyProvider, + ComponentBlock + >, pool: &Arc>, - ) -> error::Result<()>; + network_state: &Arc, + ) -> error::Result + Send>>; } impl OffchainWorker for C where @@ -271,10 +255,16 @@ impl OffchainWorker for C where { fn offchain_workers( number: &FactoryBlockNumber, - offchain: &offchain::OffchainWorkers, ComponentBlock>, + offchain: &offchain::OffchainWorkers< + ComponentClient, + ComponentOffchainStorage, + ComponentAuthorityKeyProvider, + ComponentBlock + >, pool: &Arc>, - ) -> error::Result<()> { - Ok(offchain.on_block_imported(number, pool)) + network_state: &Arc, + ) -> error::Result + Send>> { + Ok(Box::new(offchain.on_block_imported(number, pool, network_state.clone()))) } } @@ -282,7 +272,6 @@ impl OffchainWorker for C where pub trait ServiceTrait: Deref> + Send - + Sync + 'static + StartRPC + MaintainTransactionPool @@ -291,17 +280,23 @@ pub trait ServiceTrait: impl ServiceTrait for T where T: Deref> + Send - + Sync + 'static + StartRPC + MaintainTransactionPool + OffchainWorker {} +/// Alias for a an implementation of `futures::future::Executor`. +pub type TaskExecutor = Arc + Send>> + Send + Sync>; + /// A collection of types and methods to build a service on top of the substrate service. pub trait ServiceFactory: 'static + Sized { /// Block type. type Block: BlockT; + /// Consensus crypto type. + type ConsensusPair: Pair; + /// Finality crypto type. + type FinalityPair: Pair; /// The type that implements the runtime API. type RuntimeApi: Send + Sync; /// Network protocol extensions. @@ -351,10 +346,10 @@ pub trait ServiceFactory: 'static + Sized { ) -> Result; /// Build full service. - fn new_full(config: FactoryFullConfiguration, executor: TaskExecutor) + fn new_full(config: FactoryFullConfiguration) -> Result; /// Build light service. - fn new_light(config: FactoryFullConfiguration, executor: TaskExecutor) + fn new_light(config: FactoryFullConfiguration) -> Result; /// ImportQueue for a full client @@ -377,7 +372,7 @@ pub trait ServiceFactory: 'static + Sized { fn build_light_import_queue( config: &mut FactoryFullConfiguration, _client: Arc> - ) -> Result { + ) -> Result<(Self::LightImportQueue, BoxFinalityProofRequestBuilder), error::Error> { if let Some(name) = config.chain_spec.consensus_engine() { match name { _ => Err(format!("Chain Specification defines unknown consensus engine '{}'", name).into()) @@ -428,12 +423,13 @@ pub trait Components: Sized + 'static { fn build_transaction_pool(config: TransactionPoolOptions, client: Arc>) -> Result, error::Error>; - /// instance of import queue for clients + /// Build the queue that imports blocks from the network, and optionally a way for the network + /// to build requests for proofs of finality. fn build_import_queue( config: &mut FactoryFullConfiguration, client: Arc>, select_chain: Option, - ) -> Result; + ) -> Result<(Self::ImportQueue, Option>>), error::Error>; /// Finality proof provider for serving network requests. fn build_finality_proof_provider( @@ -455,12 +451,11 @@ pub struct FullComponents { impl FullComponents { /// Create new `FullComponents` pub fn new( - config: FactoryFullConfiguration, - task_executor: TaskExecutor + config: FactoryFullConfiguration ) -> Result { Ok( Self { - service: Service::new(config, task_executor)?, + service: Service::new(config)?, } ) } @@ -480,6 +475,15 @@ impl DerefMut for FullComponents { } } +impl Future for FullComponents { + type Item = (); + type Error = (); + + fn poll(&mut self) -> Poll { + self.service.poll() + } +} + impl Components for FullComponents { type Factory = Factory; type Executor = FullExecutor; @@ -504,7 +508,7 @@ impl Components for FullComponents { state_cache_size: config.state_cache_size, state_cache_child_ratio: config.state_cache_child_ratio.map(|v| (v, 100)), - path: config.database_path.as_str().into(), + path: config.database_path.clone(), pruning: config.pruning.clone(), }; Ok((Arc::new(client_db::new_client( @@ -526,10 +530,11 @@ impl Components for FullComponents { config: &mut FactoryFullConfiguration, client: Arc>, select_chain: Option, - ) -> Result { + ) -> Result<(Self::ImportQueue, Option>>), error::Error> { let select_chain = select_chain .ok_or(error::Error::SelectChainRequired)?; Factory::build_full_import_queue(config, client, select_chain) + .map(|queue| (queue, None)) } fn build_select_chain( @@ -555,11 +560,10 @@ impl LightComponents { /// Create new `LightComponents` pub fn new( config: FactoryFullConfiguration, - task_executor: TaskExecutor ) -> Result { Ok( Self { - service: Service::new(config, task_executor)?, + service: Service::new(config)?, } ) } @@ -573,6 +577,15 @@ impl Deref for LightComponents { } } +impl Future for LightComponents { + type Item = (); + type Error = (); + + fn poll(&mut self) -> Poll { + self.service.poll() + } +} + impl Components for LightComponents { type Factory = Factory; type Executor = LightExecutor; @@ -598,7 +611,7 @@ impl Components for LightComponents { state_cache_size: config.state_cache_size, state_cache_child_ratio: config.state_cache_child_ratio.map(|v| (v, 100)), - path: config.database_path.as_str().into(), + path: config.database_path.clone(), pruning: config.pruning.clone(), }; let db_storage = client_db::light::LightStorage::new(db_settings)?; @@ -620,8 +633,9 @@ impl Components for LightComponents { config: &mut FactoryFullConfiguration, client: Arc>, _select_chain: Option, - ) -> Result { + ) -> Result<(Self::ImportQueue, Option>>), error::Error> { Factory::build_light_import_queue(config, client) + .map(|(queue, builder)| (queue, Some(builder))) } fn build_finality_proof_provider( diff --git a/core/service/src/config.rs b/core/service/src/config.rs index 3a49630898d42c4eff7c815db4461c986d5b17f9..6f2f51ebfb6ea6a69bf61f899adcedb8f940585f 100644 --- a/core/service/src/config.rs +++ b/core/service/src/config.rs @@ -16,12 +16,14 @@ //! Service configuration. -use std::net::SocketAddr; -use transaction_pool; -use crate::chain_spec::ChainSpec; pub use client::ExecutionStrategies; pub use client_db::PruningMode; -pub use network::config::{NetworkConfiguration, Roles}; +pub use network::config::{ExtTransport, NetworkConfiguration, Roles}; + +use std::{path::PathBuf, net::SocketAddr}; +use transaction_pool; +use crate::chain_spec::ChainSpec; +use primitives::crypto::Protected; use runtime_primitives::BuildStorage; use serde::{Serialize, de::DeserializeOwned}; use target_info::Target; @@ -43,15 +45,15 @@ pub struct Configuration { /// Network configuration. pub network: NetworkConfiguration, /// Path to key files. - pub keystore_path: String, + pub keystore_path: Option, /// Path to the database. - pub database_path: String, + pub database_path: PathBuf, /// Cache Size for internal database in MiB pub database_cache_size: Option, /// Size of internal state cache in Bytes pub state_cache_size: usize, - /// Size in percent of cache size dedicated to child tries - pub state_cache_child_ratio: Option, + /// Size in percent of cache size dedicated to child tries + pub state_cache_child_ratio: Option, /// Pruning settings. pub pruning: PruningMode, /// Additional key seeds. @@ -74,6 +76,9 @@ pub struct Configuration { pub rpc_cors: Option>, /// Telemetry service URL. `None` if disabled. pub telemetry_endpoints: Option, + /// External WASM transport for the telemetry. If `Some`, when connection to a telemetry + /// endpoint, this transport will be tried in priority before all others. + pub telemetry_external_transport: Option, /// The default number of 64KB pages to allocate for Wasm execution pub default_heap_pages: Option, /// Should offchain workers be executed. @@ -82,8 +87,11 @@ pub struct Configuration { pub force_authoring: bool, /// Disable GRANDPA when running in validator mode pub disable_grandpa: bool, + /// Run GRANDPA voter even when no additional key seed is specified. This can for example be of interest when + /// running a sentry node in front of a validator, thus needing to forward GRANDPA gossip messages. + pub grandpa_voter: bool, /// Node keystore's password - pub password: String, + pub password: Protected, } impl Configuration { @@ -112,11 +120,13 @@ impl Configuration String { let commit_dash = if impl_commit.is_empty() { "" } else { "-" }; format!("{}{}{}-{}", impl_version, commit_dash, impl_commit, platform()) } - diff --git a/core/service/src/lib.rs b/core/service/src/lib.rs index 0b13852627c18911b7889c3bbfc0169f8cb86173..68a4bd45b97ee1a64646ab80e09da92e4de55d10 100644 --- a/core/service/src/lib.rs +++ b/core/service/src/lib.rs @@ -26,21 +26,24 @@ pub mod chain_ops; pub mod error; use std::io; +use std::marker::PhantomData; use std::net::SocketAddr; use std::collections::HashMap; -use std::time::Duration; +use std::time::{Duration, Instant}; use futures::sync::mpsc; use parking_lot::Mutex; -use client::{BlockchainEvents, backend::Backend}; +use client::{BlockchainEvents, backend::Backend, runtime_api::BlockT}; use exit_future::Signal; use futures::prelude::*; +use futures03::stream::{StreamExt as _, TryStreamExt as _}; use keystore::Store as Keystore; -use log::{info, warn, debug}; +use network::{NetworkState, NetworkStateInfo}; +use log::{log, info, warn, debug, error, Level}; use parity_codec::{Encode, Decode}; -use primitives::Pair; +use primitives::{Pair, ed25519, crypto}; use runtime_primitives::generic::BlockId; -use runtime_primitives::traits::{Header, NumberFor, SaturatedConversion}; +use runtime_primitives::traits::{Header, NumberFor, SaturatedConversion, Zero}; use substrate_executor::NativeExecutor; use sysinfo::{get_current_pid, ProcessExt, System, SystemExt}; use tel::{telemetry, SUBSTRATE_INFO}; @@ -51,43 +54,60 @@ pub use chain_spec::{ChainSpec, Properties}; pub use transaction_pool::txpool::{ self, Pool as TransactionPool, Options as TransactionPoolOptions, ChainApi, IntoPoolError }; -use client::runtime_api::BlockT; pub use client::FinalityNotifications; -pub use components::{ServiceFactory, FullBackend, FullExecutor, LightBackend, - LightExecutor, Components, PoolApi, ComponentClient, +pub use components::{ + ServiceFactory, FullBackend, FullExecutor, LightBackend, ComponentAuthorityKeyProvider, + LightExecutor, Components, PoolApi, ComponentClient, ComponentOffchainStorage, ComponentBlock, FullClient, LightClient, FullComponents, LightComponents, CodeExecutor, NetworkService, FactoryChainSpec, FactoryBlock, FactoryFullConfiguration, RuntimeGenesis, FactoryGenesis, - ComponentExHash, ComponentExtrinsic, FactoryExtrinsic + ComponentExHash, ComponentExtrinsic, FactoryExtrinsic, + ComponentConsensusPair, ComponentFinalityPair, }; use components::{StartRPC, MaintainTransactionPool, OffchainWorker}; #[doc(hidden)] pub use std::{ops::Deref, result::Result, sync::Arc}; #[doc(hidden)] -pub use network::{FinalityProofProvider, OnDemand}; +pub use network::{FinalityProofProvider, OnDemand, config::BoxFinalityProofRequestBuilder}; #[doc(hidden)] -pub use tokio::runtime::TaskExecutor; +pub use futures::future::Executor; const DEFAULT_PROTOCOL_ID: &str = "sup"; /// Substrate service. pub struct Service { client: Arc>, - select_chain: Option<::SelectChain>, + select_chain: Option, network: Arc>, /// Sinks to propagate network status updates. - network_status_sinks: Arc>>>>>, + network_status_sinks: Arc>, NetworkState + )>>>>, transaction_pool: Arc>, - keystore: Keystore, + keystore: ComponentAuthorityKeyProvider, exit: ::exit_future::Exit, signal: Option, + /// Sender for futures that must be spawned as background tasks. + to_spawn_tx: mpsc::UnboundedSender + Send>>, + /// Receiver for futures that must be spawned as background tasks. + to_spawn_rx: mpsc::UnboundedReceiver + Send>>, + /// List of futures to poll from `poll`. + /// If spawning a background task is not possible, we instead push the task into this `Vec`. + /// The elements must then be polled manually. + to_poll: Vec + Send>>, /// Configuration of this Service pub config: FactoryFullConfiguration, + rpc_handlers: rpc::RpcHandler, _rpc: Box, _telemetry: Option, - _offchain_workers: Option, ComponentBlock>>>, _telemetry_on_connect_sinks: Arc>>>, + _offchain_workers: Option, + ComponentOffchainStorage, + ComponentAuthorityKeyProvider, + ComponentBlock> + >>, } /// Creates bare client without any networking. @@ -102,17 +122,33 @@ pub fn new_client(config: &FactoryFullConfi Ok(client) } +/// An handle for spawning tasks in the service. +#[derive(Clone)] +pub struct SpawnTaskHandle { + sender: mpsc::UnboundedSender + Send>>, +} + +impl Executor + Send>> for SpawnTaskHandle { + fn execute( + &self, + future: Box + Send> + ) -> Result<(), futures::future::ExecuteError + Send>>> { + if let Err(err) = self.sender.unbounded_send(future) { + let kind = futures::future::ExecuteErrorKind::Shutdown; + Err(futures::future::ExecuteError::new(kind, err.into_inner())) + } else { + Ok(()) + } + } +} + /// Stream of events for connection established to a telemetry server. pub type TelemetryOnConnectNotifications = mpsc::UnboundedReceiver<()>; /// Used to hook on telemetry connection established events. -pub struct TelemetryOnConnect<'a> { - /// Handle to a future that will resolve on exit. - pub on_exit: Box + Send + 'static>, +pub struct TelemetryOnConnect { /// Event stream. pub telemetry_connection_sinks: TelemetryOnConnectNotifications, - /// Executor to which the hook is spawned. - pub executor: &'a TaskExecutor, } impl Service { @@ -126,39 +162,59 @@ impl Service { /// Creates a new service. pub fn new( mut config: FactoryFullConfiguration, - task_executor: TaskExecutor, ) -> Result { 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 client let executor = NativeExecutor::new(config.default_heap_pages); - let mut keystore = Keystore::open(config.keystore_path.as_str().into())?; + let mut keystore = if let Some(keystore_path) = config.keystore_path.as_ref() { + match Keystore::open(keystore_path.clone()) { + Ok(ks) => Some(ks), + Err(err) => { + error!("Failed to initialize keystore: {}", err); + None + } + } + } else { + None + }; + + // Keep the public key for telemetry + let public_key: String; // This is meant to be for testing only // FIXME #1063 remove this - for seed in &config.keys { - keystore.generate_from_seed(seed)?; - } - // Keep the public key for telemetry - let public_key = match keystore.contents()?.get(0) { - Some(public_key) => public_key.clone(), - None => { - let key = keystore.generate(&config.password)?; - let public_key = key.public(); - info!("Generated a new keypair: {:?}", public_key); - - public_key + if let Some(keystore) = keystore.as_mut() { + for seed in &config.keys { + keystore.generate_from_seed::(seed)?; } - }; + + public_key = match keystore.contents::()?.get(0) { + Some(public_key) => public_key.to_string(), + None => { + let key: ed25519::Pair = keystore.generate(&config.password.as_ref())?; + let public_key = key.public(); + info!("Generated a new keypair: {:?}", public_key); + public_key.to_string() + } + } + } else { + public_key = format!(""); + } let (client, on_demand) = Components::build_client(&config, executor)?; let select_chain = Components::build_select_chain(&mut config, client.clone())?; - let import_queue = Box::new(Components::build_import_queue( + let (import_queue, finality_proof_request_builder) = Components::build_import_queue( &mut config, client.clone(), select_chain.clone(), - )?); + )?; + let import_queue = Box::new(import_queue); let finality_proof_provider = Components::build_finality_proof_provider(client.clone())?; let chain_info = client.info().chain; @@ -189,7 +245,7 @@ impl Service { DEFAULT_PROTOCOL_ID } }.as_bytes(); - network::ProtocolId::from(protocol_id_full) + network::config::ProtocolId::from(protocol_id_full) }; let network_params = network::config::Params { @@ -197,6 +253,7 @@ impl Service { 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, @@ -209,35 +266,44 @@ impl Service { let network = network_mut.service().clone(); let network_status_sinks = Arc::new(Mutex::new(Vec::new())); - task_executor.spawn(build_network_future(network_mut, network_status_sinks.clone()) - .map_err(|_| ()) - .select(exit.clone()) - .then(|_| Ok(()))); + let keystore_authority_key = AuthorityKeyProvider { + _marker: PhantomData, + roles: config.roles, + password: config.password.clone(), + keystore: keystore.map(Arc::new), + }; - let offchain_workers = if config.offchain_worker { - Some(Arc::new(offchain::OffchainWorkers::new( - client.clone(), - task_executor.clone(), - ))) - } else { - None + #[allow(deprecated)] + let offchain_storage = client.backend().offchain_storage(); + let offchain_workers = match (config.offchain_worker, offchain_storage) { + (true, Some(db)) => { + Some(Arc::new(offchain::OffchainWorkers::new( + client.clone(), + db, + keystore_authority_key.clone(), + config.password.clone(), + ))) + }, + (true, None) => { + log::warn!("Offchain workers disabled, due to lack of offchain storage support in backend."); + None + }, + _ => None, }; { // block notifications - let network = Arc::downgrade(&network); 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 events = client.import_notification_stream() + .map(|v| Ok::<_, ()>(v)).compat() .for_each(move |notification| { let number = *notification.header.number(); - if let Some(network) = network.upgrade() { - network.on_block_imported(notification.hash, notification.header); - } - if let (Some(txpool), Some(client)) = (txpool.upgrade(), wclient.upgrade()) { Components::RuntimeServices::maintain_transaction_pool( &BlockId::hash(notification.hash), @@ -247,64 +313,20 @@ impl Service { } if let (Some(txpool), Some(offchain)) = (txpool.upgrade(), offchain.as_ref().and_then(|o| o.upgrade())) { - Components::RuntimeServices::offchain_workers( + let future = Components::RuntimeServices::offchain_workers( &number, &offchain, &txpool, + &network_state_info, ).map_err(|e| warn!("Offchain workers error processing new block: {:?}", e))?; + let _ = to_spawn_tx_.unbounded_send(future); } Ok(()) }) .select(exit.clone()) .then(|_| Ok(())); - task_executor.spawn(events); - } - - { - // finality notifications - let network = Arc::downgrade(&network); - - // A utility stream that drops all ready items and only returns the last one. - // This is used to only keep the last finality notification and avoid - // overloading the sync module with notifications. - struct MostRecentNotification(futures::stream::Fuse>); - - impl Stream for MostRecentNotification { - type Item = as Stream>::Item; - type Error = as Stream>::Error; - - fn poll(&mut self) -> Poll, Self::Error> { - let mut last = None; - let last = loop { - match self.0.poll()? { - Async::Ready(Some(item)) => { last = Some(item) } - Async::Ready(None) => match last { - None => return Ok(Async::Ready(None)), - Some(last) => break last, - }, - Async::NotReady => match last { - None => return Ok(Async::NotReady), - Some(last) => break last, - }, - } - }; - - Ok(Async::Ready(Some(last))) - } - } - - let events = MostRecentNotification(client.finality_notification_stream().fuse()) - .for_each(move |notification| { - if let Some(network) = network.upgrade() { - network.on_block_finalized(notification.hash, notification.header); - } - Ok(()) - }) - .select(exit.clone()) - .then(|_| Ok(())); - - task_executor.spawn(events); + let _ = to_spawn_tx.unbounded_send(Box::new(events)); } { @@ -326,18 +348,17 @@ impl Service { .select(exit.clone()) .then(|_| Ok(())); - task_executor.spawn(events); + let _ = to_spawn_tx.unbounded_send(Box::new(events)); } // Periodically notify the telemetry. let transaction_pool_ = transaction_pool.clone(); let client_ = client.clone(); - let network_ = network.clone(); let mut sys = System::new(); - let self_pid = get_current_pid(); - let (netstat_tx, netstat_rx) = mpsc::unbounded(); + let self_pid = get_current_pid().ok(); + let (netstat_tx, netstat_rx) = mpsc::unbounded::<(NetworkStatus>, NetworkState)>(); network_status_sinks.lock().push(netstat_tx); - task_executor.spawn(netstat_rx.for_each(move |net_status| { + 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; @@ -355,13 +376,14 @@ impl Service { }; // get cpu usage and memory usage of this process - let (cpu_usage, memory) = 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()) + 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) }; - let network_state = network_.network_state(); - telemetry!( SUBSTRATE_INFO; "system.interval"; @@ -380,32 +402,39 @@ impl Service { ); Ok(()) - }).select(exit.clone()).then(|_| Ok(()))); + }).select(exit.clone()).then(|_| Ok(())); + let _ = to_spawn_tx.unbounded_send(Box::new(tel_task)); // RPC - let system_info = rpc::apis::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(), - }; let (system_rpc_tx, system_rpc_rx) = mpsc::unbounded(); - let rpc = Components::RuntimeServices::start_rpc( + let gen_handler = || { + let system_info = rpc::apis::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(), + }; + Components::RuntimeServices::start_rpc( + client.clone(), + system_rpc_tx.clone(), + system_info.clone(), + Arc::new(SpawnTaskHandle { sender: to_spawn_tx.clone() }), + transaction_pool.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(), - system_rpc_tx, - system_info, - config.rpc_http, - config.rpc_ws, - config.rpc_ws_max_connections, - config.rpc_cors.clone(), - task_executor.clone(), - transaction_pool.clone(), - )?; - task_executor.spawn(build_system_rpc_handler::( - network.clone(), + network_status_sinks.clone(), system_rpc_rx, has_bootnodes - )); + ) + .map_err(|_| ()) + .select(exit.clone()) + .then(|_| Ok(())))); let telemetry_connection_sinks: Arc>>> = Default::default(); @@ -413,7 +442,6 @@ impl Service { let telemetry = config.telemetry_endpoints.clone().map(|endpoints| { let is_authority = config.roles == Roles::AUTHORITY; let network_id = network.local_peer_id().to_base58(); - let pubkey = format!("{}", public_key); let name = config.name.clone(); let impl_name = config.impl_name.to_owned(); let version = version.clone(); @@ -421,9 +449,11 @@ impl Service { let telemetry_connection_sinks_ = telemetry_connection_sinks.clone(); let telemetry = tel::init_telemetry(tel::TelemetryConfig { endpoints, - wasm_external_transport: None, + 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; @@ -434,7 +464,7 @@ impl Service { "version" => version.clone(), "config" => "", "chain" => chain_name.clone(), - "pubkey" => &pubkey, + "pubkey" => &public_key, "authority" => is_authority, "network_id" => network_id.clone() ); @@ -444,9 +474,9 @@ impl Service { }); Ok(()) }); - task_executor.spawn(future + let _ = to_spawn_tx.unbounded_send(Box::new(future .select(exit.clone()) - .then(|_| Ok(()))); + .then(|_| Ok(())))); telemetry }); @@ -457,10 +487,14 @@ impl Service { select_chain, transaction_pool, signal: Some(signal), - keystore, + to_spawn_tx, + to_spawn_rx, + to_poll: Vec::new(), + keystore: keystore_authority_key, config, exit, - _rpc: Box::new(rpc), + rpc_handlers, + _rpc: rpc, _telemetry: telemetry, _offchain_workers: offchain_workers, _telemetry_on_connect_sinks: telemetry_connection_sinks.clone(), @@ -468,25 +502,51 @@ impl Service { } /// give the authority key, if we are an authority and have a key - pub fn authority_key(&self) -> Option { - if self.config.roles != Roles::AUTHORITY { return None } - let keystore = &self.keystore; - if let Ok(Some(Ok(key))) = keystore.contents().map(|keys| keys.get(0) - .map(|k| keystore.load(k, &self.config.password))) - { - Some(key) - } else { - None - } + pub fn authority_key(&self) -> Option> { + use offchain::AuthorityKeyProvider; + + self.keystore.authority_key(&BlockId::Number(Zero::zero())) + } + + /// give the authority key, if we are an authority and have a key + pub fn fg_authority_key(&self) -> Option> { + use offchain::AuthorityKeyProvider; + + self.keystore.fg_authority_key(&BlockId::Number(Zero::zero())) } /// return a shared instance of Telemetry (if enabled) pub fn telemetry(&self) -> Option { self._telemetry.as_ref().map(|t| t.clone()) } -} -impl Service where Components: components::Components { + /// Spawns a task in the background that runs the future passed as parameter. + pub fn spawn_task(&self, task: impl Future + Send + 'static) { + let _ = self.to_spawn_tx.unbounded_send(Box::new(task)); + } + + /// Returns a handle for spawning tasks. + pub fn spawn_task_handle(&self) -> SpawnTaskHandle { + SpawnTaskHandle { + sender: self.to_spawn_tx.clone(), + } + } + + /// Starts an RPC query. + /// + /// The query is passed as a string and must be a JSON text similar to what an HTTP client + /// would for example send. + /// + /// Returns a `Future` that contains the optional response. + /// + /// If the request subscribes you to events, the `Sender` in the `RpcSession` object is used to + /// send back spontaneous events. + pub fn rpc_query(&self, mem: &RpcSession, request: &str) + -> impl Future, Error = ()> + { + self.rpc_handlers.handle_request(request, mem.metadata.clone()) + } + /// Get shared client instance. pub fn client(&self) -> Arc> { self.client.clone() @@ -503,7 +563,7 @@ impl Service where Components: components::Components { } /// Returns a receiver that periodically receives a status of the network. - pub fn network_status(&self) -> mpsc::UnboundedReceiver>> { + pub fn network_status(&self) -> mpsc::UnboundedReceiver<(NetworkStatus>, NetworkState)> { let (sink, stream) = mpsc::unbounded(); self.network_status_sinks.lock().push(sink); stream @@ -514,29 +574,123 @@ impl Service where Components: components::Components { self.transaction_pool.clone() } - /// Get shared keystore. - pub fn keystore(&self) -> &Keystore { - &self.keystore - } - /// Get a handle to a future that will resolve on exit. pub fn on_exit(&self) -> ::exit_future::Exit { self.exit.clone() } } +impl Future for Service where Components: components::Components { + type Item = (); + type Error = (); + + fn poll(&mut self) -> Poll { + while let Ok(Async::Ready(Some(task_to_spawn))) = self.to_spawn_rx.poll() { + let executor = tokio_executor::DefaultExecutor::current(); + if let Err(err) = executor.execute(task_to_spawn) { + debug!( + target: "service", + "Failed to spawn background task: {:?}; falling back to manual polling", + err + ); + self.to_poll.push(err.into_future()); + } + } + + // Polling all the `to_poll` futures. + while let Some(pos) = self.to_poll.iter_mut().position(|t| t.poll().map(|t| t.is_ready()).unwrap_or(true)) { + self.to_poll.remove(pos); + } + + // The service future never ends. + Ok(Async::NotReady) + } +} + +impl Executor + Send>> + for Service where Components: components::Components +{ + fn execute( + &self, + future: Box + Send> + ) -> Result<(), futures::future::ExecuteError + Send>>> { + if let Err(err) = self.to_spawn_tx.unbounded_send(future) { + let kind = futures::future::ExecuteErrorKind::Shutdown; + Err(futures::future::ExecuteError::new(kind, err.into_inner())) + } else { + Ok(()) + } + } +} + /// Builds a never-ending future that continuously polls the network. /// /// The `status_sink` contain a list of senders to send a periodic network status to. -fn build_network_future, H: network::ExHashT>( - mut network: network::NetworkWorker, - status_sinks: Arc>>>>, +fn build_network_future< + Components: components::Components, + S: network::specialization::NetworkSpecialization>, + H: network::ExHashT +> ( + mut network: network::NetworkWorker, S, H>, + client: Arc>, + status_sinks: Arc>, NetworkState)>>>>, + mut rpc_rx: mpsc::UnboundedReceiver>>, + should_have_peers: bool, ) -> impl Future { // 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() + .map(|v| Ok::<_, ()>(v)).compat(); + futures::future::poll_fn(move || { + let before_polling = Instant::now(); + + // We poll `imported_blocks_stream`. + while let Ok(Async::Ready(Some(notification))) = imported_blocks_stream.poll() { + network.on_block_imported(notification.hash, notification.header); + } + + // We poll `finality_notification_stream`, but we only take the last event. + let mut last = None; + while let Ok(Async::Ready(Some(item))) = finality_notification_stream.poll() { + last = Some(item); + } + if let Some(notification) = last { + network.on_block_finalized(notification.hash, notification.header); + } + + // Poll the RPC requests and answer them. + while let Ok(Async::Ready(Some(request))) = rpc_rx.poll() { + match request { + rpc::apis::system::Request::Health(sender) => { + let _ = sender.send(rpc::apis::system::Health { + peers: network.peers_debug_info().len(), + is_syncing: network.service().is_major_syncing(), + should_have_peers, + }); + }, + rpc::apis::system::Request::Peers(sender) => { + let _ = sender.send(network.peers_debug_info().into_iter().map(|(peer_id, p)| + rpc::apis::system::PeerInfo { + peer_id: peer_id.to_base58(), + roles: format!("{:?}", p.roles), + protocol_version: p.protocol_version, + best_hash: p.best_hash, + best_number: p.best_number, + } + ).collect()); + } + rpc::apis::system::Request::NetworkState(sender) => { + let _ = sender.send(network.network_state()); + } + }; + } + + // Interval report for the external API. while let Ok(Async::Ready(_)) = status_interval.poll() { let status = NetworkStatus { sync_state: network.sync_state(), @@ -547,14 +701,28 @@ fn build_network_future {} + Err(err) => warn!(target: "service", "Error in network: {:?}", err), + Ok(Async::Ready(())) => warn!(target: "service", "Network service finished"), + } + + // Now some diagnostic for performances. + let polling_dur = before_polling.elapsed(); + log!( + target: "service", + if polling_dur >= Duration::from_millis(50) { Level::Warn } else { Level::Trace }, + "Polling the network future took {:?}", + polling_dur + ); + + Ok(Async::NotReady) }) } @@ -586,22 +754,74 @@ impl Drop for Service where Components: components::Comp } } -fn maybe_start_server(address: Option, start: F) -> Result, io::Error> - where F: Fn(&SocketAddr) -> Result, -{ - Ok(match address { - Some(mut address) => Some(start(&address) - .or_else(|e| match e.kind() { - io::ErrorKind::AddrInUse | - io::ErrorKind::PermissionDenied => { - warn!("Unable to bind server to {}. Trying random port.", address); - address.set_port(0); - start(&address) - }, - _ => Err(e), - })?), - None => None, - }) +/// Starts RPC servers that run in their own thread, and returns an opaque object that keeps them alive. +#[cfg(not(target_os = "unknown"))] +fn start_rpc_servers rpc::RpcHandler>( + config: &FactoryFullConfiguration, + mut gen_handler: H +) -> Result, error::Error> { + fn maybe_start_server(address: Option, mut start: F) -> Result, io::Error> + where F: FnMut(&SocketAddr) -> Result, + { + Ok(match address { + Some(mut address) => Some(start(&address) + .or_else(|e| match e.kind() { + io::ErrorKind::AddrInUse | + io::ErrorKind::PermissionDenied => { + warn!("Unable to bind server to {}. Trying random port.", address); + address.set_port(0); + start(&address) + }, + _ => Err(e), + })?), + None => None, + }) + } + + Ok(Box::new(( + maybe_start_server( + config.rpc_http, + |address| rpc::start_http(address, config.rpc_cors.as_ref(), gen_handler()), + )?, + maybe_start_server( + config.rpc_ws, + |address| rpc::start_ws( + address, + config.rpc_ws_max_connections, + config.rpc_cors.as_ref(), + gen_handler(), + ), + )?.map(Mutex::new), + ))) +} + +/// Starts RPC servers that run in their own thread, and returns an opaque object that keeps them alive. +#[cfg(target_os = "unknown")] +fn start_rpc_servers rpc::RpcHandler>( + _: &FactoryFullConfiguration, + _: H +) -> 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). +pub struct RpcSession { + metadata: rpc::Metadata, +} + +impl RpcSession { + /// Creates an RPC session. + /// + /// The `sender` is stored inside the `RpcSession` and is used to communicate spontaneous JSON + /// messages. + /// + /// The `RpcSession` must be kept alive in order to receive messages on the sender. + pub fn new(sender: mpsc::Sender) -> RpcSession { + RpcSession { + metadata: rpc::Metadata::new(sender), + } + } } /// Transaction pool adapter. @@ -682,37 +902,71 @@ impl network::TransactionPool, ComponentBlock< } } -/// Builds a never-ending `Future` that answers the RPC requests coming on the receiver. -fn build_system_rpc_handler( - network: Arc>, - rx: mpsc::UnboundedReceiver>>, - should_have_peers: bool, -) -> impl Future { - rx.for_each(move |request| { - match request { - rpc::apis::system::Request::Health(sender) => { - let _ = sender.send(rpc::apis::system::Health { - peers: network.peers_debug_info().len(), - is_syncing: network.is_major_syncing(), - should_have_peers, - }); - }, - rpc::apis::system::Request::Peers(sender) => { - let _ = sender.send(network.peers_debug_info().into_iter().map(|(peer_id, p)| rpc::apis::system::PeerInfo { - peer_id: peer_id.to_base58(), - roles: format!("{:?}", p.roles), - protocol_version: p.protocol_version, - best_hash: p.best_hash, - best_number: p.best_number, - }).collect()); - } - rpc::apis::system::Request::NetworkState(sender) => { - let _ = sender.send(network.network_state()); - } +#[derive(Clone)] +/// A provider of current authority key. +pub struct AuthorityKeyProvider { + _marker: PhantomData<(Block, ConsensusPair, FinalityPair)>, + roles: Roles, + keystore: Option>, + password: crypto::Protected, +} + +impl + offchain::AuthorityKeyProvider + for AuthorityKeyProvider +where + Block: runtime_primitives::traits::Block, + ConsensusPair: Pair, + FinalityPair: Pair, +{ + type ConsensusPair = ConsensusPair; + type FinalityPair = FinalityPair; + + fn authority_key(&self, _at: &BlockId) -> Option { + if self.roles != Roles::AUTHORITY { + return None + } + + let keystore = match self.keystore { + Some(ref keystore) => keystore, + None => return None }; - Ok(()) - }) + let loaded_key = keystore + .contents() + .map(|keys| keys.get(0) + .map(|k| keystore.load(k, self.password.as_ref())) + ); + + if let Ok(Some(Ok(key))) = loaded_key { + Some(key) + } else { + None + } + } + + fn fg_authority_key(&self, _at: &BlockId) -> Option { + if self.roles != Roles::AUTHORITY { + return None + } + + let keystore = match self.keystore { + Some(ref keystore) => keystore, + None => return None + }; + + let loaded_key = keystore + .contents() + .map(|keys| keys.get(0) + .map(|k| keystore.load(k, self.password.as_ref())) + ); + + if let Ok(Some(Ok(key))) = loaded_key { + Some(key) + } else { + None + } + } } /// Constructs a service factory with the given name that implements the `ServiceFactory` trait. @@ -726,14 +980,13 @@ fn build_system_rpc_handler( /// ``` /// # use substrate_service::{ /// # construct_service_factory, Service, FullBackend, FullExecutor, LightBackend, LightExecutor, -/// # FullComponents, LightComponents, FactoryFullConfiguration, FullClient, TaskExecutor +/// # FullComponents, LightComponents, FactoryFullConfiguration, FullClient /// # }; /// # use transaction_pool::{self, txpool::{Pool as TransactionPool}}; -/// # use network::construct_simple_protocol; +/// # use network::{config::DummyFinalityProofRequestBuilder, construct_simple_protocol}; /// # use client::{self, well_known_cache_keys::Id as CacheKeyId, LongestChain}; -/// # use primitives::{Pair as PairT, ed25519}; /// # use consensus_common::import_queue::{BasicQueue, Verifier}; -/// # use consensus_common::{BlockOrigin, ImportBlock}; +/// # use consensus_common::{BlockOrigin, BlockImportParams}; /// # use node_runtime::{GenesisConfig, RuntimeApi}; /// # use std::sync::Arc; /// # use node_primitives::Block; @@ -751,7 +1004,7 @@ fn build_system_rpc_handler( /// # header: B::Header, /// # justification: Option, /// # body: Option>, -/// # ) -> Result<(ImportBlock, Option)>>), String> { +/// # ) -> Result<(BlockImportParams, Option)>>), String> { /// # unimplemented!(); /// # } /// # } @@ -764,6 +1017,8 @@ fn build_system_rpc_handler( /// struct Factory { /// // Declare the block type /// Block = Block, +/// ConsensusPair = primitives::ed25519::Pair, +/// FinalityPair = primitives::ed25519::Pair, /// RuntimeApi = RuntimeApi, /// // Declare the network protocol and give an initializer. /// NetworkProtocol = NodeProtocol { |config| Ok(NodeProtocol::new()) }, @@ -775,18 +1030,21 @@ fn build_system_rpc_handler( /// Genesis = GenesisConfig, /// Configuration = (), /// FullService = FullComponents -/// { |config, executor| >::new(config, executor) }, +/// { |config| >::new(config) }, /// // Setup as Consensus Authority (if the role and key are given) /// AuthoritySetup = { -/// |service: Self::FullService, executor: TaskExecutor, key: Option>| { +/// |service: Self::FullService| { /// Ok(service) /// }}, /// LightService = LightComponents -/// { |config, executor| >::new(config, executor) }, +/// { |config| >::new(config) }, /// FullImportQueue = BasicQueue -/// { |_, client, _| Ok(BasicQueue::new(Arc::new(MyVerifier), client, None, None, None)) }, +/// { |_, client, _| Ok(BasicQueue::new(Arc::new(MyVerifier), Box::new(client), None, None)) }, /// LightImportQueue = BasicQueue -/// { |_, client| Ok(BasicQueue::new(Arc::new(MyVerifier), client, None, None, None)) }, +/// { |_, client| { +/// let fprb = Box::new(DummyFinalityProofRequestBuilder::default()) as Box<_>; +/// Ok((BasicQueue::new(Arc::new(MyVerifier), Box::new(client), None, None), fprb)) +/// }}, /// SelectChain = LongestChain, Self::Block> /// { |config: &FactoryFullConfiguration, client: Arc>| { /// #[allow(deprecated)] @@ -804,6 +1062,8 @@ macro_rules! construct_service_factory { $(#[$attr:meta])* struct $name:ident { Block = $block:ty, + ConsensusPair = $consensus_pair:ty, + FinalityPair = $finality_pair:ty, RuntimeApi = $runtime_api:ty, NetworkProtocol = $protocol:ty { $( $protocol_init:tt )* }, RuntimeDispatch = $dispatch:ty, @@ -829,6 +1089,8 @@ macro_rules! construct_service_factory { #[allow(unused_variables)] impl $crate::ServiceFactory for $name { type Block = $block; + type ConsensusPair = $consensus_pair; + type FinalityPair = $finality_pair; type RuntimeApi = $runtime_api; type NetworkProtocol = $protocol; type RuntimeDispatch = $dispatch; @@ -882,7 +1144,7 @@ macro_rules! construct_service_factory { fn build_light_import_queue( config: &mut FactoryFullConfiguration, client: Arc<$crate::LightClient>, - ) -> Result { + ) -> Result<(Self::LightImportQueue, $crate::BoxFinalityProofRequestBuilder<$block>), $crate::Error> { ( $( $light_import_queue_init )* ) (config, client) } @@ -893,21 +1155,18 @@ macro_rules! construct_service_factory { } fn new_light( - config: $crate::FactoryFullConfiguration, - executor: $crate::TaskExecutor + config: $crate::FactoryFullConfiguration ) -> $crate::Result { - ( $( $light_service_init )* ) (config, executor) + ( $( $light_service_init )* ) (config) } fn new_full( - config: $crate::FactoryFullConfiguration, - executor: $crate::TaskExecutor, + config: $crate::FactoryFullConfiguration ) -> Result { - ( $( $full_service_init )* ) (config, executor.clone()).and_then(|service| { - let key = (&service).authority_key().map(Arc::new); - ($( $authority_setup )*)(service, executor, key) + ( $( $full_service_init )* ) (config).and_then(|service| { + ($( $authority_setup )*)(service) }) } } diff --git a/core/service/test/src/lib.rs b/core/service/test/src/lib.rs index d3d9677bb241028e528d78714c745860354699e3..353b326a91611791b97f938e92c52e066360b9b9 100644 --- a/core/service/test/src/lib.rs +++ b/core/service/test/src/lib.rs @@ -17,12 +17,12 @@ //! Service integration test utils. use std::iter; -use std::sync::Arc; +use std::sync::{Arc, Mutex, MutexGuard}; use std::net::Ipv4Addr; use std::time::Duration; use std::collections::HashMap; use log::info; -use futures::{Future, Stream}; +use futures::{Future, Stream, Poll}; use tempdir::TempDir; use tokio::{runtime::Runtime, prelude::FutureExt}; use tokio::timer::Interval; @@ -34,24 +34,54 @@ use service::{ Roles, FactoryExtrinsic, }; -use network::{multiaddr, Multiaddr, ManageNetwork}; -use network::config::{NetworkConfiguration, NodeKeyConfig, Secret, NonReservedPeerMode}; +use network::{multiaddr, Multiaddr}; +use network::config::{NetworkConfiguration, TransportConfig, NodeKeyConfig, Secret, NonReservedPeerMode}; use sr_primitives::generic::BlockId; -use consensus::{ImportBlock, BlockImport}; +use consensus::{BlockImportParams, BlockImport}; /// Maximum duration of single wait call. const MAX_WAIT_TIME: Duration = Duration::from_secs(60 * 3); struct TestNet { runtime: Runtime, - authority_nodes: Vec<(u32, Arc, Multiaddr)>, - full_nodes: Vec<(u32, Arc, Multiaddr)>, - light_nodes: Vec<(u32, Arc, Multiaddr)>, + authority_nodes: Vec<(usize, SyncService, Multiaddr)>, + full_nodes: Vec<(usize, SyncService, Multiaddr)>, + light_nodes: Vec<(usize, SyncService, Multiaddr)>, chain_spec: FactoryChainSpec, base_port: u16, nodes: usize, } +/// Wraps around an `Arc>` and implements `Future`. +pub struct SyncService(Arc>); + +impl SyncService { + pub fn get(&self) -> MutexGuard { + self.0.lock().unwrap() + } +} + +impl Clone for SyncService { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +impl From for SyncService { + fn from(service: T) -> Self { + SyncService(Arc::new(Mutex::new(service))) + } +} + +impl> Future for SyncService { + type Item = (); + type Error = (); + + fn poll(&mut self) -> Poll { + self.0.lock().unwrap().poll() + } +} + impl TestNet { pub fn run_until_all_full( &mut self, @@ -59,25 +89,31 @@ impl TestNet { light_predicate: LP, ) where - FP: Send + Sync + Fn(u32, &F::FullService) -> bool + 'static, - LP: Send + Sync + Fn(u32, &F::LightService) -> bool + 'static, + FP: Send + Fn(usize, &SyncService) -> bool + 'static, + LP: Send + Fn(usize, &SyncService) -> bool + 'static, { let full_nodes = self.full_nodes.clone(); let light_nodes = self.light_nodes.clone(); let interval = Interval::new_interval(Duration::from_millis(100)) .map_err(|_| ()) .for_each(move |_| { - let full_ready = full_nodes.iter().all(|&(ref id, ref service, _)| full_predicate(*id, service)); + let full_ready = full_nodes.iter().all(|&(ref id, ref service, _)| + full_predicate(*id, service) + ); + if !full_ready { return Ok(()); } - let light_ready = light_nodes.iter().all(|&(ref id, ref service, _)| light_predicate(*id, service)); + let light_ready = light_nodes.iter().all(|&(ref id, ref service, _)| + light_predicate(*id, service) + ); + if !light_ready { - return Ok(()); + Ok(()) + } else { + Err(()) } - - Err(()) }) .timeout(MAX_WAIT_TIME); @@ -90,7 +126,7 @@ impl TestNet { } fn node_config ( - index: u32, + index: usize, spec: &FactoryChainSpec, role: Roles, key_seed: Option, @@ -124,8 +160,10 @@ fn node_config ( non_reserved_mode: NonReservedPeerMode::Accept, client_version: "network/test/0.1".to_owned(), node_name: "unknown".to_owned(), - enable_mdns: false, - wasm_external_transport: None, + transport: TransportConfig::Normal { + enable_mdns: false, + wasm_external_transport: None, + }, }; Configuration { @@ -135,8 +173,8 @@ fn node_config ( roles: role, transaction_pool: Default::default(), network: network_config, - keystore_path: root.join("key").to_str().unwrap().into(), - database_path: root.join("db").to_str().unwrap().into(), + keystore_path: Some(root.join("key")), + database_path: root.join("db"), database_cache_size: None, state_cache_size: 16777216, state_cache_child_ratio: None, @@ -151,25 +189,37 @@ fn node_config ( rpc_ws_max_connections: None, rpc_cors: None, telemetry_endpoints: None, + telemetry_external_transport: None, default_heap_pages: None, offchain_worker: false, force_authoring: false, disable_grandpa: false, - password: "".to_string(), + grandpa_voter: false, + password: "".to_string().into(), } } -impl TestNet { - fn new(temp: &TempDir, spec: FactoryChainSpec, full: u32, light: u32, authorities: Vec, base_port: u16) -> TestNet { - let _ = ::env_logger::try_init(); - ::fdlimit::raise_fd_limit(); +impl TestNet where + F::FullService: Future, + F::LightService: Future +{ + fn new( + temp: &TempDir, + spec: FactoryChainSpec, + full: usize, + light: usize, + authorities: Vec, + base_port: u16 + ) -> TestNet { + let _ = env_logger::try_init(); + fdlimit::raise_fd_limit(); let runtime = Runtime::new().expect("Error creating tokio runtime"); let mut net = TestNet { runtime, authority_nodes: Default::default(), full_nodes: Default::default(), light_nodes: Default::default(), - chain_spec: spec.clone(), + chain_spec: spec, base_port, nodes: 0, }; @@ -177,48 +227,61 @@ impl TestNet { net } - fn insert_nodes(&mut self, temp: &TempDir, full: u32, light: u32, authorities: Vec) { + fn insert_nodes(&mut self, temp: &TempDir, full: usize, light: usize, authorities: Vec) { let mut nodes = self.nodes; let base_port = self.base_port; - let spec = self.chain_spec.clone(); + let spec = &self.chain_spec; let executor = self.runtime.executor(); self.authority_nodes.extend(authorities.iter().enumerate().map(|(index, key)| { - let node_config = node_config::(index as u32, &spec, Roles::AUTHORITY, Some(key.clone()), base_port, &temp); + let node_config = node_config::( + index, + &spec, + Roles::AUTHORITY, + Some(key.clone()), + base_port, + &temp, + ); let addr = node_config.network.listen_addresses.iter().next().unwrap().clone(); - let service = Arc::new(F::new_full(node_config, executor.clone()) - .expect("Error creating test node service")); - let addr = addr.with(multiaddr::Protocol::P2p(service.network().local_peer_id().into())); - ((index + nodes) as u32, service, addr) + let service = SyncService::from(F::new_full(node_config).expect("Error creating test node service")); + + executor.spawn(service.clone()); + let addr = addr.with(multiaddr::Protocol::P2p(service.get().network().local_peer_id().into())); + ((index + nodes), service, addr) })); nodes += authorities.len(); - self.full_nodes.extend((nodes..nodes + full as usize).map(|index| { - let node_config = node_config::(index as u32, &spec, Roles::FULL, None, base_port, &temp); + self.full_nodes.extend((nodes..nodes + full).map(|index| { + let node_config = node_config::(index, &spec, Roles::FULL, None, base_port, &temp); let addr = node_config.network.listen_addresses.iter().next().unwrap().clone(); - let service = Arc::new(F::new_full(node_config, executor.clone()) - .expect("Error creating test node service")); - let addr = addr.with(multiaddr::Protocol::P2p(service.network().local_peer_id().into())); - (index as u32, service, addr) + let service = SyncService::from(F::new_full(node_config).expect("Error creating test node service")); + + executor.spawn(service.clone()); + let addr = addr.with(multiaddr::Protocol::P2p(service.get().network().local_peer_id().into())); + (index, service, addr) })); - nodes += full as usize; + nodes += full; - self.light_nodes.extend((nodes..nodes + light as usize).map(|index| { - let node_config = node_config::(index as u32, &spec, Roles::LIGHT, None, base_port, &temp); + self.light_nodes.extend((nodes..nodes + light).map(|index| { + let node_config = node_config::(index, &spec, Roles::LIGHT, None, base_port, &temp); let addr = node_config.network.listen_addresses.iter().next().unwrap().clone(); - let service = Arc::new(F::new_light(node_config, executor.clone()) - .expect("Error creating test node service")); - let addr = addr.with(multiaddr::Protocol::P2p(service.network().local_peer_id().into())); - (index as u32, service, addr) + let service = SyncService::from(F::new_light(node_config).expect("Error creating test node service")); + + executor.spawn(service.clone()); + let addr = addr.with(multiaddr::Protocol::P2p(service.get().network().local_peer_id().into())); + (index, service, addr) })); - nodes += light as usize; + nodes += light; self.nodes = nodes; } } -pub fn connectivity(spec: FactoryChainSpec) { - const NUM_FULL_NODES: u32 = 5; - const NUM_LIGHT_NODES: u32 = 5; +pub fn connectivity(spec: FactoryChainSpec) where + F::FullService: Future, + F::LightService: Future, +{ + const NUM_FULL_NODES: usize = 5; + const NUM_LIGHT_NODES: usize = 5; { let temp = TempDir::new("substrate-connectivity-test").expect("Error creating test dir"); let runtime = { @@ -233,20 +296,20 @@ pub fn connectivity(spec: FactoryChainSpec) { info!("Checking star topology"); let first_address = network.full_nodes[0].2.clone(); for (_, service, _) in network.full_nodes.iter().skip(1) { - service.network().add_reserved_peer(first_address.to_string()).expect("Error adding reserved peer"); + service.get().network().add_reserved_peer(first_address.to_string()).expect("Error adding reserved peer"); } for (_, service, _) in network.light_nodes.iter() { - service.network().add_reserved_peer(first_address.to_string()).expect("Error adding reserved peer"); + service.get().network().add_reserved_peer(first_address.to_string()).expect("Error adding reserved peer"); } network.run_until_all_full( - |_index, service| service.network().peers_debug_info().len() == NUM_FULL_NODES as usize - 1 - + NUM_LIGHT_NODES as usize, - |_index, service| service.network().peers_debug_info().len() == NUM_FULL_NODES as usize, + |_index, service| service.get().network().num_connected() == NUM_FULL_NODES - 1 + + NUM_LIGHT_NODES, + |_index, service| service.get().network().num_connected() == NUM_FULL_NODES, ); network.runtime }; - runtime.shutdown_on_idle().wait().expect("Error shutting down runtime"); + runtime.shutdown_now().wait().expect("Error shutting down runtime"); temp.close().expect("Error removing temp dir"); } @@ -263,43 +326,40 @@ pub fn connectivity(spec: FactoryChainSpec) { ); info!("Checking linked topology"); let mut address = network.full_nodes[0].2.clone(); - let max_nodes = ::std::cmp::max(NUM_FULL_NODES, NUM_LIGHT_NODES); + let max_nodes = std::cmp::max(NUM_FULL_NODES, NUM_LIGHT_NODES); for i in 0..max_nodes { if i != 0 { - if let Some((_, service, node_id)) = network.full_nodes.get(i as usize) { - service.network().add_reserved_peer(address.to_string()).expect("Error adding reserved peer"); + if let Some((_, service, node_id)) = network.full_nodes.get(i) { + service.get().network().add_reserved_peer(address.to_string()).expect("Error adding reserved peer"); address = node_id.clone(); } } - if let Some((_, service, node_id)) = network.light_nodes.get(i as usize) { - service.network().add_reserved_peer(address.to_string()).expect("Error adding reserved peer"); + if let Some((_, service, node_id)) = network.light_nodes.get(i) { + service.get().network().add_reserved_peer(address.to_string()).expect("Error adding reserved peer"); address = node_id.clone(); } } network.run_until_all_full( - |_index, service| service.network().peers_debug_info().len() == NUM_FULL_NODES as usize - 1 - + NUM_LIGHT_NODES as usize, - |_index, service| service.network().peers_debug_info().len() == NUM_FULL_NODES as usize, + |_index, service| service.get().network().num_connected() == NUM_FULL_NODES - 1 + + NUM_LIGHT_NODES, + |_index, service| service.get().network().num_connected() == NUM_FULL_NODES, ); } temp.close().expect("Error removing temp dir"); } } -pub fn sync( - spec: FactoryChainSpec, - mut block_factory: B, - mut extrinsic_factory: E, -) -where +pub fn sync(spec: FactoryChainSpec, mut block_factory: B, mut extrinsic_factory: E) where F: ServiceFactory, - B: FnMut(&F::FullService) -> ImportBlock, - E: FnMut(&F::FullService) -> FactoryExtrinsic, + F::FullService: Future, + F::LightService: Future, + B: FnMut(&SyncService) -> BlockImportParams, + E: FnMut(&SyncService) -> FactoryExtrinsic, { - const NUM_FULL_NODES: u32 = 10; - const NUM_LIGHT_NODES: u32 = 10; - const NUM_BLOCKS: u32 = 512; + const NUM_FULL_NODES: usize = 10; + const NUM_LIGHT_NODES: usize = 10; + const NUM_BLOCKS: usize = 512; let temp = TempDir::new("substrate-sync-test").expect("Error creating test dir"); let mut network = TestNet::::new( &temp, @@ -312,45 +372,50 @@ where info!("Checking block sync"); let first_address = { let first_service = &network.full_nodes[0].1; + let mut client = first_service.get().client(); for i in 0 .. NUM_BLOCKS { if i % 128 == 0 { info!("Generating #{}", i); } let import_data = block_factory(&first_service); - first_service.client().import_block(import_data, HashMap::new()).expect("Error importing test block"); + client.import_block(import_data, HashMap::new()).expect("Error importing test block"); } network.full_nodes[0].2.clone() }; + info!("Running sync"); for (_, service, _) in network.full_nodes.iter().skip(1) { - service.network().add_reserved_peer(first_address.to_string()).expect("Error adding reserved peer"); + service.get().network().add_reserved_peer(first_address.to_string()).expect("Error adding reserved peer"); } for (_, service, _) in network.light_nodes.iter() { - service.network().add_reserved_peer(first_address.to_string()).expect("Error adding reserved peer"); + service.get().network().add_reserved_peer(first_address.to_string()).expect("Error adding reserved peer"); } network.run_until_all_full( |_index, service| - service.client().info().chain.best_number == NUM_BLOCKS.into(), + service.get().client().info().chain.best_number == (NUM_BLOCKS as u32).into(), |_index, service| - service.client().info().chain.best_number == NUM_BLOCKS.into(), + service.get().client().info().chain.best_number == (NUM_BLOCKS as u32).into(), ); + info!("Checking extrinsic propagation"); let first_service = network.full_nodes[0].1.clone(); - let best_block = BlockId::number(first_service.client().info().chain.best_number); - first_service.transaction_pool().submit_one(&best_block, extrinsic_factory(&first_service)).unwrap(); + let best_block = BlockId::number(first_service.get().client().info().chain.best_number); + let extrinsic = extrinsic_factory(&first_service); + first_service.get().transaction_pool().submit_one(&best_block, extrinsic).unwrap(); network.run_until_all_full( - |_index, service| service.transaction_pool().ready().count() == 1, + |_index, service| service.get().transaction_pool().ready().count() == 1, |_index, _service| true, ); } -pub fn consensus(spec: FactoryChainSpec, authorities: Vec) - where - F: ServiceFactory, +pub fn consensus(spec: FactoryChainSpec, authorities: Vec) where + F: ServiceFactory, + F::FullService: Future, + F::LightService: Future, { - const NUM_FULL_NODES: u32 = 10; - const NUM_LIGHT_NODES: u32 = 0; - const NUM_BLOCKS: u32 = 10; // 10 * 2 sec block production time = ~20 seconds + const NUM_FULL_NODES: usize = 10; + const NUM_LIGHT_NODES: usize = 0; + const NUM_BLOCKS: usize = 10; // 10 * 2 sec block production time = ~20 seconds let temp = TempDir::new("substrate-conensus-test").expect("Error creating test dir"); let mut network = TestNet::::new( &temp, @@ -360,35 +425,37 @@ pub fn consensus(spec: FactoryChainSpec, authorities: Vec) authorities, 30600, ); + info!("Checking consensus"); let first_address = network.authority_nodes[0].2.clone(); for (_, service, _) in network.full_nodes.iter() { - service.network().add_reserved_peer(first_address.to_string()).expect("Error adding reserved peer"); + service.get().network().add_reserved_peer(first_address.to_string()).expect("Error adding reserved peer"); } for (_, service, _) in network.light_nodes.iter() { - service.network().add_reserved_peer(first_address.to_string()).expect("Error adding reserved peer"); + service.get().network().add_reserved_peer(first_address.to_string()).expect("Error adding reserved peer"); } for (_, service, _) in network.authority_nodes.iter().skip(1) { - service.network().add_reserved_peer(first_address.to_string()).expect("Error adding reserved peer"); + service.get().network().add_reserved_peer(first_address.to_string()).expect("Error adding reserved peer"); } network.run_until_all_full( |_index, service| - service.client().info().chain.finalized_number >= (NUM_BLOCKS / 2).into(), + service.get().client().info().chain.finalized_number >= (NUM_BLOCKS as u32 / 2).into(), |_index, service| - service.client().info().chain.best_number >= (NUM_BLOCKS / 2).into(), + service.get().client().info().chain.best_number >= (NUM_BLOCKS as u32 / 2).into(), ); + info!("Adding more peers"); network.insert_nodes(&temp, NUM_FULL_NODES / 2, NUM_LIGHT_NODES / 2, vec![]); for (_, service, _) in network.full_nodes.iter() { - service.network().add_reserved_peer(first_address.to_string()).expect("Error adding reserved peer"); + service.get().network().add_reserved_peer(first_address.to_string()).expect("Error adding reserved peer"); } for (_, service, _) in network.light_nodes.iter() { - service.network().add_reserved_peer(first_address.to_string()).expect("Error adding reserved peer"); + service.get().network().add_reserved_peer(first_address.to_string()).expect("Error adding reserved peer"); } network.run_until_all_full( |_index, service| - service.client().info().chain.finalized_number >= NUM_BLOCKS.into(), + service.get().client().info().chain.finalized_number >= (NUM_BLOCKS as u32).into(), |_index, service| - service.client().info().chain.best_number >= NUM_BLOCKS.into(), + service.get().client().info().chain.best_number >= (NUM_BLOCKS as u32).into(), ); } diff --git a/core/sr-api-macros/Cargo.toml b/core/sr-api-macros/Cargo.toml index 9f2145d958eee31d72bc3817ff1f2c14991ae4bb..c9a91e74914cab57a7c721b42b94a6530cd337f2 100644 --- a/core/sr-api-macros/Cargo.toml +++ b/core/sr-api-macros/Cargo.toml @@ -23,7 +23,7 @@ sr-version = { path = "../sr-version" } substrate-primitives = { path = "../primitives" } criterion = "0.2" consensus_common = { package = "substrate-consensus-common", path = "../consensus/common" } -codec = { package = "parity-codec", version = "3.5.1" } +codec = { package = "parity-codec", version = "4.1.1" } trybuild = "1.0" [[bench]] diff --git a/core/sr-api-macros/tests/runtime_calls.rs b/core/sr-api-macros/tests/runtime_calls.rs index 6fa155437b83371ce29abe52b2f97c1ffd7631a8..fec6015835e2129d1d4485b3f6663929c042c21d 100644 --- a/core/sr-api-macros/tests/runtime_calls.rs +++ b/core/sr-api-macros/tests/runtime_calls.rs @@ -104,7 +104,7 @@ fn calling_with_both_strategy_and_fail_on_native_should_work() { #[test] -fn calling_with_native_else_wasm_and_faild_on_wasm_should_work() { +fn calling_with_native_else_wasm_and_fail_on_wasm_should_work() { let client = TestClientBuilder::new().set_execution_strategy(ExecutionStrategy::NativeElseWasm).build(); let runtime_api = client.runtime_api(); let block_id = BlockId::Number(client.info().chain.best_number); diff --git a/core/sr-api-macros/tests/trybuild.rs b/core/sr-api-macros/tests/trybuild.rs index e04c67737a486835e7818aa98bfaea46d6b975c6..302dd7c0878e3dfb6c9b364c2004ee6f8fed2e80 100644 --- a/core/sr-api-macros/tests/trybuild.rs +++ b/core/sr-api-macros/tests/trybuild.rs @@ -1,5 +1,10 @@ +use std::env; + #[test] fn ui() { + // As trybuild is using `cargo check`, we don't need the real WASM binaries. + env::set_var("BUILD_DUMMY_WASM_BINARY", "1"); + let t = trybuild::TestCases::new(); t.compile_fail("tests/ui/*.rs"); } 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 6d5d484efe0954ff752b865ec78788b63a2c5f6c..15434a52ba8b78ae6876b17e34f72ff9c7e85779 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,4 +10,14 @@ error[E0053]: method `test` has an incompatible type for trait = note: expected type `fn(u64)` found type `fn(std::string::String)` -For more information about this error, try `rustc --explain E0053`. +error[E0308]: mismatched types + --> $DIR/impl_incorrect_method_signature.rs:20:11 + | +20 | fn test(data: String) {} + | ^^^^ expected u64, found struct `std::string::String` + | + = 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/type_reference_in_impl_runtime_apis_call.stderr b/core/sr-api-macros/tests/ui/type_reference_in_impl_runtime_apis_call.stderr index 12ec399972efa763784b24e5243368cd10ae6641..9bfc04c8db046ecba4f42d2140143a7ade6b3886 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,4 +10,20 @@ error[E0053]: method `test` has an incompatible type for trait = note: expected type `fn(u64)` found type `fn(&u64)` -For more information about this error, try `rustc --explain E0053`. +error[E0308]: mismatched types + --> $DIR/type_reference_in_impl_runtime_apis_call.rs:18:1 + | +18 | / impl_runtime_apis! { +19 | | impl self::Api for Runtime { +20 | | fn test(data: &u64) { +21 | | unimplemented!() +22 | | } +23 | | } +24 | | } + | |_^ 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-io/Cargo.toml b/core/sr-io/Cargo.toml index 0c97ba8050d2d8f9990ee540f40dfca4ceef7da9..0172d3ac75783509e8e3b41038b974a04bbd4e88 100644 --- a/core/sr-io/Cargo.toml +++ b/core/sr-io/Cargo.toml @@ -11,8 +11,8 @@ rustc_version = "0.2" [dependencies] rstd = { package = "sr-std", path = "../sr-std", default-features = false } primitives = { package = "substrate-primitives", path = "../primitives", default-features = false } -codec = { package = "parity-codec", version = "3.5.1", default-features = false } -hash-db = { version = "0.12", default-features = false } +codec = { package = "parity-codec", version = "4.1.1", default-features = false } +hash-db = { version = "0.14.0", default-features = false } libsecp256k1 = { version = "0.2.1", optional = true } tiny-keccak = { version = "1.4.2", optional = true } environmental = { version = "1.0.1", optional = true } diff --git a/core/sr-io/src/lib.rs b/core/sr-io/src/lib.rs index cd9b43798b3d1d7f59ea13ed2e1464ef949fb0de..6ffb15ffdf2d3eacb0793062ee7dcea5cce25df7 100644 --- a/core/sr-io/src/lib.rs +++ b/core/sr-io/src/lib.rs @@ -33,7 +33,13 @@ use rstd::vec::Vec; pub use codec; pub use primitives::Blake2Hasher; -use primitives::offchain::{Timestamp, HttpRequestId, HttpRequestStatus, HttpError, CryptoKind, CryptoKeyId}; +use primitives::offchain::{ + Timestamp, + HttpRequestId, HttpRequestStatus, HttpError, + CryptoKind, CryptoKey, + StorageKind, + OpaqueNetworkState, +}; /// Error verifying ECDSA signature pub enum EcdsaVerifyError { @@ -234,38 +240,44 @@ export_api! { /// The transaction will end up in the pool. fn submit_transaction(data: &T) -> Result<(), ()>; + /// Returns information about the local node's network state. + fn network_state() -> Result; + + /// Returns the currently configured authority public key, if available. + fn pubkey(key: CryptoKey) -> Result, ()>; + /// Create new key(pair) for signing/encryption/decryption. /// /// Returns an error if given crypto kind is not supported. - fn new_crypto_key(crypto: CryptoKind) -> Result; + fn new_crypto_key(crypto: CryptoKind) -> Result; /// Encrypt a piece of data using given crypto key. /// /// If `key` is `None`, it will attempt to use current authority key. /// /// Returns an error if `key` is not available or does not exist. - fn encrypt(key: Option, data: &[u8]) -> Result, ()>; + fn encrypt(key: CryptoKey, data: &[u8]) -> Result, ()>; /// Decrypt a piece of data using given crypto key. /// /// If `key` is `None`, it will attempt to use current authority key. /// /// Returns an error if data cannot be decrypted or the `key` is not available or does not exist. - fn decrypt(key: Option, data: &[u8]) -> Result, ()>; + fn decrypt(key: CryptoKey, data: &[u8]) -> Result, ()>; /// Sign a piece of data using given crypto key. /// /// If `key` is `None`, it will attempt to use current authority key. /// /// Returns an error if `key` is not available or does not exist. - fn sign(key: Option, data: &[u8]) -> Result, ()>; + fn sign(key: CryptoKey, data: &[u8]) -> Result, ()>; /// Verifies that `signature` for `msg` matches given `key`. /// /// Returns an `Ok` with `true` in case it does, `false` in case it doesn't. /// Returns an error in case the key is not available or does not exist or the parameters /// lengths are incorrect. - fn verify(key: Option, msg: &[u8], signature: &[u8]) -> Result; + fn verify(key: CryptoKey, msg: &[u8], signature: &[u8]) -> Result; /// Returns current UNIX timestamp (in millis) fn timestamp() -> Timestamp; @@ -283,25 +295,27 @@ export_api! { /// /// Note this storage is not part of the consensus, it's only accessible by /// offchain worker tasks running on the same machine. It IS persisted between runs. - fn local_storage_set(key: &[u8], value: &[u8]); + fn local_storage_set(kind: StorageKind, key: &[u8], value: &[u8]); /// Sets a value in the local storage if it matches current value. /// /// Since multiple offchain workers may be running concurrently, to prevent /// data races use CAS to coordinate between them. /// + /// Returns `true` if the value has been set, `false` otherwise. + /// /// Note this storage is not part of the consensus, it's only accessible by /// offchain worker tasks running on the same machine. It IS persisted between runs. - fn local_storage_compare_and_set(key: &[u8], old_value: &[u8], new_value: &[u8]); + fn local_storage_compare_and_set(kind: StorageKind, key: &[u8], old_value: &[u8], new_value: &[u8]) -> bool; /// Gets a value from the local storage. /// /// If the value does not exist in the storage `None` will be returned. /// Note this storage is not part of the consensus, it's only accessible by /// offchain worker tasks running on the same machine. It IS persisted between runs. - fn local_storage_get(key: &[u8]) -> Option>; + fn local_storage_get(kind: StorageKind, key: &[u8]) -> Option>; - /// Initiaties a http request given HTTP verb and the URL. + /// Initiates a http request given HTTP verb and the URL. /// /// Meta is a future-reserved field containing additional, parity-codec encoded parameters. /// Returns the id of newly started request. @@ -382,7 +396,10 @@ mod imp { } #[cfg(feature = "std")] -pub use self::imp::{StorageOverlay, ChildrenStorageOverlay, with_storage, with_externalities}; +pub use self::imp::{ + StorageOverlay, ChildrenStorageOverlay, with_storage, with_storage_and_children, + with_externalities +}; #[cfg(not(feature = "std"))] pub use self::imp::ext::*; diff --git a/core/sr-io/src/offchain/http.rs b/core/sr-io/src/offchain/http.rs index 0708f837179ed3072001e5308050dd318c9fe0ad..6685dd023f469e607b59cfcf7509b629c58bde94 100644 --- a/core/sr-io/src/offchain/http.rs +++ b/core/sr-io/src/offchain/http.rs @@ -486,9 +486,8 @@ mod tests { #[test] fn should_send_a_basic_request_and_get_response() { - let offchain = testing::TestOffchainExt::default(); + let (offchain, state) = testing::TestOffchainExt::new(); let mut t = TestExternalities::default(); - let state = offchain.0.clone(); t.set_offchain_externalities(offchain); with_externalities(&mut t, || { @@ -528,9 +527,8 @@ mod tests { #[test] fn should_send_a_post_request() { - let offchain = testing::TestOffchainExt::default(); + let (offchain, state) = testing::TestOffchainExt::new(); let mut t = TestExternalities::default(); - let state = offchain.0.clone(); t.set_offchain_externalities(offchain); with_externalities(&mut t, || { diff --git a/core/sr-io/with_std.rs b/core/sr-io/with_std.rs index 34bfc22b9db1544571739135ca955517ae2350d1..18cb2fd2dfda64c666e4e899eb8070538f2737b9 100644 --- a/core/sr-io/with_std.rs +++ b/core/sr-io/with_std.rs @@ -269,31 +269,56 @@ impl OffchainApi for () { }, "submit_transaction can be called only in the offchain worker context") } - fn new_crypto_key(crypto: offchain::CryptoKind) -> Result { + fn network_state() -> Result { + with_offchain(|ext| { + ext.network_state() + }, "network_state can be called only in the offchain worker context") + } + + fn pubkey(key: offchain::CryptoKey) -> Result, ()> { + with_offchain(|ext| { + ext.pubkey(key) + }, "authority_pubkey can be called only in the offchain worker context") + } + + fn new_crypto_key(crypto: offchain::CryptoKind) -> Result { with_offchain(|ext| { ext.new_crypto_key(crypto) }, "new_crypto_key can be called only in the offchain worker context") } - fn encrypt(key: Option, data: &[u8]) -> Result, ()> { + fn encrypt( + key: offchain::CryptoKey, + data: &[u8], + ) -> Result, ()> { with_offchain(|ext| { ext.encrypt(key, data) }, "encrypt can be called only in the offchain worker context") } - fn decrypt(key: Option, data: &[u8]) -> Result, ()> { + fn decrypt( + key: offchain::CryptoKey, + data: &[u8], + ) -> Result, ()> { with_offchain(|ext| { ext.decrypt(key, data) }, "decrypt can be called only in the offchain worker context") } - fn sign(key: Option, data: &[u8]) -> Result, ()> { + fn sign( + key: offchain::CryptoKey, + data: &[u8], + ) -> Result, ()> { with_offchain(|ext| { ext.sign(key, data) }, "sign can be called only in the offchain worker context") } - fn verify(key: Option, msg: &[u8], signature: &[u8]) -> Result { + fn verify( + key: offchain::CryptoKey, + msg: &[u8], + signature: &[u8], + ) -> Result { with_offchain(|ext| { ext.verify(key, msg, signature) }, "verify can be called only in the offchain worker context") @@ -305,7 +330,7 @@ impl OffchainApi for () { }, "timestamp can be called only in the offchain worker context") } - fn sleep_until(deadline: Timestamp) { + fn sleep_until(deadline: offchain::Timestamp) { with_offchain(|ext| { ext.sleep_until(deadline) }, "sleep_until can be called only in the offchain worker context") @@ -317,21 +342,26 @@ impl OffchainApi for () { }, "random_seed can be called only in the offchain worker context") } - fn local_storage_set(key: &[u8], value: &[u8]) { + fn local_storage_set(kind: offchain::StorageKind, key: &[u8], value: &[u8]) { with_offchain(|ext| { - ext.local_storage_set(key, value) + ext.local_storage_set(kind, key, value) }, "local_storage_set can be called only in the offchain worker context") } - fn local_storage_compare_and_set(key: &[u8], old_value: &[u8], new_value: &[u8]) { + fn local_storage_compare_and_set( + kind: offchain::StorageKind, + key: &[u8], + old_value: &[u8], + new_value: &[u8], + ) -> bool { with_offchain(|ext| { - ext.local_storage_compare_and_set(key, old_value, new_value) + ext.local_storage_compare_and_set(kind, key, old_value, new_value) }, "local_storage_compare_and_set can be called only in the offchain worker context") } - fn local_storage_get(key: &[u8]) -> Option> { + fn local_storage_get(kind: offchain::StorageKind, key: &[u8]) -> Option> { with_offchain(|ext| { - ext.local_storage_get(key) + ext.local_storage_get(kind, key) }, "local_storage_get can be called only in the offchain worker context") } @@ -413,9 +443,32 @@ pub type ChildrenStorageOverlay = HashMap, StorageOverlay>; pub fn with_storage R>(storage: &mut StorageOverlay, f: F) -> R { let mut alt_storage = Default::default(); rstd::mem::swap(&mut alt_storage, storage); - let mut ext: BasicExternalities = alt_storage.into(); + let mut ext = BasicExternalities::new(alt_storage); let r = ext::using(&mut ext, f); - *storage = ext.into(); + *storage = ext.into_storages().0; + r +} + +/// Execute the given closure with global functions available whose functionality routes into +/// externalities that draw from and populate `storage` and `children_storage`. +/// Forwards the value that the closure returns. +pub fn with_storage_and_children R>( + storage: &mut StorageOverlay, + children_storage: &mut ChildrenStorageOverlay, + f: F +) -> R { + let mut alt_storage = Default::default(); + let mut alt_children_storage = Default::default(); + rstd::mem::swap(&mut alt_storage, storage); + rstd::mem::swap(&mut alt_children_storage, children_storage); + + let mut ext = BasicExternalities::new_with_children(alt_storage, alt_children_storage); + let r = ext::using(&mut ext, f); + + let storage_tuple = ext.into_storages(); + *storage = storage_tuple.0; + *children_storage = storage_tuple.1; + r } diff --git a/core/sr-io/without_std.rs b/core/sr-io/without_std.rs index b1dc10d1b88eb4d0cf92c51197b7f44c402eb402..001b697934c99fec2ac607375100d93318bdcf0b 100644 --- a/core/sr-io/without_std.rs +++ b/core/sr-io/without_std.rs @@ -19,7 +19,7 @@ pub use rstd; pub use rstd::{mem, slice}; use core::{intrinsics, panic::PanicInfo}; -use rstd::{vec::Vec, cell::Cell, convert::TryInto}; +use rstd::{vec::Vec, cell::Cell, convert::TryInto, convert::TryFrom}; use primitives::{offchain, Blake2Hasher}; #[cfg(not(feature = "no_panic_handler"))] @@ -385,28 +385,60 @@ pub mod ext { /// - nonzero otherwise. fn ext_submit_transaction(data: *const u8, len: u32) -> u32; + /// Returns information about the local node's network state. + /// + /// # Returns + /// + /// The encoded `Result`. + /// `written_out` contains the length of the message. + /// + /// The ownership of the returned buffer is transferred to the runtime + /// code and the runtime is responsible for freeing it. This is always + /// a properly allocated pointer (which cannot be NULL), hence the + /// runtime code can always rely on it. + fn ext_network_state(written_out: *mut u32) -> *mut u8; + + /// Returns the locally configured authority public key, if available. + /// The `crypto` argument is `offchain::CryptoKind` converted to `u32`. + /// + /// # Returns + /// + /// The encoded `Result, ()>`. + /// `written_out` contains the length of the message. + /// + /// The ownership of the returned buffer is transferred to the runtime + /// code and the runtime is responsible for freeing it. This is always + /// a properly allocated pointer (which cannot be NULL), hence the + /// runtime code can always rely on it. + fn ext_pubkey(key: u64, written_out: *mut u32) -> *mut u8; + /// Create new key(pair) for signing/encryption/decryption. /// /// # Returns /// /// - A crypto key id (if the value is less than u16::max_value) /// - `u32::max_value` in case the crypto is not supported - fn ext_new_crypto_key(crypto: u32) -> u32; + fn ext_new_crypto_key(crypto: u32) -> u64; /// Encrypt a piece of data using given crypto key. /// - /// If `key` is `0`, it will attempt to use current authority key. + /// If `key` is `0`, it will attempt to use current authority key of given `kind`. /// /// # Returns /// /// - `0` in case the key is invalid, `msg_len` is set to `u32::max_value` /// - Otherwise, pointer to the encrypted message in memory, /// `msg_len` contains the length of the message. - fn ext_encrypt(key: u32, data: *const u8, data_len: u32, msg_len: *mut u32) -> *mut u8; + fn ext_encrypt( + key: u64, + data: *const u8, + data_len: u32, + msg_len: *mut u32 + ) -> *mut u8; /// Decrypt a piece of data using given crypto key. /// - /// If `key `is `0`, it will attempt to use current authority key. + /// If `key` is `0`, it will attempt to use current authority key of given `kind`. /// /// # Returns /// @@ -414,11 +446,16 @@ pub mod ext { /// `msg_len` is set to `u32::max_value` /// - Otherwise, pointer to the decrypted message in memory, /// `msg_len` contains the length of the message. - fn ext_decrypt(key: u32, data: *const u8, data_len: u32, msg_len: *mut u32) -> *mut u8; + fn ext_decrypt( + key: u64, + data: *const u8, + data_len: u32, + msg_len: *mut u32 + ) -> *mut u8; /// Sign a piece of data using given crypto key. /// - /// If `key` is `0`, it will attempt to use current authority key. + /// If `key` is `0`, it will attempt to use current authority key of given `kind`. /// /// # Returns /// @@ -426,18 +463,23 @@ pub mod ext { /// `sig_data_len` is set to `u32::max_value` /// - Otherwise, pointer to the signature in memory, /// `sig_data_len` contains the length of the signature. - fn ext_sign(key: u32, data: *const u8, data_len: u32, sig_data_len: *mut u32) -> *mut u8; + fn ext_sign( + key: u64, + data: *const u8, + data_len: u32, + sig_data_len: *mut u32 + ) -> *mut u8; /// Verifies that `signature` for `msg` matches given `key`. /// - /// If `key` is `0`, it will attempt to use current authority key. + /// If `key` is `0`, it will attempt to use current authority key of given `kind`. /// /// # Returns /// - `0` in case the signature is correct /// - `1` in case it doesn't match the key /// - `u32::max_value` if the key is invalid. fn ext_verify( - key: u32, + key: u64, msg: *const u8, msg_len: u32, signature: *const u8, @@ -459,17 +501,22 @@ pub mod ext { fn ext_random_seed(data: *mut u8); /// Write a value to local storage. - fn ext_local_storage_set(key: *const u8, key_len: u32, value: *const u8, value_len: u32); + fn ext_local_storage_set(kind: u32, key: *const u8, key_len: u32, value: *const u8, value_len: u32); /// Write a value to local storage in atomic fashion. + /// + /// # Returns + /// - `0` in case the value has been set + /// - `1` if the `old_value` didn't match fn ext_local_storage_compare_and_set( + kind: u32, key: *const u8, key_len: u32, old_value: *const u8, old_value_len: u32, new_value: *const u8, new_value_len: u32 - ); + ) -> u32; /// Read a value from local storage. /// @@ -478,9 +525,9 @@ pub mod ext { /// /// - 0 if the value has not been found, the `value_len` is set to `u32::max_value`. /// - Otherwise, pointer to the value in memory. `value_len` contains the length of the value. - fn ext_local_storage_get(key: *const u8, key_len: u32, value_len: *mut u32) -> *mut u8; + fn ext_local_storage_get(kind: u32, key: *const u8, key_len: u32, value_len: *mut u32) -> *mut u8; - /// Initiaties a http request. + /// Initiates a http request. /// /// `meta` is parity-codec encoded additional parameters to the request (like redirection policy, /// timeouts, certificates policy, etc). The format is not yet specified and the field is currently @@ -864,54 +911,104 @@ impl OffchainApi for () { } } - fn new_crypto_key(crypto: offchain::CryptoKind) -> Result { - let crypto = crypto as u8 as u32; - let ret = unsafe { - ext_new_crypto_key.get()(crypto) + fn network_state() -> Result { + let mut len = 0_u32; + let raw_result = unsafe { + let ptr = ext_network_state.get()(&mut len); + + from_raw_parts(ptr, len) }; - if ret > u16::max_value() as u32 { - Err(()) - } else { - Ok(offchain::CryptoKeyId(ret as u16)) + match raw_result { + Some(raw_result) => codec::Decode::decode(&mut &*raw_result).unwrap_or(Err(())), + None => Err(()) + } + } + + fn pubkey(key: CryptoKey) -> Result, ()> { + let mut len = 0u32; + let raw_result = unsafe { + let ptr = ext_pubkey.get()( + key.into(), + &mut len, + ); + + from_raw_parts(ptr, len) + }; + + match raw_result { + Some(raw_result) => codec::Decode::decode(&mut &*raw_result).unwrap_or(Err(())), + None => Err(()) } } - fn encrypt(key: Option, data: &[u8]) -> Result, ()> { - let key = key.map(|x| x.0 as u32).unwrap_or(0); + fn new_crypto_key(crypto: offchain::CryptoKind) -> Result { + let crypto = crypto.into(); + let ret = unsafe { + ext_new_crypto_key.get()(crypto) + }; + offchain::CryptoKey::try_from(ret) + } + + fn encrypt( + key: offchain::CryptoKey, + data: &[u8], + ) -> Result, ()> { let mut len = 0_u32; unsafe { - let ptr = ext_encrypt.get()(key, data.as_ptr(), data.len() as u32, &mut len); + let ptr = ext_encrypt.get()( + key.into(), + data.as_ptr(), + data.len() as u32, + &mut len + ); from_raw_parts(ptr, len).ok_or(()) } } - fn decrypt(key: Option, data: &[u8]) -> Result, ()> { - let key = key.map(|x| x.0 as u32).unwrap_or(0); + fn decrypt( + key: offchain::CryptoKey, + data: &[u8], + ) -> Result, ()> { let mut len = 0_u32; unsafe { - let ptr = ext_decrypt.get()(key, data.as_ptr(), data.len() as u32, &mut len); + let ptr = ext_decrypt.get()( + key.into(), + data.as_ptr(), + data.len() as u32, + &mut len + ); from_raw_parts(ptr, len).ok_or(()) } } - fn sign(key: Option, data: &[u8]) -> Result, ()> { - let key = key.map(|x| x.0 as u32).unwrap_or(0); + fn sign( + key: offchain::CryptoKey, + data: &[u8], + ) -> Result, ()> { let mut len = 0_u32; unsafe { - let ptr = ext_sign.get()(key, data.as_ptr(), data.len() as u32, &mut len); + let ptr = ext_sign.get()( + key.into(), + data.as_ptr(), + data.len() as u32, + &mut len + ); from_raw_parts(ptr, len).ok_or(()) } } - fn verify(key: Option, msg: &[u8], signature: &[u8]) -> Result { - let key = key.map(|x| x.0 as u32).unwrap_or(0); + fn verify( + key: offchain::CryptoKey, + msg: &[u8], + signature: &[u8], + ) -> Result { let val = unsafe { ext_verify.get()( - key, + key.into(), msg.as_ptr(), msg.len() as u32, signature.as_ptr(), @@ -932,7 +1029,7 @@ impl OffchainApi for () { }) } - fn sleep_until(deadline: Timestamp) { + fn sleep_until(deadline: offchain::Timestamp) { unsafe { ext_sleep_until.get()(deadline.unix_millis()) } @@ -946,9 +1043,10 @@ impl OffchainApi for () { result } - fn local_storage_set(key: &[u8], value: &[u8]) { + fn local_storage_set(kind: offchain::StorageKind, key: &[u8], value: &[u8]) { unsafe { ext_local_storage_set.get()( + kind.into(), key.as_ptr(), key.len() as u32, value.as_ptr(), @@ -957,23 +1055,25 @@ impl OffchainApi for () { } } - fn local_storage_compare_and_set(key: &[u8], old_value: &[u8], new_value: &[u8]) { + fn local_storage_compare_and_set(kind: offchain::StorageKind, key: &[u8], old_value: &[u8], new_value: &[u8]) -> bool { unsafe { ext_local_storage_compare_and_set.get()( + kind.into(), key.as_ptr(), key.len() as u32, old_value.as_ptr(), old_value.len() as u32, new_value.as_ptr(), new_value.len() as u32, - ) + ) == 0 } } - fn local_storage_get(key: &[u8]) -> Option> { + fn local_storage_get(kind: offchain::StorageKind, key: &[u8]) -> Option> { let mut len = 0u32; unsafe { let ptr = ext_local_storage_get.get()( + kind.into(), key.as_ptr(), key.len() as u32, &mut len, @@ -1011,7 +1111,7 @@ impl OffchainApi for () { let result = unsafe { ext_http_request_add_header.get()( - request_id.0 as u32, + request_id.into(), name.as_ptr(), name.len() as u32, value.as_ptr(), @@ -1033,7 +1133,7 @@ impl OffchainApi for () { ) -> Result<(), offchain::HttpError> { let res = unsafe { ext_http_request_write_body.get()( - request_id.0 as u32, + request_id.into(), chunk.as_ptr(), chunk.len() as u32, deadline.map_or(0, |x| x.unix_millis()), @@ -1076,7 +1176,7 @@ impl OffchainApi for () { let mut len = 0u32; let raw_result = unsafe { let ptr = ext_http_response_headers.get()( - request_id.0 as u32, + request_id.into(), &mut len, ); @@ -1093,7 +1193,7 @@ impl OffchainApi for () { ) -> Result { let res = unsafe { ext_http_response_read_body.get()( - request_id.0 as u32, + request_id.into(), buffer.as_mut_ptr(), buffer.len() as u32, deadline.map_or(0, |x| x.unix_millis()), diff --git a/core/sr-primitives/Cargo.toml b/core/sr-primitives/Cargo.toml index b549b4c71e0a69d9ed570d61dec6bb47b770e89b..a5399eb5f73ba9d3229c39bcd3875d8db756b251 100644 --- a/core/sr-primitives/Cargo.toml +++ b/core/sr-primitives/Cargo.toml @@ -8,15 +8,16 @@ edition = "2018" num-traits = { version = "0.2", default-features = false } integer-sqrt = { version = "0.1.2" } serde = { version = "1.0", optional = true, features = ["derive"] } -codec = { package = "parity-codec", version = "3.5.1", default-features = false, features = ["derive"] } +codec = { package = "parity-codec", version = "4.1.1", default-features = false, features = ["derive"] } substrate-primitives = { path = "../primitives", 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", optional = true } +paste = { version = "0.1"} [dev-dependencies] serde_json = "1.0" -primitive-types = "0.2" +primitive-types = "0.4" [features] default = ["std"] diff --git a/core/sr-primitives/src/generic/checked_extrinsic.rs b/core/sr-primitives/src/generic/checked_extrinsic.rs index ee43b3af2e951ef78e63ff396d22edd3c0a5f2ea..04ccd1162c6c6893d0a5effca29050bb8c91a700 100644 --- a/core/sr-primitives/src/generic/checked_extrinsic.rs +++ b/core/sr-primitives/src/generic/checked_extrinsic.rs @@ -17,50 +17,83 @@ //! Generic implementation of an extrinsic that has passed the verification //! stage. -use crate::traits::{self, Member, SimpleArithmetic, MaybeDisplay}; -use crate::weights::{Weighable, Weight}; +use rstd::result::Result; +use crate::traits::{ + self, Member, MaybeDisplay, SignedExtension, DispatchError, Dispatchable, DispatchResult, + ValidateUnsigned +}; +use crate::weights::{GetDispatchInfo, DispatchInfo}; +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))] -pub struct CheckedExtrinsic { +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). - pub signed: Option<(AccountId, Index)>, + pub signed: Option<(AccountId, Extra)>, + /// The function that should be called. pub function: Call, } -impl traits::Applyable for CheckedExtrinsic +impl traits::Applyable +for + CheckedExtrinsic where AccountId: Member + MaybeDisplay, - Index: Member + MaybeDisplay + SimpleArithmetic, - Call: Member, + Call: Member + Dispatchable, + Extra: SignedExtension, + Origin: From>, { - type Index = Index; type AccountId = AccountId; - type Call = Call; - fn index(&self) -> Option<&Self::Index> { - self.signed.as_ref().map(|x| &x.1) - } + type Call = Call; fn sender(&self) -> Option<&Self::AccountId> { self.signed.as_ref().map(|x| &x.0) } - fn deconstruct(self) -> (Self::Call, Option) { - (self.function, self.signed.map(|x| x.0)) + fn validate>(&self, + info: DispatchInfo, + len: usize, + ) -> TransactionValidity { + if let Some((ref id, ref extra)) = self.signed { + Extra::validate(extra, id, info, len).into() + } else { + match Extra::validate_unsigned(info, len) { + Ok(extra) => match U::validate_unsigned(&self.function) { + TransactionValidity::Valid(v) => + TransactionValidity::Valid(v.combine_with(extra)), + x => x, + }, + x => x.into(), + } + } + } + + fn dispatch(self, + info: DispatchInfo, + len: usize, + ) -> Result { + let maybe_who = if let Some((id, extra)) = self.signed { + Extra::pre_dispatch(extra, &id, info, len)?; + Some(id) + } else { + Extra::pre_dispatch_unsigned(info, len)?; + None + }; + Ok(self.function.dispatch(Origin::from(maybe_who))) } } -impl Weighable for CheckedExtrinsic +impl GetDispatchInfo for CheckedExtrinsic where - Call: Weighable, + Call: GetDispatchInfo, { - fn weight(&self, len: usize) -> Weight { - self.function.weight(len) + fn get_dispatch_info(&self) -> DispatchInfo { + self.function.get_dispatch_info() } } diff --git a/core/sr-primitives/src/generic/mod.rs b/core/sr-primitives/src/generic/mod.rs index 8888e69a1894978852720e05c8d5d9aa5fe8e0ae..a1d0cb052ecb14c6eaba8d16818268a6f6f84322 100644 --- a/core/sr-primitives/src/generic/mod.rs +++ b/core/sr-primitives/src/generic/mod.rs @@ -19,8 +19,6 @@ // end::description[] mod unchecked_extrinsic; -mod unchecked_mortal_extrinsic; -mod unchecked_mortal_compact_extrinsic; mod era; mod checked_extrinsic; mod header; @@ -30,8 +28,6 @@ mod digest; mod tests; pub use self::unchecked_extrinsic::UncheckedExtrinsic; -pub use self::unchecked_mortal_extrinsic::UncheckedMortalExtrinsic; -pub use self::unchecked_mortal_compact_extrinsic::UncheckedMortalCompactExtrinsic; pub use self::era::{Era, Phase}; pub use self::checked_extrinsic::CheckedExtrinsic; pub use self::header::Header; diff --git a/core/sr-primitives/src/generic/unchecked_extrinsic.rs b/core/sr-primitives/src/generic/unchecked_extrinsic.rs index d6e0d60e2c218c255e5da9af987ce08e46b0d1e3..092af6e6f3cdd0d9b05ae314e88c62addff8c408 100644 --- a/core/sr-primitives/src/generic/unchecked_extrinsic.rs +++ b/core/sr-primitives/src/generic/unchecked_extrinsic.rs @@ -20,48 +20,40 @@ use std::fmt; use rstd::prelude::*; -use crate::codec::{Decode, Encode, Codec, Input, HasCompact}; -use crate::traits::{self, Member, SimpleArithmetic, MaybeDisplay, Lookup, Extrinsic}; +use runtime_io::blake2_256; +use crate::codec::{Decode, Encode, Input}; +use crate::traits::{self, Member, MaybeDisplay, SignedExtension, Checkable, Extrinsic}; use super::CheckedExtrinsic; -#[derive(PartialEq, Eq, Clone, Encode, Decode)] -pub struct SignatureContent -where - Address: Codec, - Index: HasCompact + Codec, - Signature: Codec, -{ - signed: Address, - signature: Signature, - index: Index, -} +const TRANSACTION_VERSION: u8 = 2; /// A extrinsic right from the external world. This is unchecked and so /// can contain a signature. #[derive(PartialEq, Eq, Clone)] -pub struct UncheckedExtrinsic +pub struct UncheckedExtrinsic where - Address: Codec, - Index: HasCompact + Codec, - Signature: Codec, + Extra: SignedExtension { - /// The signature, address and number of extrinsics have come before from - /// the same signer, if this is a signed extrinsic. - pub signature: Option>, + /// The signature, address, number of extrinsics have come before from + /// the same signer and an era describing the longevity of this transaction, + /// if this is a signed extrinsic. + pub signature: Option<(Address, Signature, Extra)>, /// The function that should be called. pub function: Call, } -impl UncheckedExtrinsic -where - Address: Codec, - Index: HasCompact + Codec, - Signature: Codec, +impl + UncheckedExtrinsic { /// New instance of a signed extrinsic aka "transaction". - pub fn new_signed(index: Index, function: Call, signed: Address, signature: Signature) -> Self { + pub fn new_signed( + function: Call, + signed: Address, + signature: Signature, + extra: Extra + ) -> Self { UncheckedExtrinsic { - signature: Some(SignatureContent{signed, signature, index}), + signature: Some((signed, signature, extra)), function, } } @@ -75,29 +67,52 @@ where } } -impl traits::Checkable - for UncheckedExtrinsic +impl Extrinsic + for UncheckedExtrinsic +{ + type Call = Call; + + fn is_signed(&self) -> Option { + Some(self.signature.is_some()) + } + + fn new_unsigned(function: Call) -> Option { + Some(UncheckedExtrinsic::new_unsigned(function)) + } +} + +impl + Checkable +for + UncheckedExtrinsic where - Address: Member + MaybeDisplay + Codec, - Index: Member + MaybeDisplay + SimpleArithmetic + Codec, + Address: Member + MaybeDisplay, Call: Encode + Member, - Signature: Member + traits::Verify + Codec, + Signature: Member + traits::Verify, + Extra: SignedExtension, AccountId: Member + MaybeDisplay, - Context: Lookup, + Lookup: traits::Lookup { - type Checked = CheckedExtrinsic; + type Checked = CheckedExtrinsic; - fn check(self, context: &Context) -> Result { + fn check(self, lookup: &Lookup) -> Result { Ok(match self.signature { - Some(SignatureContent{signed, signature, index}) => { - let payload = (index, self.function); - let signed = context.lookup(signed)?; - if !crate::verify_encoded_lazy(&signature, &payload, &signed) { + Some((signed, signature, extra)) => { + let additional_signed = extra.additional_signed()?; + let raw_payload = (self.function, extra, additional_signed); + let signed = lookup.lookup(signed)?; + if !raw_payload.using_encoded(|payload| { + if payload.len() > 256 { + signature.verify(&blake2_256(payload)[..], &signed) + } else { + signature.verify(payload, &signed) + } + }) { return Err(crate::BAD_SIGNATURE) } CheckedExtrinsic { - signed: Some((signed, payload.0)), - function: payload.1, + signed: Some((signed, raw_payload.1)), + function: raw_payload.0, } } None => CheckedExtrinsic { @@ -108,19 +123,13 @@ where } } -impl< - Address: Codec, - Index: HasCompact + Codec, - Signature: Codec, - Call, -> Extrinsic for UncheckedExtrinsic { - fn is_signed(&self) -> Option { - Some(self.signature.is_some()) - } -} - -impl Decode - for UncheckedExtrinsic +impl Decode + for UncheckedExtrinsic +where + Address: Decode, + Signature: Decode, + Call: Decode, + Extra: SignedExtension, { fn decode(input: &mut I) -> Option { // This is a little more complicated than usual since the binary format must be compatible @@ -129,70 +138,191 @@ impl // to use this). let _length_do_not_remove_me_see_above: Vec<()> = Decode::decode(input)?; + let version = input.read_byte()?; + + let is_signed = version & 0b1000_0000 != 0; + let version = version & 0b0111_1111; + if version != TRANSACTION_VERSION { + return None + } + Some(UncheckedExtrinsic { - signature: Decode::decode(input)?, + signature: if is_signed { Some(Decode::decode(input)?) } else { None }, function: Decode::decode(input)?, }) } } -impl Encode - for UncheckedExtrinsic +impl Encode + for UncheckedExtrinsic +where + Address: Encode, + Signature: Encode, + Call: Encode, + Extra: SignedExtension, { fn encode(&self) -> Vec { super::encode_with_vec_prefix::(|v| { - self.signature.encode_to(v); + // 1 byte version id. + match self.signature.as_ref() { + Some(s) => { + v.push(TRANSACTION_VERSION | 0b1000_0000); + s.encode_to(v); + } + None => { + v.push(TRANSACTION_VERSION & 0b0111_1111); + } + } self.function.encode_to(v); }) } } #[cfg(feature = "std")] -impl serde::Serialize - for UncheckedExtrinsic +impl serde::Serialize + for UncheckedExtrinsic { fn serialize(&self, seq: S) -> Result where S: ::serde::Serializer { - self.using_encoded(|bytes| ::substrate_primitives::bytes::serialize(bytes, seq)) + self.using_encoded(|bytes| seq.serialize_bytes(bytes)) } } #[cfg(feature = "std")] -impl fmt::Debug - for UncheckedExtrinsic +impl fmt::Debug + for UncheckedExtrinsic where - Address: fmt::Debug + Codec, - Index: fmt::Debug + HasCompact + Codec, - Signature: Codec, + Address: fmt::Debug, Call: fmt::Debug, + Extra: SignedExtension, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "UncheckedExtrinsic({:?}, {:?})", self.signature.as_ref().map(|x| (&x.signed, &x.index)), self.function) + write!(f, "UncheckedExtrinsic({:?}, {:?})", self.signature.as_ref().map(|x| (&x.0, &x.2)), self.function) } } #[cfg(test)] -mod test { - use crate::codec::{Decode, Encode}; - use super::UncheckedExtrinsic; +mod tests { + use super::*; + use runtime_io::blake2_256; + use crate::codec::{Encode, Decode}; + use crate::traits::{SignedExtension, BlockNumberToHash, Lookup, CurrentHeight}; + use serde::{Serialize, Deserialize}; + + struct TestContext; + impl Lookup for TestContext { + type Source = u64; + type Target = u64; + fn lookup(&self, s: u64) -> Result { Ok(s) } + } + impl CurrentHeight for TestContext { + type BlockNumber = u64; + fn current_height(&self) -> u64 { 42 } + } + impl BlockNumberToHash for TestContext { + type BlockNumber = u64; + type Hash = u64; + fn block_number_to_hash(&self, n: u64) -> Option { Some(n) } + } + + #[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 TestAccountId = u64; + type TestCall = Vec; + + const TEST_ACCOUNT: TestAccountId = 0; + + // NOTE: this is demonstration. One can simply use `()` for testing. + #[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, Ord, PartialOrd)] + struct TestExtra; + impl SignedExtension for TestExtra { + type AccountId = u64; + type AdditionalSigned = (); + fn additional_signed(&self) -> rstd::result::Result<(), &'static str> { Ok(()) } + } + + type Ex = UncheckedExtrinsic; + type CEx = CheckedExtrinsic; #[test] - fn encoding_matches_vec() { - type Extrinsic = UncheckedExtrinsic; - let ex = Extrinsic::new_unsigned(42); - let encoded = ex.encode(); - let decoded = Extrinsic::decode(&mut encoded.as_slice()).unwrap(); - assert_eq!(decoded, ex); - let as_vec: Vec = Decode::decode(&mut encoded.as_slice()).unwrap(); - assert_eq!(as_vec.encode(), encoded); + fn unsigned_codec_should_work() { + let ux = Ex::new_unsigned(vec![0u8; 0]); + let encoded = ux.encode(); + assert_eq!(Ex::decode(&mut &encoded[..]), Some(ux)); + } + + #[test] + fn signed_codec_should_work() { + let ux = Ex::new_signed( + vec![0u8; 0], + TEST_ACCOUNT, + TestSig(TEST_ACCOUNT, (vec![0u8; 0], TestExtra).encode()), + TestExtra + ); + let encoded = ux.encode(); + assert_eq!(Ex::decode(&mut &encoded[..]), Some(ux)); } + #[test] + fn large_signed_codec_should_work() { + let ux = Ex::new_signed( + vec![0u8; 0], + TEST_ACCOUNT, + TestSig(TEST_ACCOUNT, (vec![0u8; 257], TestExtra) + .using_encoded(blake2_256)[..].to_owned()), + TestExtra + ); + let encoded = ux.encode(); + assert_eq!(Ex::decode(&mut &encoded[..]), Some(ux)); + } + + #[test] + fn unsigned_check_should_work() { + let ux = Ex::new_unsigned(vec![0u8; 0]); + assert!(!ux.is_signed().unwrap_or(false)); + assert!(>::check(ux, &TestContext).is_ok()); + } #[test] - #[cfg(feature = "std")] - fn serialization_of_unchecked_extrinsics() { - type Extrinsic = UncheckedExtrinsic; - let ex = Extrinsic::new_unsigned(42); + fn badly_signed_check_should_fail() { + let ux = Ex::new_signed( + vec![0u8; 0], + TEST_ACCOUNT, + TestSig(TEST_ACCOUNT, vec![0u8; 0]), + TestExtra + ); + assert!(ux.is_signed().unwrap_or(false)); + assert_eq!(>::check(ux, &TestContext), Err(crate::BAD_SIGNATURE)); + } - assert_eq!(serde_json::to_string(&ex).unwrap(), "\"0x14002a000000\""); + #[test] + fn signed_check_should_work() { + let ux = Ex::new_signed( + vec![0u8; 0], + TEST_ACCOUNT, + TestSig(TEST_ACCOUNT, (vec![0u8; 0], TestExtra).encode()), + TestExtra + ); + assert!(ux.is_signed().unwrap_or(false)); + assert_eq!( + >::check(ux, &TestContext), + Ok(CEx { signed: Some((TEST_ACCOUNT, TestExtra)), function: vec![0u8; 0] }) + ); + } + + #[test] + fn encoding_matches_vec() { + let ex = Ex::new_unsigned(vec![0u8; 0]); + let encoded = ex.encode(); + let decoded = Ex::decode(&mut encoded.as_slice()).unwrap(); + assert_eq!(decoded, ex); + let as_vec: Vec = Decode::decode(&mut encoded.as_slice()).unwrap(); + assert_eq!(as_vec.encode(), encoded); } } diff --git a/core/sr-primitives/src/generic/unchecked_mortal_compact_extrinsic.rs b/core/sr-primitives/src/generic/unchecked_mortal_compact_extrinsic.rs deleted file mode 100644 index 36e17fc277cdeed0ef742cb1da20e3203263739e..0000000000000000000000000000000000000000 --- a/core/sr-primitives/src/generic/unchecked_mortal_compact_extrinsic.rs +++ /dev/null @@ -1,306 +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 . - -//! Generic implementation of an unchecked (pre-verification) extrinsic. - -#[cfg(feature = "std")] -use std::fmt; - -use rstd::prelude::*; -use runtime_io::blake2_256; -use crate::codec::{Decode, Encode, Input, Compact}; -use crate::traits::{self, Member, SimpleArithmetic, MaybeDisplay, CurrentHeight, BlockNumberToHash, - Lookup, Checkable, Extrinsic, SaturatedConversion}; -use super::{CheckedExtrinsic, Era}; - -const TRANSACTION_VERSION: u8 = 1; - -/// A extrinsic right from the external world. This is unchecked and so -/// can contain a signature. -#[derive(PartialEq, Eq, Clone)] -pub struct UncheckedMortalCompactExtrinsic { - /// The signature, address, number of extrinsics have come before from - /// the same signer and an era describing the longevity of this transaction, - /// if this is a signed extrinsic. - pub signature: Option<(Address, Signature, Compact, Era)>, - /// The function that should be called. - pub function: Call, -} - -impl UncheckedMortalCompactExtrinsic { - /// New instance of a signed extrinsic aka "transaction". - pub fn new_signed(index: Index, function: Call, signed: Address, signature: Signature, era: Era) -> Self { - UncheckedMortalCompactExtrinsic { - signature: Some((signed, signature, index.into(), era)), - function, - } - } - - /// New instance of an unsigned extrinsic aka "inherent". - pub fn new_unsigned(function: Call) -> Self { - UncheckedMortalCompactExtrinsic { - signature: None, - function, - } - } -} - -impl Extrinsic for UncheckedMortalCompactExtrinsic { - fn is_signed(&self) -> Option { - Some(self.signature.is_some()) - } -} - -impl Checkable - for UncheckedMortalCompactExtrinsic -where - Address: Member + MaybeDisplay, - Index: Member + MaybeDisplay + SimpleArithmetic, - Compact: Encode, - Call: Encode + Member, - Signature: Member + traits::Verify, - AccountId: Member + MaybeDisplay, - BlockNumber: SimpleArithmetic, - Hash: Encode, - Context: Lookup - + CurrentHeight - + BlockNumberToHash, -{ - type Checked = CheckedExtrinsic; - - fn check(self, context: &Context) -> Result { - Ok(match self.signature { - Some((signed, signature, index, era)) => { - let current_u64 = context.current_height().saturated_into::(); - let h = context.block_number_to_hash(era.birth(current_u64).saturated_into()) - .ok_or("transaction birth block ancient")?; - let signed = context.lookup(signed)?; - let raw_payload = (index, self.function, era, h); - if !raw_payload.using_encoded(|payload| { - if payload.len() > 256 { - signature.verify(&blake2_256(payload)[..], &signed) - } else { - signature.verify(payload, &signed) - } - }) { - return Err(crate::BAD_SIGNATURE) - } - CheckedExtrinsic { - signed: Some((signed, (raw_payload.0).0)), - function: raw_payload.1, - } - } - None => CheckedExtrinsic { - signed: None, - function: self.function, - }, - }) - } -} - -impl Decode - for UncheckedMortalCompactExtrinsic -where - Address: Decode, - Signature: Decode, - Compact: Decode, - Call: Decode, -{ - fn decode(input: &mut I) -> Option { - // This is a little more complicated than usual since the binary format must be compatible - // with substrate's generic `Vec` type. Basically this just means accepting that there - // will be a prefix of vector length (we don't need - // to use this). - let _length_do_not_remove_me_see_above: Vec<()> = Decode::decode(input)?; - - let version = input.read_byte()?; - - let is_signed = version & 0b1000_0000 != 0; - let version = version & 0b0111_1111; - if version != TRANSACTION_VERSION { - return None - } - - Some(UncheckedMortalCompactExtrinsic { - signature: if is_signed { Some(Decode::decode(input)?) } else { None }, - function: Decode::decode(input)?, - }) - } -} - -impl Encode - for UncheckedMortalCompactExtrinsic -where - Address: Encode, - Signature: Encode, - Compact: Encode, - Call: Encode, -{ - fn encode(&self) -> Vec { - super::encode_with_vec_prefix::(|v| { - // 1 byte version id. - match self.signature.as_ref() { - Some(s) => { - v.push(TRANSACTION_VERSION | 0b1000_0000); - s.encode_to(v); - } - None => { - v.push(TRANSACTION_VERSION & 0b0111_1111); - } - } - self.function.encode_to(v); - }) - } -} - -#[cfg(feature = "std")] -impl serde::Serialize - for UncheckedMortalCompactExtrinsic - where Compact: Encode -{ - fn serialize(&self, seq: S) -> Result where S: ::serde::Serializer { - self.using_encoded(|bytes| seq.serialize_bytes(bytes)) - } -} - -#[cfg(feature = "std")] -impl fmt::Debug for UncheckedMortalCompactExtrinsic where - Address: fmt::Debug, - Index: fmt::Debug, - Call: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "UncheckedMortalCompactExtrinsic({:?}, {:?})", self.signature.as_ref().map(|x| (&x.0, &x.2)), self.function) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use runtime_io::blake2_256; - use crate::codec::{Encode, Decode}; - use serde::{Serialize, Deserialize}; - - struct TestContext; - impl Lookup for TestContext { - type Source = u64; - type Target = u64; - fn lookup(&self, s: u64) -> Result { Ok(s) } - } - impl CurrentHeight for TestContext { - type BlockNumber = u64; - fn current_height(&self) -> u64 { 42 } - } - impl BlockNumberToHash for TestContext { - type BlockNumber = u64; - type Hash = u64; - fn block_number_to_hash(&self, n: u64) -> Option { Some(n) } - } - - #[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[..] - } - } - - const DUMMY_ACCOUNTID: u64 = 0; - - type Ex = UncheckedMortalCompactExtrinsic, TestSig>; - type CEx = CheckedExtrinsic>; - - #[test] - fn unsigned_codec_should_work() { - let ux = Ex::new_unsigned(vec![0u8;0]); - let encoded = ux.encode(); - assert_eq!(Ex::decode(&mut &encoded[..]), Some(ux)); - } - - #[test] - fn signed_codec_should_work() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8;0], Era::immortal(), 0u64).encode()), Era::immortal()); - let encoded = ux.encode(); - assert_eq!(Ex::decode(&mut &encoded[..]), Some(ux)); - } - - #[test] - fn large_signed_codec_should_work() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8; 257], Era::immortal(), 0u64).using_encoded(blake2_256)[..].to_owned()), Era::immortal()); - let encoded = ux.encode(); - assert_eq!(Ex::decode(&mut &encoded[..]), Some(ux)); - } - - #[test] - fn unsigned_check_should_work() { - let ux = Ex::new_unsigned(vec![0u8;0]); - assert!(!ux.is_signed().unwrap_or(false)); - assert!(>::check(ux, &TestContext).is_ok()); - } - - #[test] - fn badly_signed_check_should_fail() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, vec![0u8]), Era::immortal()); - assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Err(crate::BAD_SIGNATURE)); - } - - #[test] - fn immortal_signed_check_should_work() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (Compact::from(DUMMY_ACCOUNTID), vec![0u8;0], Era::immortal(), 0u64).encode()), Era::immortal()); - assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Ok(CEx { signed: Some((DUMMY_ACCOUNTID, 0)), function: vec![0u8;0] })); - } - - #[test] - fn mortal_signed_check_should_work() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (Compact::from(DUMMY_ACCOUNTID), vec![0u8;0], Era::mortal(32, 42), 42u64).encode()), Era::mortal(32, 42)); - assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Ok(CEx { signed: Some((DUMMY_ACCOUNTID, 0)), function: vec![0u8;0] })); - } - - #[test] - fn later_mortal_signed_check_should_work() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (Compact::from(DUMMY_ACCOUNTID), vec![0u8;0], Era::mortal(32, 11), 11u64).encode()), Era::mortal(32, 11)); - assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Ok(CEx { signed: Some((DUMMY_ACCOUNTID, 0)), function: vec![0u8;0] })); - } - - #[test] - fn too_late_mortal_signed_check_should_fail() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8;0], Era::mortal(32, 10), 10u64).encode()), Era::mortal(32, 10)); - assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Err(crate::BAD_SIGNATURE)); - } - - #[test] - fn too_early_mortal_signed_check_should_fail() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8;0], Era::mortal(32, 43), 43u64).encode()), Era::mortal(32, 43)); - assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Err(crate::BAD_SIGNATURE)); - } - - #[test] - fn encoding_matches_vec() { - let ex = Ex::new_unsigned(vec![0u8;0]); - let encoded = ex.encode(); - let decoded = Ex::decode(&mut encoded.as_slice()).unwrap(); - assert_eq!(decoded, ex); - let as_vec: Vec = Decode::decode(&mut encoded.as_slice()).unwrap(); - assert_eq!(as_vec.encode(), encoded); - } -} diff --git a/core/sr-primitives/src/generic/unchecked_mortal_extrinsic.rs b/core/sr-primitives/src/generic/unchecked_mortal_extrinsic.rs deleted file mode 100644 index 7f92b20edd0c3143e3b35d1dc47327d9f575ec62..0000000000000000000000000000000000000000 --- a/core/sr-primitives/src/generic/unchecked_mortal_extrinsic.rs +++ /dev/null @@ -1,307 +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 . - -//! Generic implementation of an unchecked (pre-verification) extrinsic. - -#[cfg(feature = "std")] -use std::fmt; - -use rstd::prelude::*; -use runtime_io::blake2_256; -use crate::codec::{Decode, Encode, Input}; -use crate::traits::{ - self, Member, SimpleArithmetic, MaybeDisplay, CurrentHeight, BlockNumberToHash, - Lookup, Checkable, Extrinsic, SaturatedConversion -}; -use super::{CheckedExtrinsic, Era}; - -const TRANSACTION_VERSION: u8 = 1; - -/// A extrinsic right from the external world. This is unchecked and so -/// can contain a signature. -#[derive(PartialEq, Eq, Clone)] -pub struct UncheckedMortalExtrinsic { - /// The signature, address, number of extrinsics have come before from - /// the same signer and an era describing the longevity of this transaction, - /// if this is a signed extrinsic. - pub signature: Option<(Address, Signature, Index, Era)>, - /// The function that should be called. - pub function: Call, -} - -impl UncheckedMortalExtrinsic { - /// New instance of a signed extrinsic aka "transaction". - pub fn new_signed(index: Index, function: Call, signed: Address, signature: Signature, era: Era) -> Self { - UncheckedMortalExtrinsic { - signature: Some((signed, signature, index, era)), - function, - } - } - - /// New instance of an unsigned extrinsic aka "inherent". - pub fn new_unsigned(function: Call) -> Self { - UncheckedMortalExtrinsic { - signature: None, - function, - } - } -} - -impl Extrinsic for UncheckedMortalExtrinsic { - fn is_signed(&self) -> Option { - Some(self.signature.is_some()) - } -} - -impl Checkable - for UncheckedMortalExtrinsic -where - Address: Member + MaybeDisplay, - Index: Encode + Member + MaybeDisplay + SimpleArithmetic, - Call: Encode + Member, - Signature: Member + traits::Verify, - AccountId: Member + MaybeDisplay, - BlockNumber: SimpleArithmetic, - Hash: Encode, - Context: Lookup - + CurrentHeight - + BlockNumberToHash, -{ - type Checked = CheckedExtrinsic; - - fn check(self, context: &Context) -> Result { - Ok(match self.signature { - Some((signed, signature, index, era)) => { - let current_u64 = context.current_height().saturated_into::(); - let h = context.block_number_to_hash(era.birth(current_u64).saturated_into()) - .ok_or("transaction birth block ancient")?; - let signed = context.lookup(signed)?; - let raw_payload = (index, self.function, era, h); - - if !raw_payload.using_encoded(|payload| { - if payload.len() > 256 { - signature.verify(&blake2_256(payload)[..], &signed) - } else { - signature.verify(payload, &signed) - } - }) { - return Err(crate::BAD_SIGNATURE) - } - CheckedExtrinsic { - signed: Some((signed, raw_payload.0)), - function: raw_payload.1, - } - } - None => CheckedExtrinsic { - signed: None, - function: self.function, - }, - }) - } -} - -impl Decode - for UncheckedMortalExtrinsic -where - Address: Decode, - Signature: Decode, - Index: Decode, - Call: Decode, -{ - fn decode(input: &mut I) -> Option { - // This is a little more complicated than usual since the binary format must be compatible - // with substrate's generic `Vec` type. Basically this just means accepting that there - // will be a prefix of vector length (we don't need - // to use this). - let _length_do_not_remove_me_see_above: Vec<()> = Decode::decode(input)?; - - let version = input.read_byte()?; - - let is_signed = version & 0b1000_0000 != 0; - let version = version & 0b0111_1111; - if version != TRANSACTION_VERSION { - return None - } - - Some(UncheckedMortalExtrinsic { - signature: if is_signed { Some(Decode::decode(input)?) } else { None }, - function: Decode::decode(input)?, - }) - } -} - -impl Encode - for UncheckedMortalExtrinsic -where - Address: Encode, - Signature: Encode, - Index: Encode, - Call: Encode, -{ - fn encode(&self) -> Vec { - super::encode_with_vec_prefix::(|v| { - // 1 byte version id. - match self.signature.as_ref() { - Some(s) => { - v.push(TRANSACTION_VERSION | 0b1000_0000); - s.encode_to(v); - } - None => { - v.push(TRANSACTION_VERSION & 0b0111_1111); - } - } - self.function.encode_to(v); - }) - } -} - -#[cfg(feature = "std")] -impl serde::Serialize - for UncheckedMortalExtrinsic -{ - fn serialize(&self, seq: S) -> Result where S: ::serde::Serializer { - self.using_encoded(|bytes| seq.serialize_bytes(bytes)) - } -} - -#[cfg(feature = "std")] -impl fmt::Debug for UncheckedMortalExtrinsic where - Address: fmt::Debug, - Index: fmt::Debug, - Call: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "UncheckedMortalExtrinsic({:?}, {:?})", self.signature.as_ref().map(|x| (&x.0, &x.2)), self.function) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use runtime_io::blake2_256; - use crate::codec::{Encode, Decode}; - use serde::{Serialize, Deserialize}; - - struct TestContext; - impl Lookup for TestContext { - type Source = u64; - type Target = u64; - fn lookup(&self, s: u64) -> Result { Ok(s) } - } - impl CurrentHeight for TestContext { - type BlockNumber = u64; - fn current_height(&self) -> u64 { 42 } - } - impl BlockNumberToHash for TestContext { - type BlockNumber = u64; - type Hash = u64; - fn block_number_to_hash(&self, n: u64) -> Option { Some(n) } - } - - #[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[..] - } - } - - const DUMMY_ACCOUNTID: u64 = 0; - - type Ex = UncheckedMortalExtrinsic, TestSig>; - type CEx = CheckedExtrinsic>; - - #[test] - fn unsigned_codec_should_work() { - let ux = Ex::new_unsigned(vec![0u8;0]); - let encoded = ux.encode(); - assert_eq!(Ex::decode(&mut &encoded[..]), Some(ux)); - } - - #[test] - fn signed_codec_should_work() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8;0], Era::immortal(), 0u64).encode()), Era::immortal()); - let encoded = ux.encode(); - assert_eq!(Ex::decode(&mut &encoded[..]), Some(ux)); - } - - #[test] - fn large_signed_codec_should_work() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8; 257], Era::immortal(), 0u64).using_encoded(blake2_256)[..].to_owned()), Era::immortal()); - let encoded = ux.encode(); - assert_eq!(Ex::decode(&mut &encoded[..]), Some(ux)); - } - - #[test] - fn unsigned_check_should_work() { - let ux = Ex::new_unsigned(vec![0u8;0]); - assert!(!ux.is_signed().unwrap_or(false)); - assert!(>::check(ux, &TestContext).is_ok()); - } - - #[test] - fn badly_signed_check_should_fail() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, vec![0u8]), Era::immortal()); - assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Err(crate::BAD_SIGNATURE)); - } - - #[test] - fn immortal_signed_check_should_work() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8;0], Era::immortal(), 0u64).encode()), Era::immortal()); - assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Ok(CEx { signed: Some((DUMMY_ACCOUNTID, 0)), function: vec![0u8;0] })); - } - - #[test] - fn mortal_signed_check_should_work() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8;0], Era::mortal(32, 42), 42u64).encode()), Era::mortal(32, 42)); - assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Ok(CEx { signed: Some((DUMMY_ACCOUNTID, 0)), function: vec![0u8;0] })); - } - - #[test] - fn later_mortal_signed_check_should_work() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8;0], Era::mortal(32, 11), 11u64).encode()), Era::mortal(32, 11)); - assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Ok(CEx { signed: Some((DUMMY_ACCOUNTID, 0)), function: vec![0u8;0] })); - } - - #[test] - fn too_late_mortal_signed_check_should_fail() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8;0], Era::mortal(32, 10), 10u64).encode()), Era::mortal(32, 10)); - assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Err(crate::BAD_SIGNATURE)); - } - - #[test] - fn too_early_mortal_signed_check_should_fail() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8;0], Era::mortal(32, 43), 43u64).encode()), Era::mortal(32, 43)); - assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Err(crate::BAD_SIGNATURE)); - } - - #[test] - fn encoding_matches_vec() { - let ex = Ex::new_unsigned(vec![0u8;0]); - let encoded = ex.encode(); - let decoded = Ex::decode(&mut encoded.as_slice()).unwrap(); - assert_eq!(decoded, ex); - let as_vec: Vec = Decode::decode(&mut encoded.as_slice()).unwrap(); - assert_eq!(as_vec.encode(), encoded); - } -} diff --git a/core/sr-primitives/src/lib.rs b/core/sr-primitives/src/lib.rs index 007010bc53da16b9d00e650c9bc44e66779b99da..0b6321034cda4addb31cb7da538b2b639fde2416 100644 --- a/core/sr-primitives/src/lib.rs +++ b/core/sr-primitives/src/lib.rs @@ -28,10 +28,13 @@ pub use serde; #[doc(hidden)] pub use rstd; +#[doc(hidden)] +pub use paste; + #[cfg(feature = "std")] pub use runtime_io::{StorageOverlay, ChildrenStorageOverlay}; -use rstd::{prelude::*, ops}; +use rstd::{prelude::*, ops, convert::TryInto}; use substrate_primitives::{crypto, ed25519, sr25519, hash::{H256, H512}}; use codec::{Encode, Decode}; @@ -40,7 +43,7 @@ pub mod testing; pub mod weights; pub mod traits; -use traits::{SaturatedConversion, UniqueSaturatedInto}; +use traits::{SaturatedConversion, UniqueSaturatedInto, Saturating, Bounded, CheckedSub, CheckedAdd}; pub mod generic; pub mod transaction_validity; @@ -48,6 +51,9 @@ pub mod transaction_validity; /// Re-export these since they're only "kind of" generic. pub use generic::{DigestItem, Digest}; +/// Re-export this since it's part of the API of this crate. +pub use substrate_primitives::crypto::{key_types, KeyTypeId}; + /// A message indicating an invalid signature in extrinsic. pub const BAD_SIGNATURE: &str = "bad signature in extrinsic"; @@ -66,6 +72,14 @@ pub type Justification = Vec; use traits::{Verify, Lazy}; +/// A module identifier. These are per module and should be stored in a registry somewhere. +#[derive(Clone, Copy, Eq, PartialEq, Encode, Decode)] +pub struct ModuleId(pub [u8; 8]); + +impl traits::TypeId for ModuleId { + const TYPE_ID: [u8; 4] = *b"modl"; +} + /// A String that is a `&'static str` on `no_std` and a `Cow<'static, str>` on `std`. #[cfg(feature = "std")] pub type RuntimeString = ::std::borrow::Cow<'static, str>; @@ -101,7 +115,22 @@ pub trait BuildStorage: Sized { Ok((storage, child_storage)) } /// Assimilate the storage for this module into pre-existing overlays. - fn assimilate_storage(self, storage: &mut StorageOverlay, child_storage: &mut ChildrenStorageOverlay) -> Result<(), String>; + fn assimilate_storage( + self, + storage: &mut StorageOverlay, + child_storage: &mut ChildrenStorageOverlay + ) -> Result<(), String>; +} + +/// Something that can build the genesis storage of a module. +#[cfg(feature = "std")] +pub trait BuildModuleGenesisStorage: Sized { + /// Create the module genesis storage into the given `storage` and `child_storage`. + fn build_module_genesis_storage( + self, + storage: &mut StorageOverlay, + child_storage: &mut ChildrenStorageOverlay + ) -> Result<(), String>; } #[cfg(feature = "std")] @@ -139,7 +168,7 @@ impl BuildStorage for (StorageOverlay, ChildrenStorageOverlay) { pub type ConsensusEngineId = [u8; 4]; /// Permill is parts-per-million (i.e. after multiplying by this, divide by 1000000). -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug, Ord, PartialOrd))] #[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq)] pub struct Permill(u32); @@ -244,7 +273,7 @@ impl From> for Permill { /// Perbill is parts-per-billion. It stores a value between 0 and 1 in fixed point and /// provides a means to multiply some other value by that. #[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] -#[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq)] +#[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)] pub struct Perbill(u32); impl Perbill { @@ -348,6 +377,128 @@ impl From> for Perbill { } } + +/// A fixed point number by the scale of 1 billion. +/// +/// cannot hold a value larger than +-`9223372036854775807 / 1_000_000_000` (~9 billion). +#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct Fixed64(i64); + +/// The maximum value 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 + } + + /// 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 * DIV as i128 / (d as i128).max(1)).try_into().unwrap_or(Bounded::max_value())) + } + + /// Performs a saturated multiply and accumulate. + /// + /// Returns `n + (self * n)`. + pub fn saturated_multiply_accumulate(&self, int: u32) -> u32 { + let parts = self.0; + + let positive = parts > 0; + // natural parts might overflow. + let natural_parts = self.clone().saturated_into::(); + // fractional parts can always fit into u32. + let perbill_parts = (parts.abs() % DIV) as u32; + + let n = int.saturating_mul(natural_parts); + let p = Perbill::from_parts(perbill_parts) * int; + // 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) + } + } + + /// Raw constructor. Equal to `parts / 1_000_000_000`. + pub fn from_parts(parts: i64) -> Self { + Self(parts) + } +} + +impl UniqueSaturatedInto for Fixed64 { + /// Note that the maximum value of Fixed64 might be more than what can fit in u32. This is hence, + /// expected to be lossy. + fn unique_saturated_into(self) -> u32 { + (self.0.abs() / DIV).try_into().unwrap_or(Bounded::max_value()) + } +} + +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 + } + } +} + /// PerU128 is parts-per-u128-max-value. It stores a value between 0 and 1 in fixed point. #[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] #[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq)] @@ -585,26 +736,32 @@ pub fn verify_encoded_lazy(sig: &V, item: &T, signe /// Helper macro for `impl_outer_config` #[macro_export] macro_rules! __impl_outer_config_types { + // Generic + Instance ( - $concrete:ident $config:ident $snake:ident < $ignore:ident, $instance:path > $( $rest:tt )* + $concrete:ident $config:ident $snake:ident { $instance:ident } < $ignore:ident >; + $( $rest:tt )* ) => { #[cfg(any(feature = "std", test))] - pub type $config = $snake::GenesisConfig<$concrete, $instance>; - $crate::__impl_outer_config_types! {$concrete $($rest)*} + pub type $config = $snake::GenesisConfig<$concrete, $snake::$instance>; + $crate::__impl_outer_config_types! { $concrete $( $rest )* } }; + // Generic ( - $concrete:ident $config:ident $snake:ident < $ignore:ident > $( $rest:tt )* + $concrete:ident $config:ident $snake:ident < $ignore:ident >; + $( $rest:tt )* ) => { #[cfg(any(feature = "std", test))] pub type $config = $snake::GenesisConfig<$concrete>; - $crate::__impl_outer_config_types! {$concrete $($rest)*} + $crate::__impl_outer_config_types! { $concrete $( $rest )* } }; + // No Generic and maybe Instance ( - $concrete:ident $config:ident $snake:ident $( $rest:tt )* + $concrete:ident $config:ident $snake:ident $( { $instance:ident } )?; + $( $rest:tt )* ) => { #[cfg(any(feature = "std", test))] pub type $config = $snake::GenesisConfig; - __impl_outer_config_types! {$concrete $($rest)*} + $crate::__impl_outer_config_types! { $concrete $( $rest )* } }; ($concrete:ident) => () } @@ -619,30 +776,76 @@ macro_rules! __impl_outer_config_types { macro_rules! impl_outer_config { ( pub struct $main:ident for $concrete:ident { - $( $config:ident => $snake:ident $( < $generic:ident $(, $instance:path)? > )*, )* + $( $config:ident => + $snake:ident $( $instance:ident )? $( <$generic:ident> )*, )* } ) => { - $crate::__impl_outer_config_types! { $concrete $( $config $snake $( < $generic $(, $instance)? > )* )* } - #[cfg(any(feature = "std", test))] - #[derive($crate::serde::Serialize, $crate::serde::Deserialize)] - #[serde(rename_all = "camelCase")] - #[serde(deny_unknown_fields)] - pub struct $main { - $( - pub $snake: Option<$config>, - )* + $crate::__impl_outer_config_types! { + $concrete $( $config $snake $( { $instance } )? $( <$generic> )*; )* } - #[cfg(any(feature = "std", test))] - impl $crate::BuildStorage for $main { - fn assimilate_storage(self, top: &mut $crate::StorageOverlay, children: &mut $crate::ChildrenStorageOverlay) -> ::std::result::Result<(), String> { + + $crate::paste::item! { + #[cfg(any(feature = "std", test))] + #[derive($crate::serde::Serialize, $crate::serde::Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(deny_unknown_fields)] + pub struct $main { $( - if let Some(extra) = self.$snake { - extra.assimilate_storage(top, children)?; - } + pub [< $snake $(_ $instance )? >]: Option<$config>, )* - Ok(()) + } + #[cfg(any(feature = "std", test))] + impl $crate::BuildStorage for $main { + fn assimilate_storage( + self, + top: &mut $crate::StorageOverlay, + children: &mut $crate::ChildrenStorageOverlay + ) -> std::result::Result<(), String> { + $( + if let Some(extra) = self.[< $snake $(_ $instance )? >] { + $crate::impl_outer_config! { + @CALL_FN + $concrete; + $snake; + $( $instance )?; + extra; + top; + children; + } + } + )* + Ok(()) + } } } + }; + (@CALL_FN + $runtime:ident; + $module:ident; + $instance:ident; + $extra:ident; + $top:ident; + $children:ident; + ) => { + $crate::BuildModuleGenesisStorage::<$runtime, $module::$instance>::build_module_genesis_storage( + $extra, + $top, + $children, + )?; + }; + (@CALL_FN + $runtime:ident; + $module:ident; + ; + $extra:ident; + $top:ident; + $children:ident; + ) => { + $crate::BuildModuleGenesisStorage::<$runtime, $module::__InherentHiddenInstance>::build_module_genesis_storage( + $extra, + $top, + $children, + )?; } } @@ -666,14 +869,19 @@ impl ::serde::Serialize for OpaqueExtrinsic { } impl traits::Extrinsic for OpaqueExtrinsic { + type Call = (); + fn is_signed(&self) -> Option { None } + + fn new_unsigned(_call: Self::Call) -> Option { None } } #[cfg(test)] mod tests { use crate::codec::{Encode, Decode}; + use super::{Perbill, Permill}; macro_rules! per_thing_upper_test { ($num_type:tt, $per:tt) => { @@ -717,19 +925,19 @@ mod tests { fn compact_permill_perbill_encoding() { let tests = [(0u32, 1usize), (63, 1), (64, 2), (16383, 2), (16384, 4), (1073741823, 4), (1073741824, 5), (u32::max_value(), 5)]; for &(n, l) in &tests { - let compact: crate::codec::Compact = super::Permill(n).into(); + let compact: crate::codec::Compact = Permill(n).into(); let encoded = compact.encode(); assert_eq!(encoded.len(), l); - let decoded = >::decode(&mut & encoded[..]).unwrap(); - let permill: super::Permill = decoded.into(); - assert_eq!(permill, super::Permill(n)); + let decoded = >::decode(&mut & encoded[..]).unwrap(); + let permill: Permill = decoded.into(); + assert_eq!(permill, Permill(n)); - let compact: crate::codec::Compact = super::Perbill(n).into(); + let compact: crate::codec::Compact = Perbill(n).into(); let encoded = compact.encode(); assert_eq!(encoded.len(), l); - let decoded = >::decode(&mut & encoded[..]).unwrap(); - let perbill: super::Perbill = decoded.into(); - assert_eq!(perbill, super::Perbill(n)); + let decoded = >::decode(&mut & encoded[..]).unwrap(); + let perbill: Perbill = decoded.into(); + assert_eq!(perbill, Perbill(n)); } } @@ -740,16 +948,16 @@ mod tests { #[test] fn test_has_compact_permill() { - let data = WithCompact { data: super::Permill(1) }; + let data = WithCompact { data: Permill(1) }; let encoded = data.encode(); - assert_eq!(data, WithCompact::::decode(&mut &encoded[..]).unwrap()); + assert_eq!(data, WithCompact::::decode(&mut &encoded[..]).unwrap()); } #[test] fn test_has_compact_perbill() { - let data = WithCompact { data: super::Perbill(1) }; + let data = WithCompact { data: Perbill(1) }; let encoded = data.encode(); - assert_eq!(data, WithCompact::::decode(&mut &encoded[..]).unwrap()); + assert_eq!(data, WithCompact::::decode(&mut &encoded[..]).unwrap()); } #[test] @@ -769,7 +977,7 @@ mod tests { #[test] fn per_things_operate_in_output_type() { - assert_eq!(super::Perbill::one() * 255_u64, 255); + assert_eq!(Perbill::one() * 255_u64, 255); } #[test] @@ -777,12 +985,12 @@ mod tests { use primitive_types::U256; assert_eq!( - super::Perbill::from_parts(999_999_999) * std::u128::MAX, + Perbill::from_parts(999_999_999) * std::u128::MAX, ((Into::::into(std::u128::MAX) * 999_999_999u32) / 1_000_000_000u32).as_u128() ); assert_eq!( - super::Permill::from_parts(999_999) * std::u128::MAX, + Permill::from_parts(999_999) * std::u128::MAX, ((Into::::into(std::u128::MAX) * 999_999u32) / 1_000_000u32).as_u128() ); } diff --git a/core/sr-primitives/src/testing.rs b/core/sr-primitives/src/testing.rs index 35f3ec476f6d5b78b18c9c84f27fdefea75653fc..fcafe6dad97dec6d68679acf49481c68dcc4d61b 100644 --- a/core/sr-primitives/src/testing.rs +++ b/core/sr-primitives/src/testing.rs @@ -19,12 +19,16 @@ use serde::{Serialize, Serializer, Deserialize, de::Error as DeError, Deserializer}; use std::{fmt::Debug, ops::Deref, fmt}; use crate::codec::{Codec, Encode, Decode}; -use crate::traits::{self, Checkable, Applyable, BlakeTwo256, OpaqueKeys}; -use crate::generic; -use crate::weights::{Weighable, Weight}; +use crate::traits::{ + self, Checkable, Applyable, BlakeTwo256, OpaqueKeys, TypedKey, DispatchError, DispatchResult, + ValidateUnsigned, SignedExtension, Dispatchable, +}; +use crate::{generic, KeyTypeId}; +use crate::weights::{GetDispatchInfo, DispatchInfo}; pub use substrate_primitives::H256; use substrate_primitives::U256; use substrate_primitives::ed25519::{Public as AuthorityId}; +use crate::transaction_validity::TransactionValidity; /// Authority Id #[derive(Default, PartialEq, Eq, Clone, Encode, Decode, Debug)] @@ -37,12 +41,36 @@ impl Into for UintAuthorityId { } } +/// The key-type of the `UintAuthorityId` +pub const UINT_DUMMY_KEY: KeyTypeId = 0xdeadbeef; + +impl TypedKey for UintAuthorityId { + const KEY_TYPE: KeyTypeId = UINT_DUMMY_KEY; +} + +impl AsRef<[u8]> for UintAuthorityId { + fn as_ref(&self) -> &[u8] { + let ptr = self.0 as *const _; + // It's safe to do this here since `UintAuthorityId` is `u64`. + unsafe { std::slice::from_raw_parts(ptr, 8) } + } +} + impl OpaqueKeys for UintAuthorityId { - fn count() -> usize { 1 } + type KeyTypeIds = std::iter::Cloned>; + + fn key_ids() -> Self::KeyTypeIds { [UINT_DUMMY_KEY].iter().cloned() } // Unsafe, i know, but it's test code and it's just there because it's really convenient to // keep `UintAuthorityId` as a u64 under the hood. - fn get_raw(&self, _: usize) -> &[u8] { unsafe { &std::mem::transmute::<_, &[u8; 8]>(&self.0)[..] } } - fn get(&self, _: usize) -> Option { self.0.using_encoded(|mut x| T::decode(&mut x)) } + fn get_raw(&self, _: KeyTypeId) -> &[u8] { + unsafe { + std::slice::from_raw_parts( + &self.0 as *const _ as *const u8, + std::mem::size_of::(), + ) + } + } + fn get(&self, _: KeyTypeId) -> Option { self.0.using_encoded(|mut x| T::decode(&mut x)) } } /// Digest item @@ -117,6 +145,8 @@ impl<'a> Deserialize<'a> for Header { pub struct ExtrinsicWrapper(Xt); impl traits::Extrinsic for ExtrinsicWrapper { + type Call = (); + fn is_signed(&self) -> Option { None } @@ -178,50 +208,82 @@ impl<'a, Xt> Deserialize<'a> for Block where Block: Decode { } } -/// Test transaction, tuple of (sender, index, call) +/// Test transaction, tuple of (sender, call, signed_extra) /// with index only used if sender is some. /// /// If sender is some then the transaction is signed otherwise it is unsigned. #[derive(PartialEq, Eq, Clone, Encode, Decode)] -pub struct TestXt(pub Option, pub u64, pub Call); +pub struct TestXt(pub Option<(u64, Extra)>, pub Call); -impl Serialize for TestXt where TestXt: Encode -{ +impl Serialize for TestXt where TestXt: Encode { fn serialize(&self, seq: S) -> Result where S: Serializer { self.using_encoded(|bytes| seq.serialize_bytes(bytes)) } } -impl Debug for TestXt { +impl Debug for TestXt { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "TestXt({:?}, {:?})", self.0, self.1) + write!(f, "TestXt({:?}, ...)", self.0.as_ref().map(|x| &x.0)) } } -impl Checkable for TestXt { +impl Checkable for TestXt { type Checked = Self; fn check(self, _: &Context) -> Result { Ok(self) } } -impl traits::Extrinsic for TestXt { +impl traits::Extrinsic for TestXt { + type Call = Call; + fn is_signed(&self) -> Option { Some(self.0.is_some()) } + + fn new_unsigned(_c: Call) -> Option { + None + } } -impl Applyable for TestXt where - Call: 'static + Sized + Send + Sync + Clone + Eq + Codec + Debug, + +impl Applyable for TestXt where + Call: 'static + Sized + Send + Sync + Clone + Eq + Codec + Debug + Dispatchable, + Extra: SignedExtension, + Origin: From> { type AccountId = u64; - type Index = u64; type Call = Call; - fn sender(&self) -> Option<&u64> { self.0.as_ref() } - fn index(&self) -> Option<&u64> { self.0.as_ref().map(|_| &self.1) } - fn deconstruct(self) -> (Self::Call, Option) { - (self.2, self.0) + + fn sender(&self) -> Option<&u64> { self.0.as_ref().map(|x| &x.0) } + + /// Checks to see if this is a valid *transaction*. It returns information on it if so. + fn validate>(&self, + _info: DispatchInfo, + _len: usize, + ) -> TransactionValidity { + TransactionValidity::Valid(Default::default()) + } + + /// Executes all necessary logic needed prior to dispatch and deconstructs into function call, + /// index and sender. + fn dispatch(self, + info: DispatchInfo, + len: usize, + ) -> Result { + let maybe_who = if let Some((who, extra)) = self.0 { + Extra::pre_dispatch(extra, &who, info, len)?; + Some(who) + } else { + Extra::pre_dispatch_unsigned(info, len)?; + None + }; + Ok(self.1.dispatch(maybe_who.into())) } } -impl Weighable for TestXt { - fn weight(&self, len: usize) -> Weight { + +impl GetDispatchInfo for TestXt { + fn get_dispatch_info(&self) -> DispatchInfo { // for testing: weight == size. - len as Weight + DispatchInfo { + weight: self.encode().len() as u32, + ..Default::default() + } } } diff --git a/core/sr-primitives/src/traits.rs b/core/sr-primitives/src/traits.rs index b2bb7ab80511d659f6d179e37fdf89c5acb69841..eccf9751322cae311de1542bcd8b9a39181276dc 100644 --- a/core/sr-primitives/src/traits.rs +++ b/core/sr-primitives/src/traits.rs @@ -23,8 +23,10 @@ use runtime_io; #[cfg(feature = "std")] use serde::{Serialize, Deserialize, de::DeserializeOwned}; use substrate_primitives::{self, Hasher, Blake2Hasher}; use crate::codec::{Codec, Encode, Decode, HasCompact}; -use crate::transaction_validity::TransactionValidity; +use crate::transaction_validity::{ValidTransaction, TransactionValidity}; use crate::generic::{Digest, DigestItem}; +use crate::weights::DispatchInfo; +pub use substrate_primitives::crypto::TypedKey; pub use integer_sqrt::IntegerSquareRoot; pub use num_traits::{ Zero, One, Bounded, CheckedAdd, CheckedSub, CheckedMul, CheckedDiv, @@ -162,6 +164,13 @@ impl Convert for Identity { fn convert(a: T) -> T { a } } +/// A structure that performs standard conversion using the standard Rust conversion traits. +pub struct ConvertInto; + +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 @@ -594,11 +603,32 @@ pub trait MaybeHash {} #[cfg(not(feature = "std"))] impl MaybeHash for T {} +/// A type that provides a randomness beacon. +pub trait RandomnessBeacon { + /// Returns 32 bytes of random data. The output will change eventually, but + /// is not guaranteed to be different between any two calls. + /// + /// # Security + /// + /// This MUST NOT be used for gambling, as it can be influenced by a + /// malicious validator in the short term. It MAY be used in many + /// cryptographic protocols, however, so long as one remembers that this + /// (like everything else on-chain) is public. For example, it can be + /// used where a number is needed that cannot have been chosen by an + /// adversary, for purposes such as public-coin zero-knowledge proofs. + fn random() -> [u8; 32]; +} /// A type that can be used in runtime structures. pub trait Member: Send + Sync + Sized + MaybeDebug + Eq + PartialEq + Clone + 'static {} impl Member for T {} +/// Determine if a `MemberId` is a valid member. +pub trait IsMember { + /// Is the given `MemberId` a valid member? + fn is_member(member_id: &MemberId) -> bool; +} + /// 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 /// `parent_hash`, as well as a `digest` and a block `number`. @@ -679,10 +709,17 @@ pub trait Block: Clone + Send + Sync + Codec + Eq + MaybeSerializeDebugButNotDes } /// Something that acts like an `Extrinsic`. -pub trait Extrinsic { +pub trait Extrinsic: Sized { + /// The function call. + type Call; + /// Is this `Extrinsic` signed? /// If no information are available about signed/unsigned, `None` should be returned. fn is_signed(&self) -> Option { None } + + /// New instance of an unsigned extrinsic aka "inherent". `None` if this is an opaque + /// extrinsic type. + fn new_unsigned(_call: Self::Call) -> Option { None } } /// Extract the hashing type for a block. @@ -726,6 +763,184 @@ impl Checkable for T { } } +/// An abstract error concerning an attempt to verify, check or dispatch the transaction. This +/// cannot be more concrete because it's designed to work reasonably well over a broad range of +/// possible transaction types. +#[cfg_attr(feature = "std", derive(Debug))] +pub enum DispatchError { + /// General error to do with the inability to pay some fees (e.g. account balance too low). + Payment, + + /// General error to do with the permissions of the sender. + NoPermission, + + /// General error to do with the state of the system in general. + BadState, + + /// General error to do with the transaction being outdated (e.g. nonce too low). + Stale, + + /// General error to do with the transaction not yet being valid (e.g. nonce too high). + Future, + + /// General error to do with the transaction's proofs (e.g. signature). + BadProof, + +/* /// General error to do with actually executing the dispatched logic. + User(&'static str),*/ +} + +impl From for i8 { + fn from(e: DispatchError) -> i8 { + match e { + DispatchError::Payment => -64, + DispatchError::NoPermission => -65, + DispatchError::BadState => -66, + DispatchError::Stale => -67, + DispatchError::Future => -68, + DispatchError::BadProof => -69, + } + } +} + +/// Result of a module function call; either nothing (functions are only called for "side effects") +/// or an error message. +pub type DispatchResult = result::Result<(), &'static str>; + +/// A lazy call (module function and argument values) that can be executed via its `dispatch` +/// method. +pub trait Dispatchable { + /// Every function call from your runtime has an origin, which specifies where the extrinsic was + /// generated from. In the case of a signed extrinsic (transaction), the origin contains an + /// identifier for the caller. The origin can be empty in the case of an inherent extrinsic. + type Origin; + /// ... + type Trait; + /// Actually dispatch this call and result the result of it. + fn dispatch(self, origin: Self::Origin) -> DispatchResult; +} + +/// 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 +{ + /// The type which encodes the sender identity. + type AccountId; + + /// Any additional data that will go into the signed payload. This may be created dynamically + /// from the transaction using the `additional_signed` function. + type AdditionalSigned: Encode; + + /// Construct any additional data that should be in the signed payload of the transaction. Can + /// also perform any pre-signature-verification checks and return an error if needed. + fn additional_signed(&self) -> Result; + + /// Validate a signed transaction for the transaction queue. + fn validate( + &self, + _who: &Self::AccountId, + _info: DispatchInfo, + _len: usize, + ) -> Result { Ok(Default::default()) } + + /// Do any pre-flight stuff for a signed transaction. + fn pre_dispatch( + self, + who: &Self::AccountId, + info: DispatchInfo, + len: usize, + ) -> Result<(), DispatchError> { self.validate(who, info, len).map(|_| ()) } + + /// Validate an unsigned transaction for the transaction queue. Normally the default + /// implementation is fine since `ValidateUnsigned` is a better way of recognising and + /// validating unsigned transactions. + fn validate_unsigned( + _info: DispatchInfo, + _len: usize, + ) -> Result { Ok(Default::default()) } + + /// Do any pre-flight stuff for a unsigned transaction. + fn pre_dispatch_unsigned( + info: DispatchInfo, + len: usize, + ) -> Result<(), DispatchError> { Self::validate_unsigned(info, len).map(|_| ()) } +} + +macro_rules! tuple_impl_indexed { + ($first:ident, $($rest:ident,)+ ; $first_index:tt, $($rest_index:tt,)+) => { + tuple_impl_indexed!([$first] [$($rest)+] ; [$first_index,] [$($rest_index,)+]); + }; + ([$($direct:ident)+] ; [$($index:tt,)+]) => { + impl< + AccountId, + $($direct: SignedExtension),+ + > SignedExtension for ($($direct),+,) { + type AccountId = AccountId; + type AdditionalSigned = ($($direct::AdditionalSigned,)+); + fn additional_signed(&self) -> Result { + Ok(( $(self.$index.additional_signed()?,)+ )) + } + fn validate( + &self, + who: &Self::AccountId, + info: DispatchInfo, + len: usize, + ) -> Result { + let aggregator = vec![$(<$direct as SignedExtension>::validate(&self.$index, who, info, len)?),+]; + Ok(aggregator.into_iter().fold(ValidTransaction::default(), |acc, a| acc.combine_with(a))) + } + fn pre_dispatch( + self, + who: &Self::AccountId, + info: DispatchInfo, + len: usize, + ) -> Result<(), DispatchError> { + $(self.$index.pre_dispatch(who, info, len)?;)+ + Ok(()) + } + fn validate_unsigned( + info: DispatchInfo, + len: usize, + ) -> Result { + let aggregator = vec![$($direct::validate_unsigned(info, len)?),+]; + Ok(aggregator.into_iter().fold(ValidTransaction::default(), |acc, a| acc.combine_with(a))) + } + fn pre_dispatch_unsigned( + info: DispatchInfo, + len: usize, + ) -> Result<(), DispatchError> { + $($direct::pre_dispatch_unsigned(info, len)?;)+ + Ok(()) + } + } + + }; + ([$($direct:ident)+] [] ; [$($index:tt,)+] []) => { + tuple_impl_indexed!([$($direct)+] ; [$($index,)+]); + }; + ( + [$($direct:ident)+] [$first:ident $($rest:ident)*] + ; + [$($index:tt,)+] [$first_index:tt, $($rest_index:tt,)*] + ) => { + tuple_impl_indexed!([$($direct)+] ; [$($index,)+]); + tuple_impl_indexed!([$($direct)+ $first] [$($rest)*] ; [$($index,)+ $first_index,] [$($rest_index,)*]); + }; +} + +// TODO: merge this into `tuple_impl` once codec supports `trait Codec` for longer tuple lengths. #3152 +#[allow(non_snake_case)] +tuple_impl_indexed!(A, B, C, D, E, F, G, H, I, J, ; 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,); + +/// Only for base bone testing when you don't care about signed extensions at all.\ +#[cfg(feature = "std")] +impl SignedExtension for () { + type AccountId = u64; + type AdditionalSigned = (); + fn additional_signed(&self) -> rstd::result::Result<(), &'static str> { Ok(()) } +} + /// An "executable" piece of information, used by the standard Substrate Executive in order to /// enact a piece of extrinsic information by marshalling and dispatching to a named function /// call. @@ -735,16 +950,25 @@ impl Checkable for T { pub trait Applyable: Sized + Send + Sync { /// Id of the account that is responsible for this piece of information (sender). type AccountId: Member + MaybeDisplay; - /// Index allowing to disambiguate other `Applyable`s from the same `AccountId`. - type Index: Member + MaybeDisplay + SimpleArithmetic; - /// Function call. - type Call: Member; - /// Returns a reference to the index if any. - fn index(&self) -> Option<&Self::Index>; + + /// Type by which we can dispatch. Restricts the UnsignedValidator type. + type Call; + /// Returns a reference to the sender if any. fn sender(&self) -> Option<&Self::AccountId>; - /// Deconstructs into function call and sender. - fn deconstruct(self) -> (Self::Call, Option); + + /// Checks to see if this is a valid *transaction*. It returns information on it if so. + fn validate>(&self, + info: DispatchInfo, + len: usize, + ) -> TransactionValidity; + + /// Executes all necessary logic needed prior to dispatch and deconstructs into function call, + /// index and sender. + fn dispatch(self, + info: DispatchInfo, + len: usize, + ) -> Result; } /// Auxiliary wrapper that holds an api instance and binds it to the given lifetime. @@ -820,16 +1044,140 @@ pub trait ValidateUnsigned { /// Opaque datatype that may be destructured into a series of raw byte slices (which represent /// individual keys). pub trait OpaqueKeys: Clone { - /// Return the number of encoded keys. - fn count() -> usize { 0 } - /// Get the raw bytes of key with index `i`. - fn get_raw(&self, i: usize) -> &[u8]; + /// An iterator over the type IDs of keys that this holds. + type KeyTypeIds: IntoIterator; + + /// Return an iterator over the key-type IDs supported by this set. + fn key_ids() -> Self::KeyTypeIds; + /// 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`. - fn get(&self, i: usize) -> Option { T::decode(&mut self.get_raw(i)) } + fn get(&self, i: super::KeyTypeId) -> Option { T::decode(&mut self.get_raw(i)) } /// Verify a proof of ownership for the keys. fn ownership_proof_is_valid(&self, _proof: &[u8]) -> bool { true } } +struct TrailingZeroInput<'a>(&'a [u8]); +impl<'a> codec::Input for TrailingZeroInput<'a> { + fn read(&mut self, into: &mut [u8]) -> usize { + let len = into.len().min(self.0.len()); + into[..len].copy_from_slice(&self.0[..len]); + for i in &mut into[len..] { + *i = 0; + } + self.0 = &self.0[len..]; + into.len() + } +} + +/// This type can be converted into and possibly from an AccountId (which itself is generic). +pub trait AccountIdConversion: Sized { + /// Convert into an account ID. This is infallible. + fn into_account(&self) -> AccountId { self.into_sub_account(&()) } + + /// Try to convert an account ID into this type. Might not succeed. + fn try_from_account(a: &AccountId) -> Option { + Self::try_from_sub_account::<()>(a).map(|x| x.0) + } + + /// Convert this value amalgamated with the a secondary "sub" value into an account ID. This is + /// infallible. + /// + /// NOTE: The account IDs from this and from `into_account` are *not* guaranteed to be distinct + /// for any given value of `self`, nor are different invocations to this with different types + /// `T`. For example, the following will all encode to the same account ID value: + /// - `self.into_sub_account(0u32)` + /// - `self.into_sub_account(vec![0u8; 0])` + /// - `self.into_account()` + fn into_sub_account(&self, sub: S) -> AccountId; + + /// Try to convert an account ID into this type. Might not succeed. + 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 { + fn into_sub_account(&self, sub: S) -> T { + (Id::TYPE_ID, self, sub).using_encoded(|b| + T::decode(&mut TrailingZeroInput(b)) + ).unwrap_or_default() + } + + fn try_from_sub_account(x: &T) -> Option<(Self, S)> { + x.using_encoded(|d| { + if &d[0..4] != Id::TYPE_ID { return None } + let mut cursor = &d[4..]; + let result = Decode::decode(&mut cursor)?; + if cursor.iter().all(|x| *x == 0) { + Some(result) + } else { + None + } + }) + } +} + +#[cfg(test)] +mod tests { + use super::AccountIdConversion; + use crate::codec::{Encode, Decode}; + + #[derive(Encode, Decode, Default, PartialEq, Debug)] + struct U32Value(u32); + impl super::TypeId for U32Value { + const TYPE_ID: [u8; 4] = [0x0d, 0xf0, 0xfe, 0xca]; + } + // cafef00d + + #[derive(Encode, Decode, Default, PartialEq, Debug)] + struct U16Value(u16); + impl super::TypeId for U16Value { + const TYPE_ID: [u8; 4] = [0xfe, 0xca, 0x0d, 0xf0]; + } + // f00dcafe + + type AccountId = u64; + + #[test] + fn into_account_should_work() { + let r: AccountId = U32Value::into_account(&U32Value(0xdeadbeef)); + assert_eq!(r, 0x_deadbeef_cafef00d); + } + + #[test] + fn try_from_account_should_work() { + let r = U32Value::try_from_account(&0x_deadbeef_cafef00d_u64); + assert_eq!(r.unwrap(), U32Value(0xdeadbeef)); + } + + #[test] + fn into_account_with_fill_should_work() { + let r: AccountId = U16Value::into_account(&U16Value(0xc0da)); + assert_eq!(r, 0x_0000_c0da_f00dcafe); + } + + #[test] + fn try_from_account_with_fill_should_work() { + let r = U16Value::try_from_account(&0x0000_c0da_f00dcafe_u64); + assert_eq!(r.unwrap(), U16Value(0xc0da)); + } + + #[test] + fn bad_try_from_account_should_fail() { + let r = U16Value::try_from_account(&0x0000_c0de_baadcafe_u64); + assert!(r.is_none()); + let r = U16Value::try_from_account(&0x0100_c0da_f00dcafe_u64); + assert!(r.is_none()); + } +} + /// Calls a given macro a number of times with a set of fixed params and an incrementing numeral. /// e.g. /// ```nocompile @@ -850,41 +1198,62 @@ macro_rules! count { }; } -#[macro_export] -/// Just implement `OpaqueKeys` for a given tuple-struct. +/// 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. +/// +/// ```rust +/// use sr_primitives::{impl_opaque_keys, key_types, KeyTypeId}; +/// +/// impl_opaque_keys! { +/// pub struct Keys { +/// #[id(key_types::ED25519)] +/// pub ed25519: [u8; 32], +/// #[id(key_types::SR25519)] +/// pub sr25519: [u8; 32], +/// } +/// } +/// ``` +#[macro_export] macro_rules! impl_opaque_keys { ( - pub struct $name:ident ( $( $t:ty ),* $(,)* ); - ) => { - impl_opaque_keys! { - pub struct $name ( $( $t ,)* ); - impl OpaqueKeys for _ {} - } - }; - ( - pub struct $name:ident ( $( $t:ty ),* $(,)* ); - impl OpaqueKeys for _ { - $($rest:tt)* + 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))] - pub struct $name($( pub $t ,)*); + pub struct $name { + $( + pub $field: $type, + )* + } + impl $crate::traits::OpaqueKeys for $name { - fn count() -> usize { - let mut c = 0; - $( let _: $t; c += 1; )* - c + type KeyTypeIds = $crate::rstd::iter::Cloned< + $crate::rstd::slice::Iter<'static, $crate::KeyTypeId> + >; + + fn key_ids() -> Self::KeyTypeIds { + [ + $($key_id),* + ].iter().cloned() } - fn get_raw(&self, i: usize) -> &[u8] { - $crate::count!(impl_opaque_keys (!! self i) $($t),*); - &[] + + fn get_raw(&self, i: $crate::KeyTypeId) -> &[u8] { + match i { + $( + i if i == $key_id => self.$field.as_ref(), + )* + _ => &[], + } } - $($rest)* } }; - ( !! $self:ident $param_i:ident $i:tt) => { - if $param_i == $i { return $self.$i.as_ref() } - } } diff --git a/core/sr-primitives/src/transaction_validity.rs b/core/sr-primitives/src/transaction_validity.rs index f36599b67b42c35c52752ed9e9dfd3dee6e22e27..a6cc43c5a365d7637c29b51c37af9809e0580fd1 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::traits::DispatchError; /// Priority for a transaction. Additive. Higher is better. pub type TransactionPriority = u64; @@ -36,40 +37,81 @@ pub enum TransactionValidity { /// Transaction is invalid. Details are described by the error code. Invalid(i8), /// Transaction is valid. - Valid { - /// Priority of the transaction. - /// - /// Priority determines the ordering of two transactions that have all - /// their dependencies (required tags) satisfied. - priority: TransactionPriority, - /// Transaction dependencies - /// - /// A non-empty list signifies that some other transactions which provide - /// given tags are required to be included before that one. - requires: Vec, - /// Provided tags - /// - /// A list of tags this transaction provides. Successfully importing the transaction - /// will enable other transactions that depend on (require) those tags to be included as well. - /// Provided and required tags allow Substrate to build a dependency graph of transactions - /// and import them in the right (linear) order. - provides: Vec, - /// Transaction longevity - /// - /// Longevity describes minimum number of blocks the validity is correct. - /// After this period transaction should be removed from the pool or revalidated. - longevity: TransactionLongevity, - /// A flag indicating if the transaction should be propagated to other peers. - /// - /// By setting `false` here the transaction will still be considered for - /// including in blocks that are authored on the current node, but will - /// never be sent to other peers. - propagate: bool, - }, + Valid(ValidTransaction), /// Transaction validity can't be determined. Unknown(i8), } +impl From> for TransactionValidity { + fn from(r: Result) -> Self { + match r { + Ok(v) => TransactionValidity::Valid(v), + Err(e) => TransactionValidity::Invalid(e.into()), + } + } +} + +/// Information concerning a valid transaction. +#[derive(Clone, PartialEq, Eq, Encode)] +#[cfg_attr(feature = "std", derive(Debug))] +pub struct ValidTransaction { + /// Priority of the transaction. + /// + /// Priority determines the ordering of two transactions that have all + /// their dependencies (required tags) satisfied. + pub priority: TransactionPriority, + /// Transaction dependencies + /// + /// A non-empty list signifies that some other transactions which provide + /// given tags are required to be included before that one. + pub requires: Vec, + /// Provided tags + /// + /// A list of tags this transaction provides. Successfully importing the transaction + /// will enable other transactions that depend on (require) those tags to be included as well. + /// Provided and required tags allow Substrate to build a dependency graph of transactions + /// and import them in the right (linear) order. + pub provides: Vec, + /// Transaction longevity + /// + /// Longevity describes minimum number of blocks the validity is correct. + /// After this period transaction should be removed from the pool or revalidated. + pub longevity: TransactionLongevity, + /// A flag indicating if the transaction should be propagated to other peers. + /// + /// By setting `false` here the transaction will still be considered for + /// including in blocks that are authored on the current node, but will + /// never be sent to other peers. + pub propagate: bool, +} + +impl Default for ValidTransaction { + fn default() -> Self { + ValidTransaction { + priority: 0, + requires: vec![], + provides: vec![], + longevity: TransactionLongevity::max_value(), + propagate: true, + } + } +} + +impl ValidTransaction { + /// Combine two instances into one, as a best effort. This will take the superset of each of the + /// `provides` and `requires` tags, it will sum the priorities, take the minimum longevity and + /// the logic *And* of the propagate flags. + pub fn combine_with(mut self, mut other: ValidTransaction) -> Self { + ValidTransaction { + priority: self.priority.saturating_add(other.priority), + requires: { self.requires.append(&mut other.requires); self.requires }, + provides: { self.provides.append(&mut other.provides); self.provides }, + longevity: self.longevity.min(other.longevity), + propagate: self.propagate && other.propagate, + } + } +} + impl Decode for TransactionValidity { fn decode(value: &mut I) -> Option { match value.read_byte()? { @@ -81,9 +123,9 @@ impl Decode for TransactionValidity { let longevity = TransactionLongevity::decode(value)?; let propagate = bool::decode(value).unwrap_or(true); - Some(TransactionValidity::Valid { + Some(TransactionValidity::Valid(ValidTransaction { priority, requires, provides, longevity, propagate, - }) + })) }, 2 => Some(TransactionValidity::Unknown(i8::decode(value)?)), _ => None, @@ -101,24 +143,24 @@ mod tests { 1, 5, 0, 0, 0, 0, 0, 0, 0, 4, 16, 1, 2, 3, 4, 4, 12, 4, 5, 6, 42, 0, 0, 0, 0, 0, 0, 0 ]; - assert_eq!(TransactionValidity::decode(&mut &*old_encoding), Some(TransactionValidity::Valid { + assert_eq!(TransactionValidity::decode(&mut &*old_encoding), Some(TransactionValidity::Valid(ValidTransaction { priority: 5, requires: vec![vec![1, 2, 3, 4]], provides: vec![vec![4, 5, 6]], longevity: 42, propagate: true, - })); + }))); } #[test] fn should_encode_and_decode() { - let v = TransactionValidity::Valid { + let v = TransactionValidity::Valid(ValidTransaction { priority: 5, requires: vec![vec![1, 2, 3, 4]], provides: vec![vec![4, 5, 6]], longevity: 42, propagate: false, - }; + }); let encoded = v.encode(); assert_eq!( diff --git a/core/sr-primitives/src/weights.rs b/core/sr-primitives/src/weights.rs index 3443992c7396bb109b402b48a4bcd77b182ee291..c592d9e19546c67f09f8fa3081c20e9348a226e6 100644 --- a/core/sr-primitives/src/weights.rs +++ b/core/sr-primitives/src/weights.rs @@ -16,61 +16,229 @@ //! Primitives for transaction weighting. //! -//! Each dispatch function within `decl_module!` can now have an optional -//! `#[weight = $x]` attribute. $x can be any object that implements the -//! `Weighable` trait. By default, All transactions are annotated by -//! `#[weight = TransactionWeight::default()]`. +//! 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()]`. //! -//! Note that the decl_module macro _cannot_ enforce this and will simply fail -//! if an invalid struct is passed in. +//! 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. -/// The final type that each `#[weight = $x:expr]`'s -/// expression must evaluate to. +use crate::{Fixed64, traits::Saturating}; +use crate::codec::{Encode, Decode}; + +pub use crate::transaction_validity::TransactionPriority; +use crate::traits::Bounded; + +/// Numeric range of a transaction weight. pub type Weight = u32; -/// A `Call` enum (aka transaction) that can be weighted using the custom weight attribute of -/// its dispatchable functions. Is implemented by default in the `decl_module!`. -/// -/// Both the outer Call enum and the per-module individual ones will implement this. -/// The outer enum simply calls the inner ones based on call type. -pub trait Weighable { - /// Return the weight of this call. - /// The `len` argument is the encoded length of the transaction/call. - fn weight(&self, len: usize) -> Weight; +/// 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)] +pub enum DispatchClass { + /// A normal dispatch. + Normal, + /// An operational dispatch. + Operational, +} + +impl Default for DispatchClass { + fn default() -> Self { + DispatchClass::Normal + } +} + +impl From for DispatchClass { + fn from(tx: SimpleDispatchInfo) -> Self { + match tx { + SimpleDispatchInfo::FixedOperational(_) => DispatchClass::Operational, + SimpleDispatchInfo::MaxOperational => DispatchClass::Operational, + SimpleDispatchInfo::FreeOperational => DispatchClass::Operational, + + SimpleDispatchInfo::FixedNormal(_) => DispatchClass::Normal, + SimpleDispatchInfo::MaxNormal => DispatchClass::Normal, + SimpleDispatchInfo::FreeNormal => DispatchClass::Normal, + } + } +} + +/// A bundle of static information collected from the `#[weight = $x]` attributes. +#[cfg_attr(feature = "std", derive(PartialEq, Eq, Debug))] +#[derive(Clone, Copy, Default)] +pub struct DispatchInfo { + /// Weight of this transaction. + pub weight: Weight, + /// Class of this transaction. + pub class: DispatchClass, } -/// Default type used as the weight representative in a `#[weight = x]` attribute. +impl DispatchInfo { + /// Determine if this dispatch should pay the base length-related fee or not. + pub fn pay_length_fee(&self) -> bool { + match self.class { + DispatchClass::Normal => true, + // For now we assume all operational transactions don't pay the length fee. + DispatchClass::Operational => false, + } + } +} + +/// 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. + /// + /// This is done independently of its encoded size. + 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` +/// implementation of [`SimpleDispatchInfo`] is used. /// -/// A user may pass in any other type that implements [`Weighable`]. If not, the `Default` -/// implementation of [`TransactionWeight`] is used. -pub enum TransactionWeight { - /// Basic weight (base, byte). - /// The values contained are the base weight and byte weight respectively. - Basic(Weight, Weight), - /// Maximum fee. This implies that this transaction _might_ get included but - /// no more transaction can be added. This can be done by setting the - /// implementation to _maximum block weight_. - Max, - /// Free. The transaction does not increase the total weight - /// (i.e. is not included in weight calculation). - Free, +/// For each generalized group (`Normal` and `Operation`): +/// - A `Fixed` variant means weight fee is charged normally and the weight is the number +/// specified in the inner value of the variant. +/// - 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. +/// - `Operational` variants will be assigned the maximum priority. They can potentially consume +/// the entire block resource limit. +#[derive(Clone, Copy)] +pub enum SimpleDispatchInfo { + /// A normal dispatch with fixed weight. + FixedNormal(Weight), + /// A normal dispatch with the maximum weight. + MaxNormal, + /// A normal dispatch with no weight. + FreeNormal, + /// An operational dispatch with fixed weight. + FixedOperational(Weight), + /// An operational dispatch with the maximum weight. + MaxOperational, + /// An operational dispatch with no weight. + FreeOperational, } -impl Weighable for TransactionWeight { - fn weight(&self, len: usize) -> Weight { +impl WeighData for SimpleDispatchInfo { + fn weigh_data(&self, _: T) -> Weight { match self { - TransactionWeight::Basic(base, byte) => base + byte * len as Weight, - TransactionWeight::Max => 3 * 1024 * 1024, - TransactionWeight::Free => 0, + SimpleDispatchInfo::FixedNormal(w) => *w, + SimpleDispatchInfo::MaxNormal => Bounded::max_value(), + SimpleDispatchInfo::FreeNormal => Bounded::min_value(), + + SimpleDispatchInfo::FixedOperational(w) => *w, + SimpleDispatchInfo::MaxOperational => Bounded::max_value(), + SimpleDispatchInfo::FreeOperational => Bounded::min_value(), } } } -impl Default for TransactionWeight { +impl ClassifyDispatch for SimpleDispatchInfo { + fn classify_dispatch(&self, _: T) -> DispatchClass { + DispatchClass::from(*self) + } +} + +impl Default for SimpleDispatchInfo { fn default() -> Self { - // This implies that the weight is currently equal to tx-size, nothing more + // This implies that the weight is currently equal to 100, nothing more // for all substrate transactions that do NOT explicitly annotate weight. // TODO #2431 needs to be updated with proper max values. - TransactionWeight::Basic(0, 1) + SimpleDispatchInfo::FixedNormal(100) + } +} + +/// 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(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/Cargo.toml b/core/sr-sandbox/Cargo.toml index f8d98253ad7143e083a4dc039d5da81f2a1f7224..748bc543623e3d0bad0f46392cc97baeee0961f4 100755 --- a/core/sr-sandbox/Cargo.toml +++ b/core/sr-sandbox/Cargo.toml @@ -12,7 +12,7 @@ rustc_version = "0.2" wasmi = { version = "0.4.3", optional = true } primitives = { package = "substrate-primitives", path = "../primitives", default-features = false } rstd = { package = "sr-std", path = "../sr-std", default-features = false } -codec = { package = "parity-codec", version = "3.2", default-features = false } +codec = { package = "parity-codec", version = "4.1.1", default-features = false } [dev-dependencies] wabt = "~0.7.4" diff --git a/core/sr-sandbox/src/lib.rs b/core/sr-sandbox/src/lib.rs index 1e8b2b3f1df5e63f76012f7e642f8dd04422aabf..9cb39236b0e265eb6bfc4b58706aad3e74194547 100755 --- a/core/sr-sandbox/src/lib.rs +++ b/core/sr-sandbox/src/lib.rs @@ -96,7 +96,7 @@ impl Memory { /// /// The memory allocated with initial number of pages specified by `initial`. /// Minimal possible value for `initial` is 0 and maximum possible is `65536`. - /// (Since maximum addressible memory is 232 = 4GiB = 65536 * 64KiB). + /// (Since maximum addressable memory is 232 = 4GiB = 65536 * 64KiB). /// /// It is possible to limit maximum number of pages this memory instance can have by specifying /// `maximum`. If not specified, this memory instance would be able to allocate up to 4GiB. @@ -168,7 +168,6 @@ impl EnvironmentDefinitionBuilder { /// This instance can be used for invoking exported functions. pub struct Instance { inner: imp::Instance, - } impl Instance { diff --git a/core/sr-sandbox/without_std.rs b/core/sr-sandbox/without_std.rs index 894ba43eb0ccc39795ef89590835591fcfd4d50d..9fb255a5230f6cb076c769b7ff9ca2e20beae9e5 100755 --- a/core/sr-sandbox/without_std.rs +++ b/core/sr-sandbox/without_std.rs @@ -33,7 +33,7 @@ mod ffi { /// # Safety /// /// This function should be only called with a `HostFuncIndex` that was previously registered - /// in the environment defintion. Typically this should only + /// in the environment definition. Typically this should only /// be called with an argument received in `dispatch_thunk`. pub unsafe fn coerce_host_index_to_func(idx: HostFuncIndex) -> HostFuncType { // We need to ensure that sizes of a callable function pointer and host function index is diff --git a/core/sr-std/src/lib.rs b/core/sr-std/src/lib.rs index b9874bcc2018d5e680fdf51025ffaa77165700ac..24c137c285f41096e94b71a43eaa967fc7f16cc9 100644 --- a/core/sr-std/src/lib.rs +++ b/core/sr-std/src/lib.rs @@ -30,6 +30,32 @@ macro_rules! map { ) } +/// Feature gate some code that should only be run when `std` feature is enabled. +/// +/// # Example +/// +/// ``` +/// use sr_std::if_std; +/// +/// if_std! { +/// // This code is only being compiled and executed when the `std` feature is enabled. +/// println!("Hello native world"); +/// } +/// ``` +#[cfg(feature = "std")] +#[macro_export] +macro_rules! if_std { + ( $( $code:tt )* ) => { + $( $code )* + } +} + +#[cfg(not(feature = "std"))] +#[macro_export] +macro_rules! if_std { + ( $( $code:tt )* ) => {} +} + #[cfg(feature = "std")] include!("../with_std.rs"); diff --git a/core/sr-std/with_std.rs b/core/sr-std/with_std.rs index 5824e26241675e80336a96bf7570f128b909ed88..0d5ee04ed51ab9e5dee332b8a1c6447890eb31d3 100644 --- a/core/sr-std/with_std.rs +++ b/core/sr-std/with_std.rs @@ -38,4 +38,5 @@ pub use std::vec; pub mod collections { pub use std::collections::btree_map; pub use std::collections::btree_set; + pub use std::collections::vec_deque; } diff --git a/core/sr-std/without_std.rs b/core/sr-std/without_std.rs index db81372c2f0708e6f8e86ea171860f06efd2b539..327e271049d586f7f35b6292ca52a5352634d703 100755 --- a/core/sr-std/without_std.rs +++ b/core/sr-std/without_std.rs @@ -68,10 +68,11 @@ pub use core::slice; // Allow intepreting vectors of bytes as strings, but not constructing them. pub use core::str; // We are trying to avoid certain things here, such as `core::string` -// (if you need `String` you most probably doing something wrong, since +// (if you need `String` you are probably doing something wrong, since // runtime doesn't require anything human readable). pub mod collections { pub use alloc::collections::btree_map; pub use alloc::collections::btree_set; + pub use alloc::collections::vec_deque; } diff --git a/core/sr-version/Cargo.toml b/core/sr-version/Cargo.toml index b4810b8457d81271d44264f1cae48172b277d12c..fb9b0a468875c382321cc1eab635e901f8aae774 100644 --- a/core/sr-version/Cargo.toml +++ b/core/sr-version/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" [dependencies] impl-serde = { version = "0.1", optional = true } serde = { version = "1.0", optional = true, features = ["derive"] } -parity-codec = { version = "3.3", default-features = false, features = ["derive"] } +parity-codec = { version = "4.1.1", default-features = false, features = ["derive"] } rstd = { package = "sr-std", path = "../sr-std", default-features = false } runtime_primitives = { package = "sr-primitives", path = "../sr-primitives", default-features = false } diff --git a/core/state-db/Cargo.toml b/core/state-db/Cargo.toml index 8c02dbc450fb82989e45f864ca710611d30ed0d5..a8dad1e84e0393c274f7ea8a0095399d579e7d21 100644 --- a/core/state-db/Cargo.toml +++ b/core/state-db/Cargo.toml @@ -8,7 +8,7 @@ edition = "2018" parking_lot = "0.8.0" log = "0.4" primitives = { package = "substrate-primitives", path = "../../core/primitives" } -parity-codec = { version = "3.3", features = ["derive"] } +parity-codec = { version = "4.1.1", features = ["derive"] } [dev-dependencies] env_logger = "0.6" diff --git a/core/state-machine/Cargo.toml b/core/state-machine/Cargo.toml index b1cb98ae808875bd8a9d28fc6a38063979c361f3..bcb471d5306db5c76a9228606de83c590d72085c 100644 --- a/core/state-machine/Cargo.toml +++ b/core/state-machine/Cargo.toml @@ -8,13 +8,13 @@ edition = "2018" [dependencies] log = "0.4" parking_lot = "0.8.0" -hash-db = "0.12" -trie-db = "0.12" -trie-root = "0.12" +hash-db = "0.14.0" +trie-db = "0.14.0" +trie-root = "0.14.0" trie = { package = "substrate-trie", path = "../trie" } primitives = { package = "substrate-primitives", path = "../primitives" } panic-handler = { package = "substrate-panic-handler", path = "../panic-handler" } -parity-codec = "3.3" +parity-codec = "4.1.1" num-traits = "0.2" [dev-dependencies] diff --git a/core/state-machine/src/basic.rs b/core/state-machine/src/basic.rs index a18cadda9ad8f29453998c4b618c53a0bad46566..e922db260cf02bb1344b8c0c786b29efd32173f9 100644 --- a/core/state-machine/src/basic.rs +++ b/core/state-machine/src/basic.rs @@ -18,62 +18,64 @@ use std::collections::HashMap; use std::iter::FromIterator; +use crate::backend::{Backend, InMemory}; use hash_db::Hasher; use trie::trie_root; use primitives::offchain; -use primitives::storage::well_known_keys::{CHANGES_TRIE_CONFIG, CODE, HEAP_PAGES}; +use primitives::storage::well_known_keys::{HEAP_PAGES, is_child_storage_key}; use parity_codec::Encode; -use super::{ChildStorageKey, Externalities, OverlayedChanges}; +use super::{ChildStorageKey, Externalities}; use log::warn; /// Simple HashMap-based Externalities impl. +#[derive(Debug)] pub struct BasicExternalities { - inner: HashMap, Vec>, - changes: OverlayedChanges, - code: Option>, + top: HashMap, Vec>, + children: HashMap, HashMap, Vec>>, } impl BasicExternalities { /// Create a new instance of `BasicExternalities` - pub fn new(inner: HashMap, Vec>) -> Self { - Self::new_with_code(&[], inner) + pub fn new(top: HashMap, Vec>) -> Self { + Self::new_with_children(top, Default::default()) } - /// Create a new instance of `BasicExternalities` - pub fn new_with_code(code: &[u8], mut inner: HashMap, Vec>) -> Self { - let mut overlay = OverlayedChanges::default(); - overlay.collect_extrinsics(inner.contains_key(CHANGES_TRIE_CONFIG)); - inner.insert(HEAP_PAGES.to_vec(), 8u64.encode()); - + /// Create a new instance of `BasicExternalities` with children + pub fn new_with_children( + mut top: HashMap, Vec>, + children: HashMap, HashMap, Vec>>, + ) -> Self { + top.insert(HEAP_PAGES.to_vec(), 8u64.encode()); BasicExternalities { - inner, - changes: overlay, - code: Some(code.to_vec()), + top, + children, } } /// Insert key/value pub fn insert(&mut self, k: Vec, v: Vec) -> Option> { - self.inner.insert(k, v) + self.top.insert(k, v) } -} -impl ::std::fmt::Debug for BasicExternalities { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - write!(f, "{:?}", self.inner) + /// Consume self and returns inner storages + pub fn into_storages(self) -> ( + HashMap, Vec>, + HashMap, HashMap, Vec>>, + ) { + (self.top, self.children) } } impl PartialEq for BasicExternalities { fn eq(&self, other: &BasicExternalities) -> bool { - self.inner.eq(&other.inner) + self.top.eq(&other.top) && self.children.eq(&other.children) } } impl FromIterator<(Vec, Vec)> for BasicExternalities { fn from_iter, Vec)>>(iter: I) -> Self { - let mut t = Self::new(Default::default()); - t.inner.extend(iter); + let mut t = Self::default(); + t.top.extend(iter); t } } @@ -82,69 +84,84 @@ impl Default for BasicExternalities { fn default() -> Self { Self::new(Default::default()) } } -impl From for HashMap, Vec> { - fn from(tex: BasicExternalities) -> Self { - tex.inner.into() - } -} - -impl From< HashMap, Vec> > for BasicExternalities { +impl From, Vec>> for BasicExternalities { fn from(hashmap: HashMap, Vec>) -> Self { BasicExternalities { - inner: hashmap, - changes: Default::default(), - code: None, + top: hashmap, + children: Default::default(), } } } impl Externalities for BasicExternalities where H::Out: Ord { fn storage(&self, key: &[u8]) -> Option> { - match key { - CODE => self.code.clone(), - _ => self.inner.get(key).cloned(), - } + self.top.get(key).cloned() } fn original_storage(&self, key: &[u8]) -> Option> { Externalities::::storage(self, key) } - fn child_storage(&self, _storage_key: ChildStorageKey, _key: &[u8]) -> Option> { - None + 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 place_storage(&mut self, key: Vec, maybe_value: Option>) { - self.changes.set_storage(key.clone(), maybe_value.clone()); - match key.as_ref() { - CODE => self.code = maybe_value, - _ => { - match maybe_value { - Some(value) => { self.inner.insert(key, value); } - None => { self.inner.remove(&key); } - } - } + if is_child_storage_key(&key) { + warn!(target: "trie", "Refuse to set child storage key via main storage"); + return; + } + + match maybe_value { + Some(value) => { self.top.insert(key, value); } + None => { self.top.remove(&key); } } } - 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> + ) { + let child_map = self.children.entry(storage_key.into_owned()).or_default(); + if let Some(value) = value { + child_map.insert(key, value); + } else { + child_map.remove(&key); + } } - fn kill_child_storage(&mut self, _storage_key: ChildStorageKey) { } + fn kill_child_storage(&mut self, storage_key: ChildStorageKey) { + self.children.remove(storage_key.as_ref()); + } fn clear_prefix(&mut self, prefix: &[u8]) { - self.changes.clear_prefix(prefix); - self.inner.retain(|key, _| !key.starts_with(prefix)); + if is_child_storage_key(prefix) { + warn!( + target: "trie", + "Refuse to clear prefix that is part of child storage key via main storage" + ); + return; + } + + self.top.retain(|key, _| !key.starts_with(prefix)); } fn chain_id(&self) -> u64 { 42 } fn storage_root(&mut self) -> H::Out { - trie_root::(self.inner.clone()) + trie_root::(self.top.clone()) } - fn child_storage_root(&mut self, _storage_key: ChildStorageKey) -> Vec { - vec![42] + 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 + } else { + vec![] + } } fn storage_changes_root(&mut self, _parent: H::Out) -> Result, ()> { @@ -160,7 +177,8 @@ impl Externalities for BasicExternalities where H::Out: Ord { #[cfg(test)] mod tests { use super::*; - use primitives::{Blake2Hasher, H256}; + use primitives::{Blake2Hasher, H256, map}; + use primitives::storage::well_known_keys::CODE; use hex_literal::hex; #[test] @@ -184,4 +202,33 @@ mod tests { assert_eq!(&ext.storage(CODE).unwrap(), &code); } + + #[test] + fn children_works() { + let child_storage = b":child_storage:default:test".to_vec(); + + let mut ext = BasicExternalities::new_with_children( + Default::default(), + map![ + child_storage.clone() => map![ + b"doe".to_vec() => b"reindeer".to_vec() + ] + ] + ); + + 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())); + + ext.set_child_storage(child(), b"dog".to_vec(), b"puppy".to_vec()); + assert_eq!(ext.child_storage(child(), b"dog"), Some(b"puppy".to_vec())); + + ext.clear_child_storage(child(), b"dog"); + assert_eq!(ext.child_storage(child(), b"dog"), None); + + ext.kill_child_storage(child()); + assert_eq!(ext.child_storage(child(), b"doe"), None); + } } diff --git a/core/state-machine/src/changes_trie/build.rs b/core/state-machine/src/changes_trie/build.rs index 6b7170b871c4902002f4b9cfa107cfc0b7d8024f..22afcd11da2bb8a32cba3b08d3ad385b4aa7bea2 100644 --- a/core/state-machine/src/changes_trie/build.rs +++ b/core/state-machine/src/changes_trie/build.rs @@ -16,7 +16,8 @@ //! Structures and functions required to build changes trie for given block. -use std::collections::{BTreeMap, BTreeSet}; +use std::collections::BTreeMap; +use std::collections::btree_map::Entry; use parity_codec::Decode; use hash_db::Hasher; use num_traits::One; @@ -29,72 +30,87 @@ use crate::changes_trie::{AnchorBlockId, Configuration, Storage, BlockNumber}; /// Prepare input pairs for building a changes trie of given block. /// -/// Returns Err if storage error has occured OR if storage haven't returned +/// Returns Err if storage error has occurred OR if storage haven't returned /// required data. /// Returns Ok(None) data required to prepare input pairs is not collected /// or storage is not provided. pub fn prepare_input<'a, B, H, Number>( - backend: &B, + backend: &'a B, storage: &'a Storage, config_activation_block: Number, config: &'a Configuration, - changes: &OverlayedChanges, + changes: &'a OverlayedChanges, parent: &'a AnchorBlockId, -) -> Result>>, String> +) -> Result> + 'a, String> where B: Backend, - H: Hasher, + H: Hasher + 'a, Number: BlockNumber, { - let mut input = Vec::new(); - input.extend(prepare_extrinsics_input( + let number = parent.number.clone() + One::one(); + let extrinsics_input = prepare_extrinsics_input( backend, - parent.number.clone() + 1.into(), - changes)?); - input.extend(prepare_digest_input::( + &number, + changes)?; + let digest_input = prepare_digest_input::( parent, config_activation_block, config, - storage)?); - - Ok(Some(input)) + number, + storage)?; + Ok(extrinsics_input.chain(digest_input)) } /// Prepare ExtrinsicIndex input pairs. -fn prepare_extrinsics_input( - backend: &B, - block: Number, - changes: &OverlayedChanges, -) -> Result>, String> +fn prepare_extrinsics_input<'a, B, H, Number>( + backend: &'a B, + block: &Number, + changes: &'a OverlayedChanges, +) -> Result> + 'a, String> where B: Backend, H: Hasher, Number: BlockNumber, { - let mut extrinsic_map = BTreeMap::, BTreeSet>::new(); - for (key, val) in changes.prospective.top.iter().chain(changes.committed.top.iter()) { - let extrinsics = match val.extrinsics { - Some(ref extrinsics) => extrinsics, - None => continue, - }; + changes.committed.top.iter() + .chain(changes.prospective.top.iter()) + .filter(|( _, v)| v.extrinsics.is_some()) + .try_fold(BTreeMap::new(), |mut map: BTreeMap<&[u8], (ExtrinsicIndex, Vec)>, (k, v)| { + match map.entry(k) { + Entry::Vacant(entry) => { + // ignore temporary values (values that have null value at the end of operation + // AND are not in storage at the beginning of operation + if !changes.storage(k).map(|v| v.is_some()).unwrap_or_default() { + if !backend.exists_storage(k).map_err(|e| format!("{}", e))? { + return Ok(map); + } + } - // ignore values that have null value at the end of operation AND are not in storage - // at the beginning of operation - if !changes.storage(key).map(|v| v.is_some()).unwrap_or_default() { - if !backend.exists_storage(key).map_err(|e| format!("{}", e))? { - continue; + let extrinsics = v.extrinsics.as_ref() + .expect("filtered by filter() call above; qed") + .iter().cloned().collect(); + entry.insert((ExtrinsicIndex { + block: block.clone(), + key: k.to_vec(), + }, extrinsics)); + }, + Entry::Occupied(mut entry) => { + // we do not need to check for temporary values here, because entry is Occupied + // AND we are checking it before insertion + let extrinsics = &mut entry.get_mut().1; + extrinsics.extend( + v.extrinsics.as_ref() + .expect("filtered by filter() call above; qed") + .iter() + .cloned() + ); + extrinsics.sort_unstable(); + }, } - } - - extrinsic_map.entry(key.clone()).or_default() - .extend(extrinsics.iter().cloned()); - } - Ok(extrinsic_map.into_iter() - .map(move |(key, extrinsics)| InputPair::ExtrinsicIndex(ExtrinsicIndex { - block: block.clone(), - key, - }, extrinsics.iter().cloned().collect()))) + Ok(map) + }) + .map(|pairs| pairs.into_iter().map(|(_, (k, v))| InputPair::ExtrinsicIndex(k, v))) } /// Prepare DigestIndex input pairs. @@ -102,6 +118,7 @@ fn prepare_digest_input<'a, H, Number>( parent: &'a AnchorBlockId, config_activation_block: Number, config: &Configuration, + block: Number, storage: &'a Storage, ) -> Result> + 'a, String> where @@ -109,35 +126,52 @@ fn prepare_digest_input<'a, H, Number>( H::Out: 'a, Number: BlockNumber, { - let mut digest_map = BTreeMap::, BTreeSet>::new(); - for digest_build_block in digest_build_iterator(config, config_activation_block, parent.number.clone() + One::one()) { - let trie_root = storage.root(parent, digest_build_block.clone())?; - let trie_root = trie_root.ok_or_else(|| format!("No changes trie root for block {}", digest_build_block.clone()))?; - let trie_storage = TrieBackendEssence::<_, H>::new( - crate::changes_trie::TrieBackendStorageAdapter(storage), - trie_root, - ); + digest_build_iterator(config, config_activation_block, block.clone()) + .try_fold(BTreeMap::new(), move |mut map, digest_build_block| { + let trie_root = storage.root(parent, digest_build_block.clone())?; + let trie_root = trie_root.ok_or_else(|| format!("No changes trie root for block {}", digest_build_block.clone()))?; + let trie_storage = TrieBackendEssence::<_, H>::new( + crate::changes_trie::TrieBackendStorageAdapter(storage), + trie_root, + ); - let extrinsic_prefix = ExtrinsicIndex::key_neutral_prefix(digest_build_block.clone()); - trie_storage.for_keys_with_prefix(&extrinsic_prefix, |key| - if let Some(InputKey::ExtrinsicIndex::(trie_key)) = Decode::decode(&mut &key[..]) { - digest_map.entry(trie_key.key).or_default() - .insert(digest_build_block.clone()); - }); + let mut insert_to_map = |key: Vec| { + match map.entry(key.clone()) { + Entry::Vacant(entry) => { + entry.insert((DigestIndex { + block: block.clone(), + key, + }, vec![digest_build_block.clone()])); + }, + Entry::Occupied(mut entry) => { + // DigestIndexValue must be sorted. Here we are relying on the fact that digest_build_iterator() + // returns blocks in ascending order => we only need to check for duplicates + // + // is_dup_block could be true when key has been changed in both digest block + // AND other blocks that it covers + let is_dup_block = entry.get().1.last() == Some(&digest_build_block); + if !is_dup_block { + entry.get_mut().1.push(digest_build_block.clone()); + } + }, + } + }; - let digest_prefix = DigestIndex::key_neutral_prefix(digest_build_block.clone()); - trie_storage.for_keys_with_prefix(&digest_prefix, |key| - if let Some(InputKey::DigestIndex::(trie_key)) = Decode::decode(&mut &key[..]) { - digest_map.entry(trie_key.key).or_default() - .insert(digest_build_block.clone()); - }); - } + let extrinsic_prefix = ExtrinsicIndex::key_neutral_prefix(digest_build_block.clone()); + trie_storage.for_keys_with_prefix(&extrinsic_prefix, |key| + if let Some(InputKey::ExtrinsicIndex::(trie_key)) = Decode::decode(&mut &key[..]) { + insert_to_map(trie_key.key); + }); - Ok(digest_map.into_iter() - .map(move |(key, set)| InputPair::DigestIndex(DigestIndex { - block: parent.number.clone() + One::one(), - key - }, set.into_iter().collect()))) + let digest_prefix = DigestIndex::key_neutral_prefix(digest_build_block.clone()); + trie_storage.for_keys_with_prefix(&digest_prefix, |key| + if let Some(InputKey::DigestIndex::(trie_key)) = Decode::decode(&mut &key[..]) { + insert_to_map(trie_key.key); + }); + + Ok(map) + }) + .map(|pairs| pairs.into_iter().map(|(_, (k, v))| InputPair::DigestIndex(k, v))) } #[cfg(test)] @@ -228,33 +262,35 @@ mod test { #[test] fn build_changes_trie_nodes_on_non_digest_block() { let (backend, storage, changes, config) = prepare_for_build(); + let parent = AnchorBlockId { hash: Default::default(), number: 4 }; let changes_trie_nodes = prepare_input( &backend, &storage, 0, // TODO: test other cases &config, &changes, - &AnchorBlockId { hash: Default::default(), number: 4 }, + &parent, ).unwrap(); - assert_eq!(changes_trie_nodes, Some(vec![ + assert_eq!(changes_trie_nodes.collect::>>(), vec![ InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 5, key: vec![100] }, vec![0, 2, 3]), InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 5, key: vec![101] }, vec![1]), InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 5, key: vec![103] }, vec![0, 1]), - ])); + ]); } #[test] fn build_changes_trie_nodes_on_digest_block_l1() { let (backend, storage, changes, config) = prepare_for_build(); + let parent = AnchorBlockId { hash: Default::default(), number: 3 }; let changes_trie_nodes = prepare_input( &backend, &storage, 0, // TODO: test other cases &config, &changes, - &AnchorBlockId { hash: Default::default(), number: 3 }, + &parent, ).unwrap(); - assert_eq!(changes_trie_nodes, Some(vec![ + assert_eq!(changes_trie_nodes.collect::>>(), vec![ InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 4, key: vec![100] }, vec![0, 2, 3]), InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 4, key: vec![101] }, vec![1]), InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 4, key: vec![103] }, vec![0, 1]), @@ -263,21 +299,22 @@ mod test { InputPair::DigestIndex(DigestIndex { block: 4, key: vec![101] }, vec![1]), InputPair::DigestIndex(DigestIndex { block: 4, key: vec![102] }, vec![2]), InputPair::DigestIndex(DigestIndex { block: 4, key: vec![105] }, vec![1, 3]), - ])); + ]); } #[test] fn build_changes_trie_nodes_on_digest_block_l2() { let (backend, storage, changes, config) = prepare_for_build(); + let parent = AnchorBlockId { hash: Default::default(), number: 15 }; let changes_trie_nodes = prepare_input( &backend, &storage, 0, // TODO: test other cases &config, &changes, - &AnchorBlockId { hash: Default::default(), number: 15 }, + &parent, ).unwrap(); - assert_eq!(changes_trie_nodes, Some(vec![ + assert_eq!(changes_trie_nodes.collect::>>(), vec![ InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 16, key: vec![100] }, vec![0, 2, 3]), InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 16, key: vec![101] }, vec![1]), InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 16, key: vec![103] }, vec![0, 1]), @@ -287,7 +324,7 @@ mod test { InputPair::DigestIndex(DigestIndex { block: 16, key: vec![102] }, vec![4]), InputPair::DigestIndex(DigestIndex { block: 16, key: vec![103] }, vec![4]), InputPair::DigestIndex(DigestIndex { block: 16, key: vec![105] }, vec![4, 8]), - ])); + ]); } #[test] @@ -300,15 +337,16 @@ mod test { extrinsics: Some(vec![1].into_iter().collect()) }); + let parent = AnchorBlockId { hash: Default::default(), number: 3 }; let changes_trie_nodes = prepare_input( &backend, &storage, 0, // TODO: test other cases &config, &changes, - &AnchorBlockId { hash: Default::default(), number: 3 }, + &parent, ).unwrap(); - assert_eq!(changes_trie_nodes, Some(vec![ + assert_eq!(changes_trie_nodes.collect::>>(), vec![ InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 4, key: vec![100] }, vec![0, 2, 3]), InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 4, key: vec![101] }, vec![1]), InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 4, key: vec![103] }, vec![0, 1]), @@ -317,6 +355,6 @@ mod test { InputPair::DigestIndex(DigestIndex { block: 4, key: vec![101] }, vec![1]), InputPair::DigestIndex(DigestIndex { block: 4, key: vec![102] }, vec![2]), InputPair::DigestIndex(DigestIndex { block: 4, key: vec![105] }, vec![1, 3]), - ])); + ]); } } diff --git a/core/state-machine/src/changes_trie/build_iterator.rs b/core/state-machine/src/changes_trie/build_iterator.rs index 8cb7b8a90d5f759a774d0035fc3916cc9bccbfa5..afb27ff0949c4d05dcd54e5f3fe59d560336eed7 100644 --- a/core/state-machine/src/changes_trie/build_iterator.rs +++ b/core/state-machine/src/changes_trie/build_iterator.rs @@ -15,12 +15,13 @@ // along with Substrate. If not, see . //! Structures and functions to return blocks whose changes are to be included -//! in given block' changes trie. +//! in given block's changes trie. use crate::changes_trie::{Configuration, BlockNumber}; /// Returns iterator of OTHER blocks that are required for inclusion into -/// changes trie of given block. +/// changes trie of given block. Blocks are guaranteed to be returned in +/// ascending order. pub fn digest_build_iterator( config: &Configuration, zero: Number, @@ -48,6 +49,8 @@ pub struct DigestBuildIterator { max_step: u32, /// Step of current blocks range. current_step: u32, + /// Reverse step of current blocks range. + current_step_reverse: u32, /// Current blocks range. current_range: Option>, } @@ -59,7 +62,8 @@ impl DigestBuildIterator { block, digest_interval, max_step, - current_step: 0, + current_step: max_step, + current_step_reverse: 0, current_range: None, } } @@ -82,18 +86,27 @@ impl Iterator for DigestBuildIterator { // DigestBuildIterator is created only by internal function that is checking // that all multiplications/subtractions are safe within max_step limit - let next_step = if self.current_step == 0 { 1 } else { self.current_step * self.digest_interval }; - if next_step > self.max_step { + let next_step_reverse = if self.current_step_reverse == 0 { + 1 + } else { + self.current_step_reverse * self.digest_interval + }; + if next_step_reverse > self.max_step { return None; } - self.current_step = next_step; + self.current_step_reverse = next_step_reverse; self.current_range = Some(BlocksRange::new( self.block.clone() - (self.current_step * self.digest_interval - self.current_step).into(), self.block.clone(), self.current_step.into(), )); + self.current_step = self.current_step / self.digest_interval; + if self.current_step == 0 { + self.current_step = 1; + } + Some(self.current_range.as_mut() .expect("assigned one line above; qed") .next() @@ -253,18 +266,18 @@ mod tests { fn test_with_zero(zero: u64) { assert_eq!(digest_build_iterator_blocks(16, 2, zero, zero + 256), [ - // level2 is a level1 digest of 16-1 previous blocks: - 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, // level2 points to previous 16-1 level1 digests: 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240, + // level2 is a level1 digest of 16-1 previous blocks: + 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, ].iter().map(|item| zero + item).collect::>(), ); assert_eq!(digest_build_iterator_blocks(16, 2, zero, zero + 4096), [ - // level2 is a level1 digest of 16-1 previous blocks: - 4081, 4082, 4083, 4084, 4085, 4086, 4087, 4088, 4089, 4090, 4091, 4092, 4093, 4094, 4095, // level2 points to previous 16-1 level1 digests: 3856, 3872, 3888, 3904, 3920, 3936, 3952, 3968, 3984, 4000, 4016, 4032, 4048, 4064, 4080, + // level2 is a level1 digest of 16-1 previous blocks: + 4081, 4082, 4083, 4084, 4085, 4086, 4087, 4088, 4089, 4090, 4091, 4092, 4093, 4094, 4095, ].iter().map(|item| zero + item).collect::>(), ); } @@ -279,12 +292,12 @@ mod tests { fn test_with_zero(zero: u64) { assert_eq!(digest_build_iterator_blocks(16, 3, zero, zero + 4096), [ - // level3 is a level1 digest of 16-1 previous blocks: - 4081, 4082, 4083, 4084, 4085, 4086, 4087, 4088, 4089, 4090, 4091, 4092, 4093, 4094, 4095, - // level3 points to previous 16-1 level1 digests: - 3856, 3872, 3888, 3904, 3920, 3936, 3952, 3968, 3984, 4000, 4016, 4032, 4048, 4064, 4080, // level3 points to previous 16-1 level2 digests: 256, 512, 768, 1024, 1280, 1536, 1792, 2048, 2304, 2560, 2816, 3072, 3328, 3584, 3840, + // level3 points to previous 16-1 level1 digests: + 3856, 3872, 3888, 3904, 3920, 3936, 3952, 3968, 3984, 4000, 4016, 4032, 4048, 4064, 4080, + // level3 is a level1 digest of 16-1 previous blocks: + 4081, 4082, 4083, 4084, 4085, 4086, 4087, 4088, 4089, 4090, 4091, 4092, 4093, 4094, 4095, ].iter().map(|item| zero + item).collect::>(), ); } diff --git a/core/state-machine/src/changes_trie/changes_iterator.rs b/core/state-machine/src/changes_trie/changes_iterator.rs index 30114be01865fe5644b89ad2aad9547a2b4fdd54..5ea722efda2be6b9cd95da73180ace1a57f4435d 100644 --- a/core/state-machine/src/changes_trie/changes_iterator.rs +++ b/core/state-machine/src/changes_trie/changes_iterator.rs @@ -112,7 +112,7 @@ pub fn key_changes_proof( Ok(iter.extract_proof()) } -/// Check key changes proog and return changes of the key at given blocks range. +/// Check key changes proof and return changes of the key at given blocks range. /// `max` is the number of best known block. /// Changes are returned in descending order (i.e. last block comes first). pub fn key_changes_proof_check( diff --git a/core/state-machine/src/changes_trie/mod.rs b/core/state-machine/src/changes_trie/mod.rs index 2b3bf618bfbd05ce347496094fc4211381a980df..91dee5f49460bf48853a0dc7897f84be389aa431 100644 --- a/core/state-machine/src/changes_trie/mod.rs +++ b/core/state-machine/src/changes_trie/mod.rs @@ -53,7 +53,7 @@ use parity_codec::{Decode, Encode}; use primitives; use crate::changes_trie::build::prepare_input; use crate::overlayed_changes::OverlayedChanges; -use trie::{DBValue, trie_root}; +use trie::{MemoryDB, TrieDBMut, TrieMut, DBValue}; /// Changes that are made outside of extrinsics are marked with this index; pub const NO_EXTRINSIC_INDEX: u32 = 0xffffffff; @@ -162,13 +162,13 @@ pub fn disabled_state<'a, H, Number>() -> Option> { /// Compute the changes trie root and transaction for given block. /// Returns Err(()) if unknown `parent_hash` has been passed. /// Returns Ok(None) if there's no data to perform computation. -/// Panics if background storage returns an error. -pub fn compute_changes_trie_root<'a, B: Backend, H: Hasher, Number: BlockNumber>( +/// Panics if background storage returns an error OR if insert to MemoryDB fails. +pub fn build_changes_trie<'a, B: Backend, H: Hasher, Number: BlockNumber>( backend: &B, state: Option<&'a State<'a, H, Number>>, changes: &OverlayedChanges, parent_hash: H::Out, -) -> Result, Vec)>)>, ()> +) -> Result, H::Out)>, ()> where H::Out: Ord + 'static, { @@ -183,16 +183,16 @@ pub fn compute_changes_trie_root<'a, B: Backend, H: Hasher, Number: BlockNumb // storage errors are considered fatal (similar to situations when runtime fetches values from storage) let input_pairs = prepare_input::(backend, state.storage, state.config_activation_block.clone(), &state.config, changes, &parent) - .expect("storage is not allowed to fail within runtime"); - match input_pairs { - Some(input_pairs) => { - let transaction = input_pairs.into_iter() - .map(Into::into) - .collect::>(); - let root = trie_root::(transaction.iter().map(|(k, v)| (&*k, &*v))); - - Ok(Some((root, transaction))) - }, - None => Ok(None), + .expect("changes trie: storage access is not allowed to fail within runtime"); + let mut root = Default::default(); + let mut mdb = MemoryDB::default(); + { + let mut trie = TrieDBMut::::new(&mut mdb, &mut root); + for (key, value) in input_pairs.map(Into::into) { + trie.insert(&key, &value) + .expect("changes trie: insertion to trie is not allowed to fail within runtime"); + } } + + Ok(Some((mdb, root))) } diff --git a/core/state-machine/src/ext.rs b/core/state-machine/src/ext.rs index 365696ad05e38a25dbf37f1b22ded8c6117eb540..35967133a4495bb25d3c3ddbd14fb6cf149ea7bb 100644 --- a/core/state-machine/src/ext.rs +++ b/core/state-machine/src/ext.rs @@ -14,17 +14,17 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -//! Conrete externalities implementation. +//! Concrete externalities implementation. use std::{error, fmt, cmp::Ord}; use log::warn; use crate::backend::Backend; -use crate::changes_trie::{State as ChangesTrieState, compute_changes_trie_root}; +use crate::changes_trie::{State as ChangesTrieState, build_changes_trie}; use crate::{Externalities, OverlayedChanges, ChildStorageKey}; use hash_db::Hasher; use primitives::offchain; use primitives::storage::well_known_keys::is_child_storage_key; -use trie::{MemoryDB, TrieDBMut, TrieMut, default_child_trie_root}; +use trie::{MemoryDB, default_child_trie_root}; const EXT_NOT_ALLOWED_TO_FAIL: &str = "Externalities not allowed to fail within runtime"; @@ -80,7 +80,7 @@ where changes_trie_transaction: Option<(MemoryDB, H::Out)>, /// Additional externalities for offchain workers. /// - /// If None, some methods from the trait might not supported. + /// If None, some methods from the trait might not be supported. offchain_externalities: Option<&'a mut O>, /// Dummy usage of N arg. _phantom: ::std::marker::PhantomData, @@ -113,7 +113,7 @@ where } /// Get the transaction necessary to update the backend. - pub fn transaction(mut self) -> (B::Transaction, Option>) { + pub fn transaction(mut self) -> ((B::Transaction, H::Out), Option>) { let _ = self.storage_root(); let (storage_transaction, changes_trie_transaction) = ( @@ -124,7 +124,7 @@ where ); ( - storage_transaction.0, + storage_transaction, changes_trie_transaction, ) } @@ -316,28 +316,13 @@ where fn storage_changes_root(&mut self, parent_hash: H::Out) -> Result, ()> { let _guard = panic_handler::AbortGuard::new(true); - - let root_and_tx = compute_changes_trie_root::<_, H, N>( + self.changes_trie_transaction = build_changes_trie::<_, H, N>( self.backend, self.changes_trie_state.clone(), self.overlay, parent_hash, )?; - let root_and_tx = root_and_tx.map(|(root, changes)| { - let mut calculated_root = Default::default(); - let mut mdb = MemoryDB::default(); - { - let mut trie = TrieDBMut::::new(&mut mdb, &mut calculated_root); - for (key, value) in changes { - trie.insert(&key, &value).expect(EXT_NOT_ALLOWED_TO_FAIL); - } - } - - (mdb, root) - }); - let root = root_and_tx.as_ref().map(|(_, root)| root.clone()); - self.changes_trie_transaction = root_and_tx; - Ok(root) + Ok(self.changes_trie_transaction.as_ref().map(|(_, root)| root.clone())) } fn offchain(&mut self) -> Option<&mut dyn offchain::Externalities> { diff --git a/core/state-machine/src/lib.rs b/core/state-machine/src/lib.rs index 9275245448f47df21f38a8d067ad0632f635aac7..e20c1546995b14d509d118b1fc85e46f5560f1ca 100644 --- a/core/state-machine/src/lib.rs +++ b/core/state-machine/src/lib.rs @@ -62,7 +62,7 @@ pub use trie_backend::TrieBackend; /// A wrapper around a child storage key. /// -/// This wrapper ensures that the child storage key is correct and properly used. It is +/// 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, H: Hasher> { storage_key: Cow<'a, [u8]>, @@ -240,16 +240,29 @@ impl offchain::Externalities for NeverOffchainExt { unreachable!() } + fn network_state( + &self, + ) -> Result { + unreachable!() + } + + fn pubkey( + &self, + _key: offchain::CryptoKey, + ) -> Result, ()> { + unreachable!() + } + fn new_crypto_key( &mut self, _crypto: offchain::CryptoKind, - ) -> Result { + ) -> Result { unreachable!() } fn encrypt( &mut self, - _key: Option, + _key: offchain::CryptoKey, _data: &[u8], ) -> Result, ()> { unreachable!() @@ -257,19 +270,23 @@ impl offchain::Externalities for NeverOffchainExt { fn decrypt( &mut self, - _key: Option, + _key: offchain::CryptoKey, _data: &[u8], ) -> Result, ()> { unreachable!() } - fn sign(&mut self, _key: Option, _data: &[u8]) -> Result, ()> { + fn sign( + &mut self, + _key: offchain::CryptoKey, + _data: &[u8], + ) -> Result, ()> { unreachable!() } fn verify( &mut self, - _key: Option, + _key: offchain::CryptoKey, _msg: &[u8], _signature: &[u8], ) -> Result { @@ -288,15 +305,21 @@ impl offchain::Externalities for NeverOffchainExt { unreachable!() } - fn local_storage_set(&mut self, _key: &[u8], _value: &[u8]) { + fn local_storage_set(&mut self, _kind: offchain::StorageKind, _key: &[u8], _value: &[u8]) { unreachable!() } - fn local_storage_compare_and_set(&mut self, _key: &[u8], _old_value: &[u8], _new_value: &[u8]) { + fn local_storage_compare_and_set( + &mut self, + _kind: offchain::StorageKind, + _key: &[u8], + _old_value: &[u8], + _new_value: &[u8], + ) -> bool { unreachable!() } - fn local_storage_get(&mut self, _key: &[u8]) -> Option> { + fn local_storage_get(&mut self, _kind: offchain::StorageKind, _key: &[u8]) -> Option> { unreachable!() } @@ -501,7 +524,7 @@ impl<'a, H, N, B, O, Exec> StateMachine<'a, H, N, B, O, Exec> where pub fn execute( &mut self, strategy: ExecutionStrategy, - ) -> Result<(Vec, B::Transaction, Option>), Box> { + ) -> 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() -> _>( @@ -521,7 +544,12 @@ impl<'a, H, N, B, O, Exec> StateMachine<'a, H, N, B, O, Exec> where compute_tx: bool, use_native: bool, native_call: Option, - ) -> (CallResult, bool, Option, Option>) where + ) -> ( + CallResult, + bool, + Option<(B::Transaction, H::Out)>, + Option>, + ) where R: Decode + Encode + PartialEq, NC: FnOnce() -> result::Result + UnwindSafe, { @@ -553,7 +581,7 @@ impl<'a, H, N, B, O, Exec> StateMachine<'a, H, N, B, O, Exec> where mut native_call: Option, orig_prospective: OverlayedChangeSet, on_consensus_failure: Handler, - ) -> (CallResult, Option, Option>) where + ) -> (CallResult, Option<(B::Transaction, H::Out)>, Option>) where R: Decode + Encode + PartialEq, NC: FnOnce() -> result::Result + UnwindSafe, Handler: FnOnce( @@ -584,7 +612,7 @@ impl<'a, H, N, B, O, Exec> StateMachine<'a, H, N, B, O, Exec> where compute_tx: bool, mut native_call: Option, orig_prospective: OverlayedChangeSet, - ) -> (CallResult, Option, Option>) where + ) -> (CallResult, Option<(B::Transaction, H::Out)>, Option>) where R: Decode + Encode + PartialEq, NC: FnOnce() -> result::Result + UnwindSafe, { @@ -612,7 +640,11 @@ impl<'a, H, N, B, O, Exec> StateMachine<'a, H, N, B, O, Exec> where manager: ExecutionManager, compute_tx: bool, mut native_call: Option, - ) -> Result<(NativeOrEncoded, Option, Option>), Box> where + ) -> Result<( + NativeOrEncoded, + Option<(B::Transaction, H::Out)>, + Option> + ), Box> where R: Decode + Encode + PartialEq, NC: FnOnce() -> result::Result + UnwindSafe, Handler: FnOnce( diff --git a/core/state-machine/src/overlayed_changes.rs b/core/state-machine/src/overlayed_changes.rs index 9a48d9cd771b5b5565e639d7e22b8202d1b13613..facb342a6169983f2ac327cab21ff0eedbf62151 100644 --- a/core/state-machine/src/overlayed_changes.rs +++ b/core/state-machine/src/overlayed_changes.rs @@ -17,7 +17,7 @@ //! The overlayed changes to state. #[cfg(test)] use std::iter::FromIterator; -use std::collections::{HashMap, HashSet}; +use std::collections::{HashMap, BTreeSet}; use parity_codec::Decode; use crate::changes_trie::NO_EXTRINSIC_INDEX; use primitives::storage::well_known_keys::EXTRINSIC_INDEX; @@ -44,7 +44,7 @@ pub struct OverlayedValue { pub value: Option>, /// The set of extinsic indices where the values has been changed. /// Is filled only if runtime has announced changes trie support. - pub extrinsics: Option>, + pub extrinsics: Option>, } /// Prospective or committed overlayed change set. @@ -54,7 +54,7 @@ pub struct OverlayedChangeSet { /// Top level storage changes. pub top: HashMap, OverlayedValue>, /// Child storage changes. - pub children: HashMap, (Option>, HashMap, Option>>)>, + pub children: HashMap, (Option>, HashMap, Option>>)>, } #[cfg(test)] diff --git a/core/state-machine/src/testing.rs b/core/state-machine/src/testing.rs index e52c96326e7d8d20e8176dad86a735afc0a1262c..b21579f1ba538cc52248ca97bda48fe6ba13d6af 100644 --- a/core/state-machine/src/testing.rs +++ b/core/state-machine/src/testing.rs @@ -16,7 +16,7 @@ //! Test implementation for Externalities. -use std::collections::{HashMap, BTreeMap}; +use std::collections::{HashMap}; use std::iter::FromIterator; use hash_db::Hasher; use num_traits::Zero; @@ -24,7 +24,7 @@ use parity_codec::Decode; use crate::backend::{InMemory, Backend}; use primitives::storage::well_known_keys::is_child_storage_key; use crate::changes_trie::{ - compute_changes_trie_root, InMemoryStorage as ChangesTrieInMemoryStorage, + build_changes_trie, InMemoryStorage as ChangesTrieInMemoryStorage, BlockNumber as ChangesTrieBlockNumber, Configuration as ChangesTrieConfiguration, State as ChangesTrieState, }; @@ -35,6 +35,8 @@ use super::{ChildStorageKey, Externalities, OverlayedChanges}; const EXT_NOT_ALLOWED_TO_FAIL: &str = "Externalities not allowed to fail within runtime"; +type StorageTuple = (HashMap, Vec>, HashMap, HashMap, Vec>>); + /// Simple HashMap-based Externalities impl. pub struct TestExternalities { overlay: OverlayedChanges, @@ -45,26 +47,44 @@ pub struct TestExternalities { } impl TestExternalities { - /// Create a new instance of `TestExternalities` - pub fn new(inner: HashMap, Vec>) -> Self { - Self::new_with_code(&[], inner) + /// Create a new instance of `TestExternalities` with storage. + pub fn new(storage: HashMap, Vec>) -> Self { + Self::new_with_children((storage, Default::default())) + } + + /// Create a new instance of `TestExternalities` with storage and children. + pub fn new_with_children(storage: StorageTuple) -> Self { + Self::new_with_code_with_children(&[], storage) + } + + /// Create a new instance of `TestExternalities` with code and storage. + pub fn new_with_code(code: &[u8], storage: HashMap, Vec>) -> Self { + Self::new_with_code_with_children(code, (storage, Default::default())) } - /// Create a new instance of `TestExternalities` - pub fn new_with_code(code: &[u8], mut inner: HashMap, Vec>) -> Self { + /// Create a new instance of `TestExternalities` with code, storage and children. + pub fn new_with_code_with_children(code: &[u8], mut storage: StorageTuple) -> Self { let mut overlay = OverlayedChanges::default(); - let changes_trie_config = inner.get(CHANGES_TRIE_CONFIG) + let changes_trie_config = storage.0.get(CHANGES_TRIE_CONFIG) .and_then(|v| Decode::decode(&mut &v[..])); overlay.collect_extrinsics(changes_trie_config.is_some()); - inner.insert(HEAP_PAGES.to_vec(), 8u64.encode()); - inner.insert(CODE.to_vec(), code.to_vec()); + assert!(storage.0.keys().all(|key| !is_child_storage_key(key))); + assert!(storage.1.keys().all(|key| is_child_storage_key(key))); + + storage.0.insert(HEAP_PAGES.to_vec(), 8u64.encode()); + storage.0.insert(CODE.to_vec(), code.to_vec()); + + let backend: HashMap<_, _> = storage.1.into_iter() + .map(|(keyspace, map)| (Some(keyspace), map)) + .chain(Some((None, storage.0)).into_iter()) + .collect(); TestExternalities { overlay, changes_trie_config, changes_trie_storage: ChangesTrieInMemoryStorage::new(), - backend: inner.into(), + backend: backend.into(), offchain: None, } } @@ -74,17 +94,6 @@ impl TestExternalities { self.backend = self.backend.update(vec![(None, k, Some(v))]); } - /// Iter to all pairs in key order - pub fn iter_pairs_in_order(&self) -> impl Iterator, Vec)> { - self.backend.pairs().iter() - .map(|&(ref k, ref v)| (k.to_vec(), Some(v.to_vec()))) - .chain(self.overlay.committed.top.clone().into_iter().map(|(k, v)| (k, v.value))) - .chain(self.overlay.prospective.top.clone().into_iter().map(|(k, v)| (k, v.value))) - .collect::>() - .into_iter() - .filter_map(|(k, maybe_val)| maybe_val.map(|val| (k, val))) - } - /// Set offchain externaltiies. pub fn set_offchain_externalities(&mut self, offchain: impl offchain::Externalities + 'static) { self.offchain = Some(Box::new(offchain)); @@ -94,9 +103,26 @@ impl TestExternalities { pub fn changes_trie_storage(&mut self) -> &mut ChangesTrieInMemoryStorage { &mut self.changes_trie_storage } + + /// Return a new backend with all pending value. + pub fn commit_all(&self) -> InMemory { + let top = self.overlay.committed.top.clone().into_iter() + .chain(self.overlay.prospective.top.clone().into_iter()) + .map(|(k, v)| (None, k, v.value)); + + let children = self.overlay.committed.children.clone().into_iter() + .chain(self.overlay.prospective.children.clone().into_iter()) + .flat_map(|(keyspace, map)| { + map.1.into_iter() + .map(|(k, v)| (Some(keyspace.clone()), k, v)) + .collect::>() + }); + + self.backend.update(top.chain(children).collect()) + } } -impl ::std::fmt::Debug for TestExternalities { +impl 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()) } @@ -106,15 +132,13 @@ impl 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 { - self.iter_pairs_in_order().eq(other.iter_pairs_in_order()) + self.commit_all().eq(&other.commit_all()) } } impl FromIterator<(Vec, Vec)> for TestExternalities { fn from_iter, Vec)>>(iter: I) -> Self { - let mut t = Self::new(Default::default()); - t.backend = t.backend.update(iter.into_iter().map(|(k, v)| (None, k, Some(v))).collect()); - t + Self::new(iter.into_iter().collect()) } } @@ -122,15 +146,15 @@ impl Default for TestExternalities { fn default() -> Self { Self::new(Default::default()) } } -impl From> for HashMap, Vec> { - fn from(tex: TestExternalities) -> Self { - tex.iter_pairs_in_order().collect() +impl From, Vec>> for TestExternalities { + fn from(hashmap: HashMap, Vec>) -> Self { + Self::from_iter(hashmap) } } -impl From< HashMap, Vec> > for TestExternalities { - fn from(hashmap: HashMap, Vec>) -> Self { - Self::from_iter(hashmap) +impl From for TestExternalities { + fn from(storage: StorageTuple) -> Self { + Self::new_with_children(storage) } } @@ -231,12 +255,12 @@ impl Externalities for TestExternalities match self.changes_trie_config.clone() { Some(config) => { let state = ChangesTrieState::new(config, Zero::zero(), &self.changes_trie_storage); - Ok(compute_changes_trie_root::<_, H, N>( + Ok(build_changes_trie::<_, H, N>( &self.backend, Some(&state), &self.overlay, parent, - )?.map(|(root, _)| root.clone())) + )?.map(|(_, root)| root)) }, None => Ok(None), } diff --git a/core/telemetry/Cargo.toml b/core/telemetry/Cargo.toml index 1b19796836abb060f2911b30e4fc9411998254fb..ca95fe94e55a3ddc3ef3fc03fb44425550494471 100644 --- a/core/telemetry/Cargo.toml +++ b/core/telemetry/Cargo.toml @@ -8,8 +8,10 @@ edition = "2018" [dependencies] bytes = "0.4" parking_lot = "0.8.0" -futures = "0.1" -libp2p = { version = "0.9.1", default-features = false, features = ["libp2p-websocket"] } +futures01 = { package = "futures", version = "0.1" } +futures-preview = { version = "0.3.0-alpha.17", features = ["compat"] } +futures-timer = "0.2.1" +libp2p = { version = "0.10.0", default-features = false, features = ["libp2p-websocket"] } log = "0.4" rand = "0.6" serde = { version = "1.0.81", features = ["derive"] } @@ -17,8 +19,4 @@ slog = { version = "^2", features = ["nested-values"] } slog-json = { version = "^2", features = ["nested-values"] } slog-scope = "^4" tokio-io = "0.1" -tokio-timer = "0.2" void = "1.0" - -[dev-dependencies] -tokio = "0.1" diff --git a/core/telemetry/src/lib.rs b/core/telemetry/src/lib.rs index b9d99548510bb62735761421a2fcb5a2ae8ab9a7..88d515e5385d27697c60505ee79f63a89267ed0f 100644 --- a/core/telemetry/src/lib.rs +++ b/core/telemetry/src/lib.rs @@ -48,7 +48,7 @@ //! //! // The `telemetry` object implements `Stream` and must be processed. //! std::thread::spawn(move || { -//! tokio::run(telemetry.for_each(|_| Ok(()))); +//! futures::executor::block_on(telemetry.for_each(|_| future::ready(()))); //! }); //! //! // Sends a message on the telemetry. @@ -58,13 +58,12 @@ //! ``` //! -use futures::{prelude::*, task::AtomicTask}; +use futures::{prelude::*, task::AtomicWaker}; use libp2p::{Multiaddr, wasm_ext}; use log::warn; use parking_lot::Mutex; use serde::{Serialize, Deserialize}; -use std::sync::{Arc, Weak}; -use std::time::{Duration, Instant}; +use std::{pin::Pin, sync::{Arc, Weak}, task::{Context, Poll}, time::{Duration, Instant}}; pub use slog_scope::with_logger; pub use slog; @@ -131,8 +130,8 @@ pub struct Telemetry { struct TelemetryInner { /// Worker for the telemetry. worker: Mutex, - /// Task to wake up when we add a log entry to the worker. - polling_task: AtomicTask, + /// Waker to wake up when we add a log entry to the worker. + polling_waker: AtomicWaker, } /// Implements `slog::Drain`. @@ -156,7 +155,7 @@ pub fn init_telemetry(config: TelemetryConfig) -> Telemetry { let inner = Arc::new(TelemetryInner { worker: Mutex::new(worker::TelemetryWorker::new(endpoints, config.wasm_external_transport)), - polling_task: AtomicTask::new(), + polling_waker: AtomicWaker::new(), }); let guard = { @@ -181,13 +180,12 @@ pub enum TelemetryEvent { impl Stream for Telemetry { type Item = TelemetryEvent; - type Error = (); - fn poll(&mut self) -> Poll, Self::Error> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { let before = Instant::now(); let mut has_connected = false; - while let Async::Ready(event) = self.inner.worker.lock().poll() { + while let Poll::Ready(event) = self.inner.worker.lock().poll(cx) { // Right now we only have one possible event. This line is here in order to not // forget to handle any possible new event type. let worker::TelemetryWorkerEvent::Connected = event; @@ -199,10 +197,10 @@ impl Stream for Telemetry { } if has_connected { - Ok(Async::Ready(Some(TelemetryEvent::Connected))) + Poll::Ready(Some(TelemetryEvent::Connected)) } else { - self.inner.polling_task.register(); - Ok(Async::NotReady) + self.inner.polling_waker.register(cx.waker()); + Poll::Pending } } } @@ -215,7 +213,7 @@ impl slog::Drain for TelemetryDrain { if let Some(inner) = self.inner.0.upgrade() { let before = Instant::now(); let result = inner.worker.lock().log(record, values); - inner.polling_task.notify(); + inner.polling_waker.wake(); if before.elapsed() > Duration::from_millis(50) { warn!(target: "telemetry", "Writing a telemetry log took more than 50ms"); } diff --git a/core/telemetry/src/worker.rs b/core/telemetry/src/worker.rs index 87a3deb6ef9605793893ede5f9f5ddea8c4e279e..e13270937899c69bc610fb949ca67986ef20cb85 100644 --- a/core/telemetry/src/worker.rs +++ b/core/telemetry/src/worker.rs @@ -27,12 +27,11 @@ //! use bytes::BytesMut; -use futures::prelude::*; +use futures::compat::Compat01As03Sink; use libp2p::{core::transport::OptionalTransport, core::ConnectedPoint, Multiaddr, Transport, wasm_ext}; use log::{trace, warn, error}; use slog::Drain; -use std::{io, time}; -use tokio_io::AsyncWrite; +use std::{io, pin::Pin, task::Context, task::Poll, time}; mod node; @@ -58,19 +57,29 @@ pub struct TelemetryWorker { /// The pile of libp2p transports. #[cfg(not(target_os = "unknown"))] type WsTrans = libp2p::core::transport::timeout::TransportTimeout< - libp2p::core::transport::OrTransport< - libp2p::core::transport::map::Map< - OptionalTransport, - fn(wasm_ext::Connection, ConnectedPoint) -> StreamSink + libp2p::core::transport::map::Map< + libp2p::core::transport::OrTransport< + libp2p::core::transport::map::Map< + OptionalTransport, + fn(wasm_ext::Connection, ConnectedPoint) -> StreamSink + >, + libp2p::websocket::framed::WsConfig> >, - libp2p::websocket::framed::WsConfig> + fn(libp2p::core::either::EitherOutput, + libp2p::websocket::framed::BytesConnection>, ConnectedPoint) + -> Compat01As03Sink, + libp2p::websocket::framed::BytesConnection>, BytesMut> > >; #[cfg(target_os = "unknown")] type WsTrans = libp2p::core::transport::timeout::TransportTimeout< libp2p::core::transport::map::Map< - OptionalTransport, - fn(wasm_ext::Connection, ConnectedPoint) -> StreamSink + libp2p::core::transport::map::Map< + OptionalTransport, + fn(wasm_ext::Connection, ConnectedPoint) -> StreamSink + >, + fn(StreamSink, ConnectedPoint) + -> Compat01As03Sink, BytesMut> > >; @@ -98,7 +107,9 @@ impl TelemetryWorker { libp2p::websocket::framed::WsConfig::new(inner) }); - let transport = transport.with_timeout(CONNECT_TIMEOUT); + let transport = transport + .map((|inner, _| Compat01As03Sink::new(inner)) as fn(_, _) -> _) + .with_timeout(CONNECT_TIMEOUT); TelemetryWorker { nodes: endpoints.into_iter().map(|(addr, verbosity)| { @@ -109,19 +120,19 @@ impl TelemetryWorker { } /// Polls the worker for events that happened. - pub fn poll(&mut self) -> Async { + pub fn poll(&mut self, cx: &mut Context) -> Poll { for (node, _) in &mut self.nodes { loop { - match node.poll() { - Async::Ready(node::NodeEvent::Connected) => - return Async::Ready(TelemetryWorkerEvent::Connected), - Async::Ready(node::NodeEvent::Disconnected(_)) => continue, - Async::NotReady => break, + match node::Node::poll(Pin::new(node), cx) { + Poll::Ready(node::NodeEvent::Connected) => + return Poll::Ready(TelemetryWorkerEvent::Connected), + Poll::Ready(node::NodeEvent::Disconnected(_)) => continue, + Poll::Pending => break, } } } - Async::NotReady + Poll::Pending } /// Equivalent to `slog::Drain::log`, but takes `self` by `&mut` instead, which is more convenient. @@ -132,7 +143,7 @@ impl TelemetryWorker { let msg_verbosity = match record.tag().parse::() { Ok(v) => v, Err(err) => { - warn!(target: "telemetry", "Failed to parse telemetry tag {:?}: {:?}", + warn!(target: "telemetry", "Failed to parse telemetry tag {:?}: {:?}", record.tag(), err); return Err(()) } @@ -176,27 +187,29 @@ impl TelemetryWorker { /// For some context, we put this object around the `wasm_ext::ExtTransport` in order to make sure /// that each telemetry message maps to one single call to `write` in the WASM FFI. struct StreamSink(T); -impl Sink for StreamSink { +impl futures01::Sink for StreamSink { type SinkItem = BytesMut; type SinkError = io::Error; - fn start_send(&mut self, item: Self::SinkItem) -> Result, io::Error> { + fn start_send(&mut self, item: Self::SinkItem) + -> Result, io::Error> { match self.0.write(&item[..]) { - Ok(n) if n == item.len() => Ok(AsyncSink::Ready), + Ok(n) if n == item.len() => Ok(futures01::AsyncSink::Ready), Ok(_) => { error!(target: "telemetry", "Detected some internal buffering happening in the telemetry"); Err(io::Error::new(io::ErrorKind::Other, "Internal buffering detected")) }, - Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => Ok(AsyncSink::NotReady(item)), + Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => + Ok(futures01::AsyncSink::NotReady(item)), Err(err) => Err(err), } } - fn poll_complete(&mut self) -> Poll<(), io::Error> { + fn poll_complete(&mut self) -> futures01::Poll<(), io::Error> { match self.0.flush() { - Ok(()) => Ok(Async::Ready(())), - Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => Ok(Async::NotReady), + Ok(()) => Ok(futures01::Async::Ready(())), + Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => Ok(futures01::Async::NotReady), Err(err) => Err(err), } } diff --git a/core/telemetry/src/worker/node.rs b/core/telemetry/src/worker/node.rs index a4d8f8d84e2cb22f0db900625e4fa4bf58af1fea..fc09e90c7db54a4cab6d4e7864b20747a893df4d 100644 --- a/core/telemetry/src/worker/node.rs +++ b/core/telemetry/src/worker/node.rs @@ -17,13 +17,13 @@ //! Contains the `Node` struct, which handles communications with a single telemetry endpoint. use bytes::BytesMut; -use futures::prelude::*; +use futures::{prelude::*, compat::{Future01CompatExt as _, Compat01As03}}; +use futures_timer::Delay; use libp2p::Multiaddr; use libp2p::core::transport::Transport; use log::{trace, debug, warn, error}; use rand::Rng as _; -use std::{collections::VecDeque, fmt, mem, time::Duration, time::Instant}; -use tokio_timer::Delay; +use std::{collections::VecDeque, fmt, mem, pin::Pin, task::Context, task::Poll, time::Duration}; /// Maximum number of pending telemetry messages. const MAX_PENDING: usize = 10; @@ -42,7 +42,7 @@ enum NodeSocket { /// We're connected to the node. This is the normal state. Connected(NodeSocketConnected), /// We are currently dialing the node. - Dialing(TTrans::Dial), + Dialing(Compat01As03), /// A new connection should be started as soon as possible. ReconnectNow, /// Waiting before attempting to dial again. @@ -86,8 +86,8 @@ impl Node { } impl Node -where TTrans: Clone, TTrans::Output: Sink, - TSinkErr: fmt::Debug { +where TTrans: Clone + Unpin, TTrans::Dial: Unpin, + TTrans::Output: Sink + Unpin, TSinkErr: fmt::Debug { /// Sends a WebSocket frame to the node. Returns an error if we are not connected to the node. /// /// After calling this method, you should call `poll` in order for it to be properly processed. @@ -108,29 +108,30 @@ where TTrans: Clone, TTrans::Output: Sink Async> { + pub fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { let mut socket = mem::replace(&mut self.socket, NodeSocket::Poisoned); self.socket = loop { match socket { - NodeSocket::Connected(mut conn) => match conn.poll(&self.addr) { - Ok(Async::Ready(v)) => void::unreachable(v), - Ok(Async::NotReady) => break NodeSocket::Connected(conn), - Err(err) => { - debug!(target: "telemetry", "Disconnected from {}: {:?}", self.addr, err); - let timeout = gen_rand_reconnect_delay(); - self.socket = NodeSocket::WaitingReconnect(timeout); - return Async::Ready(NodeEvent::Disconnected(err)) + 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(Err(err)) => { + debug!(target: "telemetry", "Disconnected from {}: {:?}", self.addr, err); + let timeout = gen_rand_reconnect_delay(); + self.socket = NodeSocket::WaitingReconnect(timeout); + return Poll::Ready(NodeEvent::Disconnected(err)) + } } - } - NodeSocket::Dialing(mut s) => match s.poll() { - Ok(Async::Ready(sink)) => { + 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 }; self.socket = NodeSocket::Connected(conn); - return Async::Ready(NodeEvent::Connected) + return Poll::Ready(NodeEvent::Connected) }, - Ok(Async::NotReady) => break NodeSocket::Dialing(s), - Err(err) => { + Poll::Pending => break NodeSocket::Dialing(s), + Poll::Ready(Err(err)) => { debug!(target: "telemetry", "Error while dialing {}: {:?}", self.addr, err); let timeout = gen_rand_reconnect_delay(); socket = NodeSocket::WaitingReconnect(timeout); @@ -139,7 +140,7 @@ where TTrans: Clone, TTrans::Output: Sink match self.transport.clone().dial(self.addr.clone()) { Ok(d) => { debug!(target: "telemetry", "Started dialing {}", self.addr); - socket = NodeSocket::Dialing(d); + socket = NodeSocket::Dialing(d.compat()); } Err(err) => { debug!(target: "telemetry", "Error while dialing {}: {:?}", self.addr, err); @@ -147,11 +148,12 @@ where TTrans: Clone, TTrans::Output: Sink if let Ok(Async::Ready(_)) = s.poll() { - socket = NodeSocket::ReconnectNow; - } else { - break NodeSocket::WaitingReconnect(s) - } + NodeSocket::WaitingReconnect(mut s) => + if let Poll::Ready(_) = Future::poll(Pin::new(&mut s), cx) { + socket = NodeSocket::ReconnectNow; + } else { + break NodeSocket::WaitingReconnect(s) + } NodeSocket::Poisoned => { error!(target: "telemetry", "Poisoned connection with {}", self.addr); break NodeSocket::Poisoned @@ -159,7 +161,7 @@ where TTrans: Clone, TTrans::Output: Sink Delay { let random_delay = rand::thread_rng().gen_range(5, 10); - Delay::new(Instant::now() + Duration::from_secs(random_delay)) + Delay::new(Duration::from_secs(random_delay)) } impl NodeSocketConnected -where TTrans::Output: Sink { +where TTrans::Output: Sink + Unpin { /// Processes the queue of messages for the connected socket. /// /// The address is passed for logging purposes only. - fn poll(&mut self, my_addr: &Multiaddr) -> Poll { + fn poll( + mut self: Pin<&mut Self>, + cx: &mut Context, + my_addr: &Multiaddr + ) -> Poll> { loop { if let Some(item) = self.pending.pop_front() { - let item_len = item.len(); - if let AsyncSink::NotReady(item) = self.sink.start_send(item)? { + if let Poll::Pending = Sink::poll_ready(Pin::new(&mut self.sink), cx) { self.pending.push_front(item); - break - } else { - trace!(target: "telemetry", "Successfully sent {:?} bytes message to {}", - item_len, my_addr); - self.need_flush = true; + return Poll::Pending } - } else if self.need_flush && self.sink.poll_complete()?.is_ready() { - self.need_flush = false; + let item_len = item.len(); + if let Err(err) = Sink::start_send(Pin::new(&mut self.sink), item) { + return Poll::Ready(Err(err)) + } + trace!( + target: "telemetry", "Successfully sent {:?} bytes message to {}", + item_len, my_addr + ); + self.need_flush = true; + } else if self.need_flush { + match Sink::poll_flush(Pin::new(&mut self.sink), cx) { + Poll::Pending => {} + Poll::Ready(Err(err)) => return Poll::Ready(Err(err)), + Poll::Ready(Ok(())) => self.need_flush = false, + } } else { break } } - Ok(Async::NotReady) + Poll::Pending } } diff --git a/core/test-client/Cargo.toml b/core/test-client/Cargo.toml index 0bb8367c146dc4df6cedd3c62e311e3f75a1055e..0d709fab68305b2e4209bf7e8712fc7f2bad19ac 100644 --- a/core/test-client/Cargo.toml +++ b/core/test-client/Cargo.toml @@ -9,10 +9,10 @@ client = { package = "substrate-client", path = "../client" } client-db = { package = "substrate-client-db", path = "../client/db", features = ["test-helpers"] } consensus = { package = "substrate-consensus-common", path = "../consensus/common" } executor = { package = "substrate-executor", path = "../executor" } -futures = { version = "0.1.27" } -hash-db = "0.12" +futures-preview = "0.3.0-alpha.17" +hash-db = "0.14.0" keyring = { package = "substrate-keyring", path = "../keyring" } -parity-codec = "3.5.1" +parity-codec = "4.1.1" primitives = { package = "substrate-primitives", path = "../primitives" } runtime_primitives = { package = "sr-primitives", path = "../sr-primitives" } state_machine = { package = "substrate-state-machine", path = "../state-machine" } diff --git a/core/test-client/src/client_ext.rs b/core/test-client/src/client_ext.rs index 7d05b1f570da9600eedb5360d21de9b1418f8d0d..cb0d5c1736e1de80cb64a294f3dc7e6f9e19e177 100644 --- a/core/test-client/src/client_ext.rs +++ b/core/test-client/src/client_ext.rs @@ -18,7 +18,7 @@ use client::{self, Client}; use consensus::{ - ImportBlock, BlockImport, BlockOrigin, Error as ConsensusError, + BlockImportParams, BlockImport, BlockOrigin, Error as ConsensusError, ForkChoiceStrategy, }; use hash_db::Hasher; @@ -57,14 +57,14 @@ impl ClientExt for Client where B: client::backend::Backend, E: client::CallExecutor, - Self: BlockImport, + for<'r> &'r Self: BlockImport, Block: BlockT::Out>, { fn import(&self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError> { let (header, extrinsics) = block.deconstruct(); - let import = ImportBlock { + let import = BlockImportParams { origin, header, justification: None, @@ -75,7 +75,7 @@ impl ClientExt for Client fork_choice: ForkChoiceStrategy::LongestChain, }; - self.import_block(import, HashMap::new()).map(|_| ()) + BlockImport::import_block(&mut (&*self), import, HashMap::new()).map(|_| ()) } fn import_justified( @@ -85,7 +85,7 @@ impl ClientExt for Client justification: Justification, ) -> Result<(), ConsensusError> { let (header, extrinsics) = block.deconstruct(); - let import = ImportBlock { + let import = BlockImportParams { origin, header, justification: Some(justification), @@ -96,7 +96,7 @@ impl ClientExt for Client fork_choice: ForkChoiceStrategy::LongestChain, }; - self.import_block(import, HashMap::new()).map(|_| ()) + BlockImport::import_block(&mut (&*self), import, HashMap::new()).map(|_| ()) } fn finalize_block( diff --git a/core/test-client/src/lib.rs b/core/test-client/src/lib.rs index 40fbd10d9e43870d50fbd463da7b9709b3a314ed..509863e4e56ae544463a50af457e955d98580c38 100644 --- a/core/test-client/src/lib.rs +++ b/core/test-client/src/lib.rs @@ -32,7 +32,7 @@ pub use state_machine::ExecutionStrategy; use std::sync::Arc; use std::collections::HashMap; -use futures::future::FutureResult; +use futures::future::Ready; use hash_db::Hasher; use primitives::storage::well_known_keys; use runtime_primitives::traits::{ @@ -220,11 +220,11 @@ impl TestClientBuilder< } impl client::light::fetcher::Fetcher for LightFetcher { - type RemoteHeaderResult = FutureResult; - type RemoteReadResult = FutureResult>, client::error::Error>; - type RemoteCallResult = FutureResult, client::error::Error>; - type RemoteChangesResult = FutureResult, u32)>, client::error::Error>; - type RemoteBodyResult = FutureResult, client::error::Error>; + type RemoteHeaderResult = Ready>; + type RemoteReadResult = Ready>, client::error::Error>>; + type RemoteCallResult = Ready, client::error::Error>>; + type RemoteChangesResult = Ready, u32)>, client::error::Error>>; + type RemoteBodyResult = Ready, client::error::Error>>; fn remote_header( &self, diff --git a/core/test-runtime/Cargo.toml b/core/test-runtime/Cargo.toml index 64725177fc970f3ef06290c0a3247346fd3982de..873c1eb62416a96f72ff3c8933862700c43d674e 100644 --- a/core/test-runtime/Cargo.toml +++ b/core/test-runtime/Cargo.toml @@ -3,11 +3,12 @@ name = "substrate-test-runtime" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +build = "build.rs" [dependencies] log = { version = "0.4", optional = true } serde = { version = "1.0", optional = true, features = ["derive"] } -parity-codec = { version = "3.3", default-features = false, features = ["derive"] } +parity-codec = { version = "4.1.1", default-features = false, features = ["derive"] } keyring = { package = "substrate-keyring", path = "../keyring", optional = true } substrate-client = { path = "../client", default-features = false } primitives = { package = "substrate-primitives", path = "../primitives", default-features = false } @@ -20,21 +21,26 @@ runtime_primitives = { package = "sr-primitives", path = "../sr-primitives", def runtime_version = { package = "sr-version", path = "../sr-version", default-features = false } runtime_support = { package = "srml-support", path = "../../srml/support", default-features = false } substrate-trie = { path = "../trie", default-features = false } -trie-db = { version = "0.12", default-features = false } -memory-db = { version = "0.12", default-features = false } +trie-db = { version = "0.14.0", default-features = false } offchain-primitives = { package = "substrate-offchain-primitives", path = "../offchain/primitives", default-features = false} executive = { package = "srml-executive", path = "../../srml/executive", default-features = false } cfg-if = "0.1.6" +srml-babe = { path = "../../srml/babe", default-features = false } +srml-timestamp = { path = "../../srml/timestamp", default-features = false } +srml-system = { path = "../../srml/system", default-features = false } [dev-dependencies] substrate-executor = { path = "../executor" } substrate-test-runtime-client = { path = "./client" } +[build-dependencies] +wasm-builder-runner = { package = "substrate-wasm-builder-runner", version = "1.0.2", path = "../utils/wasm-builder-runner" } + [features] default = [ "std", - "include-wasm-blob" ] +no_std = [] std = [ "log", "serde", @@ -53,9 +59,9 @@ std = [ "primitives/std", "substrate-trie/std", "trie-db/std", - "memory-db/std", "offchain-primitives/std", "executive/std", + "srml-babe/std", + "srml-timestamp/std", + "srml-system/std", ] -# If enabled, the WASM blob is added to the `GenesisConfig`. -include-wasm-blob = [] diff --git a/node/runtime/wasm/src/lib.rs b/core/test-runtime/build.rs similarity index 70% rename from node/runtime/wasm/src/lib.rs rename to core/test-runtime/build.rs index a87b3f7c79dbd94a34a14537822b8c94fc67fab7..f543f68ccd72fd30254e678804e9a897c5a81b5e 100644 --- a/node/runtime/wasm/src/lib.rs +++ b/core/test-runtime/build.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,8 +14,14 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -//! The Substrate runtime reexported for WebAssembly compile. +use wasm_builder_runner::{build_current_project, WasmBuilderSource}; -#![cfg_attr(not(feature = "std"), no_std)] - -pub use node_runtime::*; +fn main() { + build_current_project( + "wasm_binary.rs", + WasmBuilderSource::CratesOrPath { + path: "../utils/wasm-builder", + version: "1.0.4", + }, + ); +} diff --git a/core/test-runtime/client/Cargo.toml b/core/test-runtime/client/Cargo.toml index 9ddeb7ba2508b627132bdbc2b906f525296f4192..4905678ae9c30953f5f304313a3aba5953012f14 100644 --- a/core/test-runtime/client/Cargo.toml +++ b/core/test-runtime/client/Cargo.toml @@ -13,7 +13,6 @@ runtime_primitives = { package = "sr-primitives", path = "../../sr-primitives" } [features] default = [ "std", - "runtime/include-wasm-blob", ] std = [ "runtime/std", diff --git a/core/test-runtime/client/src/lib.rs b/core/test-runtime/client/src/lib.rs index ee1ad2421472695855215810fc717692afa286f8..104ffac820c50389b8cd08db7ff00b2e032f9651 100644 --- a/core/test-runtime/client/src/lib.rs +++ b/core/test-runtime/client/src/lib.rs @@ -51,7 +51,7 @@ mod local_executor { pub LocalExecutor, runtime::api::dispatch, runtime::native_version, - include_bytes!("../../wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm") + runtime::WASM_BINARY ); } diff --git a/core/test-runtime/src/genesismap.rs b/core/test-runtime/src/genesismap.rs index f14ddf6989e6701e280607b85662a632f5379ef2..21d7aae0a1a064a0ea5cf4b135ecf413f645e815 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}; +use super::{AuthorityId, AccountId, WASM_BINARY}; use parity_codec::{Encode, KeyedVec, Joiner}; use primitives::{ChangesTrieConfiguration, map, storage::well_known_keys}; use runtime_primitives::traits::Block; @@ -48,13 +48,11 @@ impl GenesisConfig { } pub fn genesis_map(&self) -> HashMap, Vec> { - #[cfg(feature = "include-wasm-blob")] - let wasm_runtime = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm").to_vec(); + let wasm_runtime = WASM_BINARY.to_vec(); let mut map: HashMap, Vec> = self.balances.iter() .map(|&(ref account, balance)| (account.to_keyed_vec(b"balance:"), vec![].and(&balance))) .map(|(k, v)| (blake2_256(&k[..])[..].to_vec(), v.to_vec())) .chain(vec![ - #[cfg(feature = "include-wasm-blob")] (well_known_keys::CODE.into(), wasm_runtime), (well_known_keys::HEAP_PAGES.into(), vec![].and(&(16 as u64))), ].into_iter()) diff --git a/core/test-runtime/src/lib.rs b/core/test-runtime/src/lib.rs index 0916cad93d02f5de78b5ed43679d1b39ee512f8a..13b8a9eac86c6008d6ea5f5e4362a8b9db3caba3 100644 --- a/core/test-runtime/src/lib.rs +++ b/core/test-runtime/src/lib.rs @@ -36,10 +36,10 @@ use substrate_client::{ use runtime_primitives::{ ApplyResult, create_runtime_str, - transaction_validity::TransactionValidity, + transaction_validity::{TransactionValidity, ValidTransaction}, traits::{ BlindCheckable, BlakeTwo256, Block as BlockT, Extrinsic as ExtrinsicT, - GetNodeBlockType, GetRuntimeBlockType, Verify + GetNodeBlockType, GetRuntimeBlockType, Verify, IdentityLookup }, }; use runtime_version::RuntimeVersion; @@ -47,15 +47,19 @@ pub use primitives::hash::H256; use primitives::{sr25519, OpaqueMetadata}; #[cfg(any(feature = "std", test))] use runtime_version::NativeVersion; +use runtime_support::{impl_outer_origin, parameter_types}; use inherents::{CheckInherentsResult, InherentData}; use cfg_if::cfg_if; pub use consensus_babe::AuthorityId; - // Ensure Babe and Aura use the same crypto to simplify things a bit. pub type AuraId = AuthorityId; // Ensure Babe and Aura use the same crypto to simplify things a bit. pub type BabeId = AuthorityId; +// Inlucde the WASM binary +#[cfg(feature = "std")] +include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); + /// Test runtime version. pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("test"), @@ -136,6 +140,8 @@ impl BlindCheckable for Extrinsic { } impl ExtrinsicT for Extrinsic { + type Call = (); + fn is_signed(&self) -> Option { if let Extrinsic::IncludeData(_) = *self { Some(false) @@ -143,6 +149,10 @@ impl ExtrinsicT for Extrinsic { Some(true) } } + + fn new_unsigned(_call: Self::Call) -> Option { + None + } } impl Extrinsic { @@ -291,6 +301,7 @@ cfg_if! { } } +#[derive(Clone, Eq, PartialEq)] pub struct Runtime; impl GetNodeBlockType for Runtime { @@ -301,6 +312,50 @@ impl GetRuntimeBlockType for Runtime { type RuntimeBlock = Block; } +impl_outer_origin!{ + pub enum Origin for Runtime where system = srml_system {} +} + +#[derive(Clone, Encode, Decode, Eq, PartialEq)] +#[cfg_attr(feature = "std", derive(Debug))] +pub struct Event; + +impl From for Event { + fn from(_evt: srml_system::Event) -> Self { + unimplemented!("Not required in tests!") + } +} + +parameter_types! { + pub const BlockHashCount: BlockNumber = 250; + pub const MinimumPeriod: u64 = 5; + pub const MaximumBlockWeight: u32 = 4 * 1024 * 1024; + pub const MaximumBlockLength: u32 = 4 * 1024 * 1024; +} + +impl srml_system::Trait for Runtime { + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type Event = Event; + type WeightMultiplierUpdate = (); + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; +} + +impl srml_timestamp::Trait for Runtime { + /// A timestamp: seconds since the unix epoch. + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; +} + /// Adds one to the given input and returns the final result. #[inline(never)] fn benchmark_add_one(i: u64) -> u64 { @@ -367,13 +422,13 @@ cfg_if! { impl client_api::TaggedTransactionQueue for Runtime { fn validate_transaction(utx: ::Extrinsic) -> TransactionValidity { if let Extrinsic::IncludeData(data) = utx { - return TransactionValidity::Valid { + return TransactionValidity::Valid(ValidTransaction { priority: data.len() as u64, requires: vec![], provides: vec![data], longevity: 1, propagate: false, - }; + }); } system::validate_transaction(utx) @@ -467,13 +522,23 @@ cfg_if! { impl consensus_babe::BabeApi for Runtime { fn startup_data() -> consensus_babe::BabeConfiguration { consensus_babe::BabeConfiguration { - slot_duration: 1, + median_required_blocks: 0, + slot_duration: 3, expected_block_time: 1, - threshold: std::u64::MAX, - median_required_blocks: 100, + threshold: core::u64::MAX, + slots_per_epoch: 6, + } + } + fn epoch() -> consensus_babe::Epoch { + let authorities = system::authorities(); + let authorities: Vec<_> = authorities.into_iter().map(|x|(x, 1)).collect(); + consensus_babe::Epoch { + authorities, + randomness: >::randomness(), + epoch_index: 1, + duration: 6, } } - fn authorities() -> Vec { system::authorities() } } impl offchain_primitives::OffchainWorkerApi for Runtime { @@ -508,13 +573,13 @@ cfg_if! { impl client_api::TaggedTransactionQueue for Runtime { fn validate_transaction(utx: ::Extrinsic) -> TransactionValidity { if let Extrinsic::IncludeData(data) = utx { - return TransactionValidity::Valid { + return TransactionValidity::Valid(ValidTransaction{ priority: data.len() as u64, requires: vec![], provides: vec![data], longevity: 1, propagate: false, - }; + }); } system::validate_transaction(utx) @@ -616,9 +681,20 @@ cfg_if! { slot_duration: 1, expected_block_time: 1, threshold: core::u64::MAX, + slots_per_epoch: 6, + } + } + + fn epoch() -> consensus_babe::Epoch { + let authorities = system::authorities(); + let authorities: Vec<_> = authorities.into_iter().map(|x|(x, 1)).collect(); + consensus_babe::Epoch { + authorities, + randomness: >::randomness(), + epoch_index: 1, + duration: 6, } } - fn authorities() -> Vec { system::authorities() } } impl offchain_primitives::OffchainWorkerApi for Runtime { diff --git a/core/test-runtime/src/system.rs b/core/test-runtime/src/system.rs index 267d322e87b136abbd94b882e234dc6e34d4849d..01b032f59925a6ffd690bbee6a048ef10c753004 100644 --- a/core/test-runtime/src/system.rs +++ b/core/test-runtime/src/system.rs @@ -23,7 +23,8 @@ use runtime_support::storage::{self, StorageValue, StorageMap}; use runtime_support::storage_items; use runtime_primitives::traits::{Hash as HashT, BlakeTwo256, Header as _}; use runtime_primitives::generic; -use runtime_primitives::{ApplyError, ApplyOutcome, ApplyResult, transaction_validity::TransactionValidity}; +use runtime_primitives::{ApplyError, ApplyOutcome, ApplyResult}; +use runtime_primitives::transaction_validity::{TransactionValidity, ValidTransaction}; use parity_codec::{KeyedVec, Encode}; use super::{ AccountId, BlockNumber, Extrinsic, Transfer, H256 as Hash, Block, Header, Digest, AuthorityId @@ -110,6 +111,8 @@ fn execute_block_with_state_root_handler( storage::unhashed::kill(well_known_keys::EXTRINSIC_INDEX); }); + let o_new_authorities = ::take(); + if let Mode::Overwrite = mode { header.state_root = storage_root().into(); } else { @@ -124,7 +127,7 @@ fn execute_block_with_state_root_handler( if let Some(storage_changes_root) = storage_changes_root(header.parent_hash.into()) { digest.push(generic::DigestItem::ChangesTrieRoot(storage_changes_root.into())); } - if let Some(new_authorities) = ::take() { + if let Some(new_authorities) = o_new_authorities { digest.push(generic::DigestItem::Consensus(*b"aura", new_authorities.encode())); digest.push(generic::DigestItem::Consensus(*b"babe", new_authorities.encode())); } @@ -173,13 +176,13 @@ pub fn validate_transaction(utx: Extrinsic) -> TransactionValidity { p }; - TransactionValidity::Valid { + TransactionValidity::Valid(ValidTransaction { priority: tx.amount, requires, provides, longevity: 64, propagate: true, - } + }) } /// Execute a transaction outside of the block execution function. @@ -203,6 +206,7 @@ pub fn finalize_block() -> Header { let parent_hash = ::take(); let mut digest = ::take().expect("StorageDigest is set by `initialize_block`"); + let o_new_authorities = ::take(); // This MUST come after all changes to storage are done. Otherwise we will fail the // “Storage root does not match that calculated” assertion. let storage_root = BlakeTwo256::storage_root(); @@ -211,7 +215,8 @@ pub fn finalize_block() -> Header { if let Some(storage_changes_root) = storage_changes_root { digest.push(generic::DigestItem::ChangesTrieRoot(storage_changes_root)); } - if let Some(new_authorities) = ::take() { + + if let Some(new_authorities) = o_new_authorities { digest.push(generic::DigestItem::Consensus(*b"aura", new_authorities.encode())); digest.push(generic::DigestItem::Consensus(*b"babe", new_authorities.encode())); } @@ -309,13 +314,10 @@ mod tests { use runtime_io::{with_externalities, TestExternalities}; use substrate_test_runtime_client::{AuthorityKeyring, AccountKeyring}; - use crate::{Header, Transfer}; + use crate::{Header, Transfer, WASM_BINARY}; use primitives::{Blake2Hasher, map}; use substrate_executor::WasmExecutor; - const WASM_CODE: &'static [u8] = - include_bytes!("../wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm"); - fn new_test_ext() -> TestExternalities { let authorities = vec![ AuthorityKeyring::Alice.to_raw_public(), @@ -361,7 +363,7 @@ mod tests { #[test] fn block_import_works_wasm() { block_import_works(|b, ext| { - WasmExecutor::new().call(ext, 8, &WASM_CODE, "Core_execute_block", &b.encode()).unwrap(); + WasmExecutor::new().call(ext, 8, &WASM_BINARY, "Core_execute_block", &b.encode()).unwrap(); }) } @@ -449,7 +451,7 @@ mod tests { #[test] fn block_import_with_transaction_works_wasm() { block_import_with_transaction_works(|b, ext| { - WasmExecutor::new().call(ext, 8, &WASM_CODE, "Core_execute_block", &b.encode()).unwrap(); + WasmExecutor::new().call(ext, 8, &WASM_BINARY, "Core_execute_block", &b.encode()).unwrap(); }) } } diff --git a/core/test-runtime/wasm/Cargo.lock b/core/test-runtime/wasm/Cargo.lock deleted file mode 100644 index 7cba86c8b6d963b7f79d28179ee0a9076a22dbdd..0000000000000000000000000000000000000000 --- a/core/test-runtime/wasm/Cargo.lock +++ /dev/null @@ -1,3740 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "adler32" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "aes-ctr" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ctr 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "stream-cipher 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "aes-soft" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "aesni" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "stream-cipher 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "aho-corasick" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "aio-limited" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "arrayref" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "arrayvec" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "asn1_der" -version = "0.6.1" -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)", -] - -[[package]] -name = "asn1_der_derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "atty" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "autocfg" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "backtrace" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "backtrace-sys" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "base58" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "base64" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bigint" -version = "4.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bitflags" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bitmask" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "blake2" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "blake2-rfc" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "block-buffer" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "block-buffer" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "block-cipher-trait" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "block-padding" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bs58" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "build_const" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bumpalo" -version = "2.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "byte-tools" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "byte-tools" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "byteorder" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "byteorder" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bytes" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "c_linked_list" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "cc" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "cfg-if" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "chrono" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "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)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "clear_on_drop" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "cloudabi" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "constant_time_eq" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "crc" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crc32fast" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-channel" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-deque" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-deque" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-deque" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-queue" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-utils" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-utils" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crunchy" -version = "0.1.6" -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 = "crypto-mac" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crypto-mac" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ctr" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "stream-cipher 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "cuckoofilter" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "curve25519-dalek" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "data-encoding" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "derive_more" -version = "0.14.1" -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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "digest" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "digest" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "dns-parser" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ed25519-dalek" -version = "1.0.0-pre.1" -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.1.4 (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)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "either" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "elastic-array" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "env_logger" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "environmental" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "erased-serde" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "failure" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "backtrace 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "failure_derive" -version = "0.1.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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "fake-simd" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "fixed-hash" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.6 (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 = "flate2" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "miniz-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "miniz_oxide_c_api 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "fnv" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "fuchsia-zircon" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "fuchsia-zircon-sys" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "futures" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "futures-cpupool" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "gcc" -version = "0.3.55" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -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)", - "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "generic-array" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "get_if_addrs" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "c_linked_list 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "get_if_addrs-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "get_if_addrs-sys" -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.55 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hash-db" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "hash256-std-hasher" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hashbrown" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hashmap_core" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "heapsize" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "heck" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hex" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "hex-literal" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "hex-literal-impl 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hex-literal-impl" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro-hack 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hmac" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crypto-mac 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hmac" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hmac-drbg" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", - "hmac 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "http" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "httparse" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "humantime" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "idna" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "impl-codec" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "impl-serde" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "impl-serde" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "serde 1.0.91 (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 = "iovec" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.55 (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" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "itoa" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "js-sys" -version = "0.3.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "wasm-bindgen 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "keccak" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "kernel32-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "kvdb" -version = "0.1.0" -source = "git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7987b8475878fc4d2e1f81d#b0317f649ab2c665b7987b8475878fc4d2e1f81d" -dependencies = [ - "elastic-array 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7987b8475878fc4d2e1f81d)", -] - -[[package]] -name = "lazy_static" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "libc" -version = "0.2.55" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "libp2p" -version = "0.9.1" -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.27 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core-derive 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-deflate 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-dns 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-floodsub 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-identify 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-kad 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-mdns 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-mplex 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-noise 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-ping 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-plaintext 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-ratelimit 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-secio 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-tcp 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-uds 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-wasm-ext 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-websocket 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-yamux 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multihash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (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.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-core" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "asn1_der 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bs58 0.2.2 (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)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libsecp256k1 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "multistream-select 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multihash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rw-stream-sink 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (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)", - "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.1 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-core-derive" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-deflate" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "flate2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-dns" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-dns-unofficial 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-floodsub" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bs58 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "cuckoofilter 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (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)", -] - -[[package]] -name = "libp2p-identify" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (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)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-timer 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-kad" -version = "0.9.0" -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.10 (registry+https://github.com/rust-lang/crates.io-index)", - "bigint 4.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bs58 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multihash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.6.1 (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)", - "smallvec 0.6.9 (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)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-timer 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-mdns" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "data-encoding 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "dns-parser 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-udp 0.1.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.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-mplex" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "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)", -] - -[[package]] -name = "libp2p-noise" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "curve25519-dalek 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", - "snow 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "x25519-dalek 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-ping" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-timer 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-plaintext" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-ratelimit" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aio-limited 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-secio" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aes-ctr 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "asn1_der 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "ctr 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "hmac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-send-wrapper 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rw-stream-sink 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "twofish 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-futures 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", - "web-sys 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-tcp" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (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)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tk-listen 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-uds" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-wasm-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-send-wrapper 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-futures 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-websocket" -version = "0.9.1" -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.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rw-stream-sink 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "soketto 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-rustls 0.10.0-alpha.3 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki-roots 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-yamux" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "yamux 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libsecp256k1" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hmac-drbg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "lock_api" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "lock_api" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "log" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "matches" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "memchr" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "memoffset" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "memory-db" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hashmap_core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "memory_units" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "merlin" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "miniz-sys" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "miniz_oxide" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "miniz_oxide_c_api" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", - "crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "miniz_oxide 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "mio" -version = "0.6.19" -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)", - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -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)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "miow" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "multistream-select" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (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)", -] - -[[package]] -name = "net2" -version = "0.2.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "nodrop" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "nohash-hasher" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "nom" -version = "4.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "version_check 0.1.5 (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.4 (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.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num_cpus" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "numtoa" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "once_cell" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "opaque-debug" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "owning_ref" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "owning_ref" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parity-bytes" -version = "0.1.0" -source = "git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7987b8475878fc4d2e1f81d#b0317f649ab2c665b7987b8475878fc4d2e1f81d" - -[[package]] -name = "parity-codec" -version = "3.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec-derive 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parity-codec-derive" -version = "3.3.0" -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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parity-multiaddr" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "bs58 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "data-encoding 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multihash 0.1.1 (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.91 (registry+https://github.com/rust-lang/crates.io-index)", - "unsigned-varint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parity-multihash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "blake2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "unsigned-varint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parity-send-wrapper" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "parity-wasm" -version = "0.31.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parking_lot" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parking_lot" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parking_lot" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parking_lot" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lock_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.5.0 (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 = "parking_lot_core" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parking_lot_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parking_lot_core" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parking_lot_core" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "paste" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "paste-impl 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "paste-impl" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro-hack 0.5.7 (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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "pbkdf2" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "percent-encoding" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "primitive-types" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "fixed-hash 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "impl-codec 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "impl-serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "uint 0.7.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.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "proc-macro-hack" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro-hack-impl 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "proc-macro-hack" -version = "0.5.7" -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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "proc-macro-hack-impl" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "proc-macro2" -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "protobuf" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "quick-error" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "quick-error" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "quote" -version = "0.6.12" -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 = "rand" -version = "0.3.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_chacha" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_core" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "rand_hc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_isaac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_jitter" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_os" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_pcg" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_xorshift" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rayon" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rayon-core" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "redox_syscall" -version = "0.1.54" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "redox_termios" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "regex" -version = "1.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aho-corasick 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "regex-syntax" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ring" -version = "0.14.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.15" -source = "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 = "rustls" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", - "sct 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rw-stream-sink" -version = "0.1.2" -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.27 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ryu" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "safe-mix" -version = "1.0.0" -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)", -] - -[[package]] -name = "schnorrkel" -version = "0.1.1" -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.1.4 (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)", - "merlin 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.1.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)", - "subtle 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "scopeguard" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "scopeguard" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "sct" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.6.2 (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 = "send_wrapper" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "serde" -version = "1.0.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "serde_derive 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "serde_derive" -version = "1.0.91" -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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "serde_json" -version = "1.0.39" -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 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "sha-1" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "sha1" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "sha2" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-buffer 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "sha2" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "sha3" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "slab" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "slog" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "erased-serde 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "slog-json" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "chrono 0.4.6 (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.91 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", - "slog 2.4.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "slog-scope" -version = "4.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slog 2.4.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "smallvec" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "snow" -version = "0.5.2" -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.1 (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)", - "rand_core 0.4.0 (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)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", - "static_slice 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "soketto" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (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)", -] - -[[package]] -name = "sourcefile" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "spin" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "sr-api-macros" -version = "2.0.0" -dependencies = [ - "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "sr-io" -version = "2.0.0" -dependencies = [ - "environmental 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libsecp256k1 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (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-primitives 2.0.0", - "substrate-state-machine 2.0.0", - "substrate-trie 2.0.0", - "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "sr-primitives" -version = "2.0.0" -dependencies = [ - "integer-sqrt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-io 2.0.0", - "sr-std 2.0.0", - "substrate-primitives 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 = "sr-version" -version = "2.0.0" -dependencies = [ - "impl-serde 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-primitives 2.0.0", - "sr-std 2.0.0", -] - -[[package]] -name = "srml-executive" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (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", -] - -[[package]] -name = "srml-metadata" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-std 2.0.0", - "substrate-primitives 2.0.0", -] - -[[package]] -name = "srml-support" -version = "2.0.0" -dependencies = [ - "bitmask 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "paste 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (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-metadata 2.0.0", - "srml-support-procedural 2.0.0", - "substrate-inherents 2.0.0", -] - -[[package]] -name = "srml-support-procedural" -version = "2.0.0" -dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-api-macros 2.0.0", - "srml-support-procedural-tools 2.0.0", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "srml-support-procedural-tools" -version = "2.0.0" -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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "srml-support-procedural-tools-derive 2.0.0", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "srml-support-procedural-tools-derive" -version = "2.0.0" -dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "srml-system" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.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.91 (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", - "substrate-primitives 2.0.0", -] - -[[package]] -name = "stable_deref_trait" -version = "1.1.1" -source = "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 = "static_slice" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "stream-cipher" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "strum" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "strum_macros" -version = "0.14.0" -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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-bip39" -version = "0.2.1" -source = "git+https://github.com/paritytech/substrate-bip39#44307fda4ea17fe97aeb93af317fbc8f6ed34193" -dependencies = [ - "hmac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "schnorrkel 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-client" -version = "2.0.0" -dependencies = [ - "derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hex-literal 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb 0.1.0 (git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7987b8475878fc4d2e1f81d)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-api-macros 2.0.0", - "sr-primitives 2.0.0", - "sr-std 2.0.0", - "sr-version 2.0.0", - "substrate-consensus-common 2.0.0", - "substrate-executor 2.0.0", - "substrate-inherents 2.0.0", - "substrate-keyring 2.0.0", - "substrate-primitives 2.0.0", - "substrate-state-machine 2.0.0", - "substrate-telemetry 2.0.0", - "substrate-trie 2.0.0", -] - -[[package]] -name = "substrate-consensus-aura-primitives" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.1 (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", -] - -[[package]] -name = "substrate-consensus-babe-primitives" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.1 (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-consensus-slots 2.0.0", - "substrate-primitives 2.0.0", -] - -[[package]] -name = "substrate-consensus-common" -version = "2.0.0" -dependencies = [ - "derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-primitives 2.0.0", - "sr-std 2.0.0", - "sr-version 2.0.0", - "substrate-inherents 2.0.0", - "substrate-primitives 2.0.0", - "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-consensus-slots" -version = "2.0.0" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-primitives 2.0.0", - "substrate-client 2.0.0", - "substrate-consensus-common 2.0.0", - "substrate-inherents 2.0.0", - "substrate-primitives 2.0.0", - "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-executor" -version = "2.0.0" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libsecp256k1 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-io 2.0.0", - "sr-version 2.0.0", - "substrate-panic-handler 2.0.0", - "substrate-primitives 2.0.0", - "substrate-serializer 2.0.0", - "substrate-state-machine 2.0.0", - "substrate-trie 2.0.0", - "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmi 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-inherents" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-primitives 2.0.0", - "sr-std 2.0.0", -] - -[[package]] -name = "substrate-keyring" -version = "2.0.0" -dependencies = [ - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-primitives 2.0.0", - "strum 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "strum_macros 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-primitives 2.0.0", -] - -[[package]] -name = "substrate-offchain-primitives" -version = "2.0.0" -dependencies = [ - "sr-primitives 2.0.0", - "substrate-client 2.0.0", -] - -[[package]] -name = "substrate-panic-handler" -version = "2.0.0" -dependencies = [ - "backtrace 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-primitives" -version = "2.0.0" -dependencies = [ - "base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.1 (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)", - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hash256-std-hasher 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "impl-serde 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "primitive-types 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "schnorrkel 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-std 2.0.0", - "substrate-bip39 0.2.1 (git+https://github.com/paritytech/substrate-bip39)", - "tiny-bip39 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "twox-hash 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmi 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-serializer" -version = "2.0.0" -dependencies = [ - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-state-machine" -version = "2.0.0" -dependencies = [ - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-panic-handler 2.0.0", - "substrate-primitives 2.0.0", - "substrate-trie 2.0.0", - "trie-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-root 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-telemetry" -version = "2.0.0" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "slog 2.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "slog-json 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slog-scope 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-test-runtime" -version = "2.0.0" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "memory-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-io 2.0.0", - "sr-primitives 2.0.0", - "sr-std 2.0.0", - "sr-version 2.0.0", - "srml-executive 2.0.0", - "srml-support 2.0.0", - "substrate-client 2.0.0", - "substrate-consensus-aura-primitives 2.0.0", - "substrate-consensus-babe-primitives 2.0.0", - "substrate-inherents 2.0.0", - "substrate-keyring 2.0.0", - "substrate-offchain-primitives 2.0.0", - "substrate-primitives 2.0.0", - "substrate-trie 2.0.0", - "trie-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-test-runtime-wasm" -version = "2.0.0" -dependencies = [ - "substrate-test-runtime 2.0.0", -] - -[[package]] -name = "substrate-trie" -version = "2.0.0" -dependencies = [ - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "memory-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-std 2.0.0", - "substrate-primitives 2.0.0", - "trie-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-root 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "subtle" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "subtle" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "syn" -version = "0.15.34" -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.12 (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 = "synstructure" -version = "0.10.2" -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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (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 = "termcolor" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "termion" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "thread_local" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "time" -version = "0.1.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -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)", - "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "hmac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tiny-keccak" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tk-listen" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio" -version = "0.1.20" -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.27 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.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-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-sync 0.1.5 (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.14 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-trace-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-codec" -version = "0.1.1" -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.27 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-current-thread" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-dns-unofficial" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-executor" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-fs" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-io" -version = "0.1.12" -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.27 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-reactor" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (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.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-rustls" -version = "0.10.0-alpha.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)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (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)", -] - -[[package]] -name = "tokio-sync" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-tcp" -version = "0.1.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)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (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.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-threadpool" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-timer" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-trace-core" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-udp" -version = "0.1.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)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-uds" -version = "0.2.5" -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.27 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "toml" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "trie-db" -version = "0.12.2" -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.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hashmap_core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.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 = "trie-root" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "twofish" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "twox-hash" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "typenum" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "ucd-util" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "uint" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.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-bidi" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "unicode-normalization" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "unicode-segmentation" -version = "1.3.0" -source = "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 = "unsigned-varint" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "untrusted" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "url" -version = "1.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "utf8-ranges" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "version_check" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "wasm-bindgen" -version = "0.2.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "wasm-bindgen-macro 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bumpalo 2.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-shared 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.3.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-macro-support 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.45" -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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-backend 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-shared 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.45" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "wasm-bindgen-webidl" -version = "0.2.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "failure 0.1.5 (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.6 (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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-backend 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "weedle 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "wasm-timer" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "send_wrapper 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "web-sys 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "wasmi" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-wasm 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmi-validation 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "wasmi-validation" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "parity-wasm 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "web-sys" -version = "0.3.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.22 (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.45 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-webidl 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "webpki" -version = "0.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "webpki-roots" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "weedle" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "winapi" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi" -version = "0.3.7" -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-build" -version = "0.1.1" -source = "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-util" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.3.7 (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" - -[[package]] -name = "wincolor" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ws2_32-sys" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "x25519-dalek" -version = "0.5.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.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "yamux" -version = "0.2.1" -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.27 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "nohash-hasher 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "quick-error 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "zeroize" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "zeroize" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "zeroize_derive 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "zeroize_derive" -version = "0.8.0" -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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[metadata] -"checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" -"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 aho-corasick 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e6f484ae0c99fec2e858eb6134949117399f222608d84cadb3f58c1f97c2364c" -"checksum aio-limited 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7f10b352bc3fc08ae24dc5d2d3ddcac153678533986122dc283d747b12071000" -"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" -"checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" -"checksum asn1_der 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9893d63fc3b1c44231e667da6836a33f27d8b6b3bdc82f83da5dfd579d1b6528" -"checksum asn1_der_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e7f92edafad155aff997fa5b727c6429b91e996b5a5d62a2b0adbae1306b5fe" -"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" -"checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf" -"checksum backtrace 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)" = "1a13fc43f04daf08ab4f71e3d27e1fc27fc437d3e95ac0063a796d92fb40f39b" -"checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" -"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 bigint 4.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ebecac13b3c745150d7b6c3ea7572d372f09d627c2077e893bf26c5c7f70d282" -"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" -"checksum bitmask 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5da9b3d9f6f585199287a473f4f8dfab6566cf827d15c00c219f53c645687ead" -"checksum blake2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "91721a6330935673395a0607df4d49a9cb90ae12d259f1b3e0a3f6e1d486872e" -"checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" -"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" -"checksum block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4dc3af3ee2e12f3e5d224e5e1e3d73668abbeb69e566d361f7d5563a4fdf09" -"checksum bs58 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0de79cfb98e7aa9988188784d8664b4b5dad6eaaa0863b91d9a4ed871d4f7a42" -"checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" -"checksum bumpalo 2.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "84dca3afd8e01b9526818b7963e5b4916063b3cdf9f10cf6b73ef0bd0ec37aa5" -"checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" -"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" -"checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" -"checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" -"checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" -"checksum c_linked_list 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4964518bd3b4a8190e832886cdc0da9794f12e8e6c1613a9e90ff331c4c8724b" -"checksum cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "39f75544d7bbaf57560d2168f28fd649ff9c76153874db88bdbdfd839b1a7e7d" -"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" -"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878" -"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 constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e" -"checksum crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" -"checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" -"checksum crossbeam 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad4c7ea749d9fb09e23c5cb17e3b70650860553a0e2744e38446b1803bf7db94" -"checksum crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f0ed1a4de2235cabda8558ff5840bffb97fcb64c97827f354a451307df5f72b" -"checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" -"checksum crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "05e44b8cf3e1a625844d1750e1f7820da46044ff6d28f4d43e455ba3e5bb2c13" -"checksum crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" -"checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150" -"checksum crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "04c9e3102cc2d69cd681412141b390abd55a362afc1540965dad0ad4d34280b4" -"checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" -"checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" -"checksum crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f8306fcef4a7b563b76b7dd949ca48f52bc1141aa067d2ea09565f3e2652aa5c" -"checksum crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" -"checksum crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" -"checksum crypto-mac 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "779015233ac67d65098614aec748ac1c756ab6677fa2e14cf8b37c08dfed1198" -"checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" -"checksum ctr 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "022cd691704491df67d25d006fe8eca083098253c4d43516c2206479c58c6736" -"checksum cuckoofilter 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8dd43f7cfaffe0a386636a10baea2ee05cc50df3b77bea4a456c9572a939bf1f" -"checksum curve25519-dalek 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "750226d75fc2f5a8daec6e7477624e258674023eb73d8d647f63b943ca182a4a" -"checksum data-encoding 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4f47ca1860a761136924ddd2422ba77b2ea54fe8cc75b9040804a0d9d32ad97" -"checksum derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6d944ac6003ed268757ef1ee686753b57efc5fcf0ebe7b64c9fc81e7e32ff839" -"checksum digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e5b29bf156f3f4b3c4f610a25ff69370616ae6e0657d416de22645483e72af0a" -"checksum digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c" -"checksum dns-parser 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c4d33be9473d06f75f58220f71f7a9317aca647dc061dbd3c361b0bef505fbea" -"checksum ed25519-dalek 1.0.0-pre.1 (registry+https://github.com/rust-lang/crates.io-index)" = "81956bcf7ef761fb4e1d88de3fa181358a0d26cbcb9755b587a08f9119824b86" -"checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b" -"checksum elastic-array 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "073be79b6538296faf81c631872676600616073817dd9a440c477ad09b408983" -"checksum env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b61fa891024a945da30a9581546e8cfaf5602c7b3f4c137a2805cf388f92075a" -"checksum environmental 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c7464757b80de8930c91c9afe77ddce501826bf9d134a87db2c67d9dc177e2c" -"checksum erased-serde 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3beee4bc16478a1b26f2e80ad819a52d24745e292f521a63c16eea5f74b7eb60" -"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 fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" -"checksum fixed-hash 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d1a683d1234507e4f3bf2736eeddf0de1dc65996dc0164d57eba0a74bcf29489" -"checksum flate2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f87e68aa82b2de08a6e037f1385455759df6e445a8df5e005b4297191dbf18aa" -"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" -"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" -"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" -"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" -"checksum futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)" = "a2037ec1c6c1c4f79557762eab1f7eae1f64f6cb418ace90fae88f0942b60139" -"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" -"checksum gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)" = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" -"checksum generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c0f28c2f5bfb5960175af447a2da7c18900693738343dc896ffbcabd9839592" -"checksum generic-array 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fceb69994e330afed50c93524be68c42fa898c2d9fd4ee8da03bd7363acd26f2" -"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 hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ba7fb417e5c470acdd61068c79767d0e65962e70836cf6c9dfd2409f06345ce0" -"checksum hash256-std-hasher 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f8b2027c19ec91eb304999abae7307d225cf93be42af53b0039f76e98ed5af86" -"checksum hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3bae29b6653b3412c2e71e9d486db9f9df5d701941d86683005efb9f2d28e3da" -"checksum hashmap_core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8e04cb7a5051270ef3fa79f8c7604d581ecfa73d520e74f554e45541c4b5881a" -"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" -"checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" -"checksum hex-literal 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ddc2928beef125e519d69ae1baa8c37ea2e0d3848545217f6db0179c5eb1d639" -"checksum hex-literal-impl 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "520870c3213943eb8d7803e80180d12a6c7ceb4ae74602544529d1643dc4ddda" -"checksum hmac 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7a13f4163aa0c5ca1be584aace0e2212b2e41be5478218d4f657f5f778b2ae2a" -"checksum hmac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f127a908633569f208325f86f71255d3363c79721d7f9fe31cd5569908819771" -"checksum hmac-drbg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4fe727d41d2eec0a6574d887914347e5ff96a3b87177817e2a9820c5c87fecc2" -"checksum http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "eed324f0f0daf6ec10c474f150505af2c143f251722bf9dbd1261bd1f2ee2c1a" -"checksum httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e8734b0cfd3bc3e101ec59100e101c2eecd19282202e87808b3037b442777a83" -"checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" -"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" -"checksum impl-codec 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d2050d823639fbeae26b2b5ba09aca8907793117324858070ade0673c49f793b" -"checksum impl-serde 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5158079de9d4158e0ce1de3ae0bd7be03904efc40b3d7dd8b8c301cbf6b52b56" -"checksum impl-serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d26be4b97d738552ea423f76c4f681012ff06c3fa36fa968656b3679f60b4a1" -"checksum integer-sqrt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ea155abb3ba6f382a75f1418988c05fe82959ed9ce727de427f9cfd425b0c903" -"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 itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" -"checksum js-sys 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "9987e7c13a91d9cf0efe59cca48a3a7a70e2b11695d5a4640f85ae71e28f5e73" -"checksum keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" -"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -"checksum kvdb 0.1.0 (git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7987b8475878fc4d2e1f81d)" = "" -"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" -"checksum libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)" = "42914d39aad277d9e176efbdad68acb1d5443ab65afe0e0e4f0d49352a950880" -"checksum libp2p 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6abde4e6fc777dc06ae2a15202ddedb1a38d7c71ed16bc10fa704b03f73aec37" -"checksum libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b4ceb4791289534d4c1ad8e4bd3c6f06d3670efa55ce71482951a287df93ddd1" -"checksum libp2p-core-derive 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "851a59dcaab66c96777ae0cace96de88a700243c3b8360ab51c7e093f3727066" -"checksum libp2p-deflate 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "902b44e92e1f8b7e697b3a186d15c841e0e38037f14286513207a5407650a635" -"checksum libp2p-dns 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71a6630a84552b39e5f752e1f6a951d31f3211079465d2e7af73491b6f48fc3f" -"checksum libp2p-floodsub 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9fced4da0c31e0dc8a759472c65fab41db40c01de2d93bc45e1431c13f0564f0" -"checksum libp2p-identify 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1ba5e882d72c71cdf77f45ab68dd715451d3b78a23085f8d385c7a31ec1b4272" -"checksum libp2p-kad 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d71966dbbb4cedcfcdb1d4c87d5dbb6f3f07b465d1ca74f2624256669997d1f2" -"checksum libp2p-mdns 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cdbdaea6f0049cc09ba5db00308f5b93105a8a33b65ba2e36bd35da707850ea2" -"checksum libp2p-mplex 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b351bfd67e97154e7b60f62402237671486c8a89f83eabdb6838f37d4d5f006" -"checksum libp2p-noise 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44324032b2f9260d2b862c741d79d250dc02298dbba56354a992528a826ee2d5" -"checksum libp2p-ping 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1ac43ffd01de4210cf1b969bbb55a008c77f9ec22b74df26a6590bb6bd4c93f" -"checksum libp2p-plaintext 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0506e10770bcbcb59f2a6154ce93c8fd5cb9730b6ceb5aa1463164af1fd0b9c6" -"checksum libp2p-ratelimit 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3886b79a35c0348497bab763517a9a2b4965173f4b4c7438d59f1e4dcf5122ff" -"checksum libp2p-secio 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b811272e5cd86d39bd71fb94687025d9802b13daf0998ebe0d3f2885c636c51a" -"checksum libp2p-tcp 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b2c54cb75f17557de6ce0149aa03e729455e2d240f84d854272bc4b11012a324" -"checksum libp2p-uds 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fbedf4a1e72a5f67523915414e9e12d71d128731873f0f24d8b878398fb47aa4" -"checksum libp2p-wasm-ext 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c1f615b56aa2a6f4ec07bf9667be9fff8877b9c5bd5335601af47490eda341" -"checksum libp2p-websocket 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a0d1bfe60577558f48a9fdf9f35c0ee2dc5baa01f685ff847d3b5cf4f12ee135" -"checksum libp2p-yamux 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bf4bfc7ff127cd622502dbe56f10513dd6776b970e33d8ebb6e367f0752324f6" -"checksum libsecp256k1 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "688e8d65e495567c2c35ea0001b26b9debf0b4ea11f8cccc954233b75fc3428a" -"checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" -"checksum lock_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed946d4529956a20f2d63ebe1b69996d5a2137c91913fe3ebbeff957f5bca7ff" -"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" -"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" -"checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39" -"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" -"checksum memory-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7623b01a4f1b7acb7cf8e3f678f05e15e6ae26cb0b738dfeb5cc186fd6b82ef4" -"checksum memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" -"checksum merlin 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8c39467de91b004f5b9c06fac5bbc8e7d28309a205ee66905166b70804a71fea" -"checksum miniz-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9e3ae51cea1576ceba0dde3d484d30e6e5b86dee0b2d412fe3a16a15c98202" -"checksum miniz_oxide 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c468f2369f07d651a5d0bb2c9079f8488a66d5466efe42d0c5c6466edcb7f71e" -"checksum miniz_oxide_c_api 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b7fe927a42e3807ef71defb191dc87d4e24479b221e67015fe38ae2b7b447bab" -"checksum mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "83f51996a3ed004ef184e16818edc51fadffe8e7ca68be67f9dee67d84d0ff23" -"checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" -"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" -"checksum multistream-select 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f989d40aab0ed0d83c1cdb4856b5790e980b96548d1a921f280e985eb049f38d" -"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" -"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" -"checksum nohash-hasher 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0d138afcce92d219ccb6eb53d9b1e8a96ac0d633cfd3c53cd9856d96d1741bb8" -"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" -"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 num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a23f0ed30a54abaa0c7e83b1d2d87ada7c3c23078d1d87815af3e3b6385fbba" -"checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" -"checksum once_cell 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "532c29a261168a45ce28948f9537ddd7a5dd272cc513b3017b1e82a88f962c37" -"checksum opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "93f5bb2e8e8dec81642920ccff6b61f1eb94fa3020c5a325c9851ff604152409" -"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" -"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)" = "" -"checksum parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dcb43c05fb71c03b4ea7327bf15694da1e0f23f19d5b1e95bab6c6d74097e336" -"checksum parity-codec-derive 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "00a486fd383382ddcb2de928364b1f82571c1e48274fc43b7667a4738ee4056c" -"checksum parity-multiaddr 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "045b3c7af871285146300da35b1932bb6e4639b66c7c98e85d06a32cbc4e8fa7" -"checksum parity-multihash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05d6a68e07ab34a9e87bd8dd4936f6bb5be21e4f6dbcdbaf04d8e854eba0af01" -"checksum parity-send-wrapper 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aa9777aa91b8ad9dd5aaa04a9b6bcb02c7f1deb952fca5a66034d5e63afc5c6f" -"checksum parity-wasm 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)" = "511379a8194230c2395d2f5fa627a5a7e108a9f976656ce723ae68fca4097bfc" -"checksum parking_lot 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d4d05f1349491390b1730afba60bb20d55761bef489a954546b58b4b34e1e2ac" -"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" -"checksum parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa7767817701cce701d5585b9c4db3cdd02086398322c1d7e8bf5094a96a2ce7" -"checksum parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4db1a8ccf734a7bce794cc19b3df06ed87ab2f3907036b693c68f56b4d4537fa" -"checksum parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad7f7e6ebdc79edff6fdcb87a55b620174f7a989e3eb31b65231f4af57f00b8c" -"checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" -"checksum parking_lot_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb88cb1cb3790baa6776844f968fea3be44956cf184fa1be5a03341f5491278c" -"checksum paste 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1f4a4a1c555c6505821f9d58b8779d0f630a6b7e4e1be24ba718610acf01fa79" -"checksum paste-impl 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "26e796e623b8b257215f27e6c80a5478856cae305f5b59810ff9acdaa34570e6" -"checksum pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" -"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" -"checksum primitive-types 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6e8612a8dc70f26276fed6131c153ca277cf275ee0a5e2a50cd8a69c697beb8f" -"checksum proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e10d4b51f154c8a7fb96fd6dad097cb74b863943ec010ac94b9fd1be8861fe1e" -"checksum proc-macro-hack 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2c725b36c99df7af7bf9324e9c999b9e37d92c8f8caf106d82e1d7953218d2d8" -"checksum proc-macro-hack 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0c1dd4172a1e1f96f709341418f49b11ea6c2d95d53dca08c0f74cbd332d9cf3" -"checksum proc-macro-hack-impl 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2b753ad9ed99dd8efeaa7d2fb8453c8f6bc3e54b97966d35f1bc77ca6865254a" -"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -"checksum protobuf 2.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a151c11a92df0059d6ab446fafa3b21a1210aad4bc2293e1c946e8132b10db01" -"checksum quick-error 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5fb6ccf8db7bbcb9c2eae558db5ab4f3da1c2a87e4e597ed394726bc8ea6ca1d" -"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" -"checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db" -"checksum rand 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)" = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" -"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" -"checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" -"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" -"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" -"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -"checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0" -"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" -"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_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 rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "373814f27745b2686b350dd261bfd24576a6fb0e2c5919b3a2b6005f820b0473" -"checksum rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b055d1e92aba6877574d8fe604a63c8b5df60f60e5982bf7ccbb1338ea527356" -"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -"checksum redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)" = "12229c14a0f65c4f1cb046a3b52047cdd9da1f4b30f8a39c5063c8bae515e252" -"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" -"checksum regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8f0a0bcab2fd7d1d7c54fa9eae6f43eddeb9ce2e7352f8518a814a4f65d60c58" -"checksum regex-syntax 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "dcfd8681eebe297b81d98498869d4aae052137651ad7b96822f09ceb690d0a96" -"checksum ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)" = "426bc186e3e95cac1e4a4be125a4aca7e84c2d616ffc02244eef36e2a60a093c" -"checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af" -"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 rw-stream-sink 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9cbe61c20455d3015b2bb7be39e1872310283b8e5a52f5b242b0ac7581fe78" -"checksum ryu 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "b96a9549dc8d48f2c283938303c4b5a77aa29bfbc5b54b084fb1630408899a8f" -"checksum safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7f7bf422d23a88c16d5090d455f182bc99c60af4df6a345c63428acf5129e347" -"checksum schnorrkel 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b5eff518f9bed3d803a0d002af0ab96339b0ebbedde3bec98a684986134b7a39" -"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 sct 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f5adf8fbd58e1b1b52699dc8bed2630faecb6d8c7bee77d009d6bbe4af569b9" -"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum send_wrapper 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a0eddf2e8f50ced781f288c19f18621fa72a3779e3cb58dbf23b07469b0abeb4" -"checksum serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)" = "a72e9b96fa45ce22a4bc23da3858dfccfd60acd28a25bcd328a98fdd6bea43fd" -"checksum serde_derive 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)" = "101b495b109a3e3ca8c4cbe44cf62391527cdfb6ba15821c5ce80bcd5ea23f9f" -"checksum serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "5a23aa71d4a4d43fdbfaac00eff68ba8a06a51759a89ac3304323e800c4dd40d" -"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" -"checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d" -"checksum sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd26bc0e7a2e3a7c959bc494caf58b72ee0c71d67704e9520f736ca7e4853ecf" -"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" -"checksum slog 2.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1e1a2eec401952cd7b12a84ea120e2d57281329940c3f93c2bf04f462539508e" -"checksum slog-json 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ddc0d2aff1f8f325ef660d9a0eb6e6dcd20b30b3f581a5897f58bf42d061c37a" -"checksum slog-scope 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "60c04b4726fa04595ccf2c2dad7bcd15474242c4c5e109a8a376e8a2c9b1539a" -"checksum smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c4488ae950c49d403731982257768f48fada354a5203fe81f9bb6f43ca9002be" -"checksum snow 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5a64f02fd208ef15bd2d1a65861df4707e416151e1272d02c8faafad1c138100" -"checksum soketto 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8cf3ae22c0bce5437c7dce6a2b00e492c19da1feb21ad64a7b6fd7058438c3f2" -"checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3" -"checksum spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44363f6f51401c34e7be73db0db371c04705d35efbe9f7d6082e03a921a32c55" -"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" -"checksum static_assertions 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c19be23126415861cb3a23e501d34a708f7f9b2183c5252d690941c2e69199d5" -"checksum static_slice 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "92a7e0c5e3dfb52e8fbe0e63a1b947bbb17b4036408b151353c4491374931362" -"checksum stream-cipher 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8861bc80f649f5b4c9bd38b696ae9af74499d479dbfb327f0607de6b326a36bc" -"checksum strum 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1810e25f576e7ffce1ff5243b37066da5ded0310b3274c20baaeccb1145b2806" -"checksum strum_macros 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "572a2f4e53dd4c3483fd79e5cc10ddd773a3acb1169bbfe8762365e107110579" -"checksum substrate-bip39 0.2.1 (git+https://github.com/paritytech/substrate-bip39)" = "" -"checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" -"checksum subtle 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "01dca13cf6c3b179864ab3292bd794e757618d35a7766b7c46050c614ba00829" -"checksum syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)" = "a1393e4a97a19c01e900df2aec855a29f71cf02c402e2f443b8d2747c25c5dbe" -"checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" -"checksum termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4096add70612622289f2fdcdbd5086dc81c1e2675e6ae58d6c4f62a16c6d7f2f" -"checksum termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dde0593aeb8d47accea5392b39350015b5eccb12c0d98044d856983d89548dea" -"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" -"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.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e9175261fbdb60781fcd388a4d6cc7e14764a2b629a7ad94abb439aed223a44f" -"checksum tk-listen 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5462b0f968c0457efe38fcd2df7e487096b992419e4f5337b06775a614bbda4b" -"checksum tokio 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "94a1f9396aec29d31bb16c24d155cfa144d1af91c40740125db3131bdaf76da8" -"checksum tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c501eceaf96f0e1793cf26beb63da3d11c738c4a943fdf3746d81d64684c39f" -"checksum tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d16217cad7f1b840c5a97dfb3c43b0c871fef423a6e8d2118c604e843662a443" -"checksum tokio-dns-unofficial 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "82c65483db54eb91b4ef3a9389a3364558590faf30ce473141707c0e16fda975" -"checksum tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "83ea44c6c0773cc034771693711c35c677b4b5a4b21b9e7071704c54de7d555e" -"checksum tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe6dc22b08d6993916647d108a1a7d15b9cd29c4f4496c62b92c45b5041b7af" -"checksum tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5090db468dad16e1a7a54c8c67280c5e4b544f3d3e018f0b913b400261f85926" -"checksum tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6af16bfac7e112bea8b0442542161bfc41cbfa4466b580bdda7d18cb88b911ce" -"checksum tokio-rustls 0.10.0-alpha.3 (registry+https://github.com/rust-lang/crates.io-index)" = "316fdbc899efec48b3b492bd0f339e6d81c4ee96a409257572147ec341943452" -"checksum tokio-sync 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "5b2f843ffdf8d6e1f90bddd48da43f99ab071660cd92b7ec560ef3cdfd7a409a" -"checksum tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1d14b10654be682ac43efee27401d792507e30fd8d26389e1da3b185de2e4119" -"checksum tokio-threadpool 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72558af20be886ea124595ea0f806dd5703b8958e4705429dd58b3d8231f72f2" -"checksum tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "f2106812d500ed25a4f38235b9cae8f78a09edf43203e16e59c3b769a342a60e" -"checksum tokio-trace-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "350c9edade9830dc185ae48ba45667a445ab59f6167ef6d0254ec9d2430d9dd3" -"checksum tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "66268575b80f4a4a710ef83d087fdfeeabdce9b74c797535fbac18a2cb906e92" -"checksum tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "037ffc3ba0e12a0ab4aca92e5234e0dedeb48fddf6ccd260f1f150a36a9f2445" -"checksum toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b8c96d7873fa7ef8bdeb3a9cda3ac48389b4154f32b9803b4bc26220b677b039" -"checksum trie-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1ba73747fd3a64ab531274c04cb588dfa9d30d972d62990831e63fbce2cfec59" -"checksum trie-root 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cfa2e20c4f1418ac2e71ddc418e35e1b56e34022e2146209ffdbf1b2de8b1bd9" -"checksum twofish 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712d261e83e727c8e2dbb75dacac67c36e35db36a958ee504f2164fc052434e1" -"checksum twox-hash 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6c7bcecad121018bdcd6b709fa2325b004878fcb3d3067934ce90749f0faff9a" -"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" -"checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" -"checksum uint 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2143cded94692b156c356508d92888acc824db5bffc0b4089732264c6fcf86d4" -"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" -"checksum unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "141339a08b982d942be2ca06ff8b076563cbe223d1befd5450716790d44e2426" -"checksum unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1967f4cdfc355b37fd76d2a954fb2ed3871034eb4f26d60537d88795cfc332a9" -"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" -"checksum unsigned-varint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2c64cdf40b4a9645534a943668681bcb219faf51874d4b65d2e0abda1b10a2ab" -"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 utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737" -"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" -"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum wasm-bindgen 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "b7ccc7b93cfd13e26700a9e2e41e6305f1951b87e166599069f77d10358100e6" -"checksum wasm-bindgen-backend 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "1953f91b1608eb1522513623c7739f047bb0fed4128ce51a93f08e12cc314645" -"checksum wasm-bindgen-futures 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "fa1af11c73eca3dc8c51c76ea475a4416e912da6402064a49fc6c0214701866d" -"checksum wasm-bindgen-macro 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "0f69da5696545d7ca6607a2e4b1a0edf5a6b36b2c49dbb0f1df6ad1d92884047" -"checksum wasm-bindgen-macro-support 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d4246f3bc73223bbb846f4f2430a60725826a96c9389adf715ed1d5af46dec6" -"checksum wasm-bindgen-shared 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "c08381e07e7a79e5e229ad7c60d15833d19033542cc5dd91d085df59d235f4a6" -"checksum wasm-bindgen-webidl 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "1f42ff7adb8102bf5ad8adbc45b1635c520c8175f9fdf6eb2c54479d485d435a" -"checksum wasm-timer 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad9ac33c834103916e373d648adf65f58c83fb3d8a0f3e6b9a64bca7253a4dca" -"checksum wasmi 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "aebbaef470840d157a5c47c8c49f024da7b1b80e90ff729ca982b2b80447e78b" -"checksum wasmi-validation 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab380192444b3e8522ae79c0a1976e42a82920916ccdfbce3def89f456ea33f3" -"checksum web-sys 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "540b8259eb242ff3a566fa0140bda03a4ece4e5c226e1284b5c95dddcd4341f6" -"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" -"checksum weedle 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc44aa200daee8b1f3a004beaf16554369746f1b4486f0cf93b0caf8a3c2d1e" -"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" -"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" -"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -"checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba" -"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" -"checksum x25519-dalek 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7ee1585dc1484373cbc1cee7aafda26634665cf449436fd6e24bfd1fad230538" -"checksum yamux 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "01bd67889938c48f0049fc60a77341039e6c3eaf16cb7693e6ead7c0ba701295" -"checksum zeroize 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8ddfeb6eee2fb3b262ef6e0898a52b7563bb8e0d5955a313b3cf2f808246ea14" -"checksum zeroize 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b60a6c572b91d8ecb0a460950d84fe5b40699edd07d65f73789b31237afc8f66" -"checksum zeroize_derive 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9dac4b660d969bff9c3fe1847a891cacaa8b21dd5f2aae6e0a3e0975aea96431" diff --git a/core/test-runtime/wasm/Cargo.toml b/core/test-runtime/wasm/Cargo.toml deleted file mode 100644 index a056eab3840740e0ee3f2f07f99214446b23a0f5..0000000000000000000000000000000000000000 --- a/core/test-runtime/wasm/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[package] -name = "substrate-test-runtime-wasm" -version = "2.0.0" -authors = ["Parity Technologies "] -edition = "2018" - -[lib] -name = "substrate_test_runtime" -crate-type = ["cdylib"] - -[dependencies] -substrate-test-runtime = { path = "..", default-features = false } - -[features] -default = [] -std = [ - "substrate-test-runtime/std", -] - -[profile.release] -panic = "abort" -lto = true - -[workspace] -members = [] diff --git a/core/test-runtime/wasm/build.sh b/core/test-runtime/wasm/build.sh deleted file mode 100755 index 059e475c71e78c7c23f2acb6ffcfa7a8e31f95b6..0000000000000000000000000000000000000000 --- a/core/test-runtime/wasm/build.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash -set -e - -if cargo --version | grep -q "nightly"; then - CARGO_CMD="cargo" -else - CARGO_CMD="cargo +nightly" -fi -CARGO_INCREMENTAL=0 RUSTFLAGS="-C link-arg=--export-table" $CARGO_CMD build --target=wasm32-unknown-unknown --release "$@" -for i in substrate_test_runtime -do - wasm-gc "target/wasm32-unknown-unknown/release/$i.wasm" "target/wasm32-unknown-unknown/release/$i.compact.wasm" -done diff --git a/core/transaction-pool/Cargo.toml b/core/transaction-pool/Cargo.toml index 2bcad4d4d6c2e624b0b887e4c3d34ff8cd6199bf..2e4f32197b11e5804736316426ef861a1b524283 100644 --- a/core/transaction-pool/Cargo.toml +++ b/core/transaction-pool/Cargo.toml @@ -8,7 +8,7 @@ edition = "2018" derive_more = "0.14.0" futures = "0.1" log = "0.4" -parity-codec = "3.3" +parity-codec = "4.1.1" parking_lot = "0.8.0" sr-primitives = { path = "../sr-primitives" } client = { package = "substrate-client", path = "../client" } diff --git a/core/transaction-pool/graph/Cargo.toml b/core/transaction-pool/graph/Cargo.toml index 3f918efa4a2ea7c61b5717123f7fdd37e4a85a8e..951a595810d7453a152c180f7bca62d2e43a94e3 100644 --- a/core/transaction-pool/graph/Cargo.toml +++ b/core/transaction-pool/graph/Cargo.toml @@ -16,5 +16,5 @@ sr-primitives = { path = "../../sr-primitives" } [dev-dependencies] assert_matches = "1.3.0" env_logger = "0.6.1" -parity-codec = "3.5.1" +parity-codec = "4.1.1" test_runtime = { package = "substrate-test-runtime", path = "../../test-runtime" } diff --git a/core/transaction-pool/graph/src/pool.rs b/core/transaction-pool/graph/src/pool.rs index 4498598aee9cab96b878e4f5984460c806c8e053..8f76a17ed980944f01d590ff429b4205e5ad61f9 100644 --- a/core/transaction-pool/graph/src/pool.rs +++ b/core/transaction-pool/graph/src/pool.rs @@ -129,18 +129,19 @@ impl Pool { } match self.api.validate_transaction(at, xt.clone())? { - TransactionValidity::Valid { priority, requires, provides, longevity, propagate } => { + TransactionValidity::Valid(validity) => { Ok(base::Transaction { data: xt, - bytes, + bytes + , hash, - priority, - requires, - provides, - propagate, + priority: validity.priority, + requires: validity.requires, + provides: validity.provides, + propagate: validity.propagate, valid_till: block_number .saturated_into::() - .saturating_add(longevity), + .saturating_add(validity.longevity), }) }, TransactionValidity::Invalid(e) => { @@ -233,7 +234,7 @@ impl Pool { for (extrinsic, existing_in_pool) in all { match *existing_in_pool { - // reuse the tags for extrinsis that were found in the pool + // reuse the tags for extrinsics that were found in the pool Some(ref transaction) => { tags.extend(transaction.provides.iter().cloned()); }, @@ -242,8 +243,8 @@ impl Pool { None => { let validity = self.api.validate_transaction(parent, extrinsic.clone()); match validity { - Ok(TransactionValidity::Valid { mut provides, .. }) => { - tags.append(&mut provides); + Ok(TransactionValidity::Valid(mut validity)) => { + tags.append(&mut validity.provides); }, // silently ignore invalid extrinsics, // cause they might just be inherent @@ -306,7 +307,7 @@ impl Pool { let hashes = status.pruned.iter().map(|tx| tx.hash.clone()).collect::>(); let results = self.submit_at(at, status.pruned.into_iter().map(|tx| tx.data.clone()))?; - // Collect the hashes of transactions that now became invalid (meaning that they are succesfully pruned). + // Collect the hashes of transactions that now became invalid (meaning that they are successfully pruned). let hashes = results.into_iter().enumerate().filter_map(|(idx, r)| match r.map_err(error::IntoPoolError::into_pool_error) { Err(Ok(error::Error::InvalidTransaction(_))) => Some(hashes[idx].clone()), _ => None, @@ -451,6 +452,7 @@ fn fire_events( #[cfg(test)] mod tests { use super::*; + use sr_primitives::transaction_validity::ValidTransaction; use futures::Stream; use parity_codec::Encode; use test_runtime::{Block, Extrinsic, Transfer, H256, AccountId}; @@ -486,13 +488,13 @@ mod tests { if nonce < block_number { Ok(TransactionValidity::Invalid(0)) } else { - Ok(TransactionValidity::Valid { + Ok(TransactionValidity::Valid(ValidTransaction { priority: 4, requires: if nonce > block_number { vec![vec![nonce as u8 - 1]] } else { vec![] }, provides: vec![vec![nonce as u8]], longevity: 3, propagate: true, - }) + })) } } diff --git a/core/transaction-pool/graph/src/ready.rs b/core/transaction-pool/graph/src/ready.rs index 3497c1bc4ba72408535c33d43813fc08031c465c..85bb4dd783c42292b07dfb7292bbd709de081960 100644 --- a/core/transaction-pool/graph/src/ready.rs +++ b/core/transaction-pool/graph/src/ready.rs @@ -350,7 +350,7 @@ impl ReadyTransactions { /// we are about to replace is lower than the priority of the replacement transaction. /// We remove/replace old transactions in case they have lower priority. /// - /// In case replacement is succesful returns a list of removed transactions. + /// In case replacement is successful returns a list of removed transactions. fn replace_previous(&mut self, tx: &Transaction) -> error::Result>>> { let mut to_remove = { // check if we are replacing a transaction diff --git a/core/transaction-pool/src/tests.rs b/core/transaction-pool/src/tests.rs index a1ee4a50df332b9fe9d74fd0f4a1e8f3d637bebf..7fb94936d2b627a003c6c4f5cf389dc6c9b340b1 100644 --- a/core/transaction-pool/src/tests.rs +++ b/core/transaction-pool/src/tests.rs @@ -23,7 +23,7 @@ use test_client::{runtime::{AccountId, Block, Hash, Index, Extrinsic, Transfer}, use sr_primitives::{ generic::{self, BlockId}, traits::{Hash as HashT, BlakeTwo256}, - transaction_validity::TransactionValidity, + transaction_validity::{TransactionValidity, ValidTransaction}, }; struct TestApi; @@ -48,13 +48,13 @@ impl txpool::ChainApi for TestApi { }; let provides = vec![vec![uxt.transfer().nonce as u8]]; - Ok(TransactionValidity::Valid { + Ok(TransactionValidity::Valid(ValidTransaction { priority: 1, requires, provides, longevity: 64, propagate: true, - }) + })) } fn block_id_to_number(&self, at: &BlockId) -> error::Result>> { diff --git a/core/trie/Cargo.toml b/core/trie/Cargo.toml index dc245a442bd06962a6da73adb7eb3cadd5d337f1..ef00e52edadc47c4d8bf74c777f7c6215c23371a 100644 --- a/core/trie/Cargo.toml +++ b/core/trie/Cargo.toml @@ -12,18 +12,18 @@ name = "bench" harness = false [dependencies] -codec = { package = "parity-codec", version = "3.2", default-features = false } +codec = { package = "parity-codec", version = "4.1.1", default-features = false } rstd = { package = "sr-std", path = "../sr-std", default-features = false } -hash-db = { version = "0.12.2", default-features = false } -trie-db = { version = "0.12.2", default-features = false } -trie-root = { version = "0.12.2", default-features = false } -memory-db = { version = "0.12.2", default-features = false } +hash-db = { version = "0.14.0", default-features = false } +trie-db = { version = "0.14.0", default-features = false } +trie-root = { version = "0.14.0", default-features = false } +memory-db = { version = "0.14.0", default-features = false } substrate-primitives = { path = "../primitives", default-features = false } [dev-dependencies] -trie-bench = { version = "0.12" } -trie-standardmap = { version = "0.12" } -keccak-hasher = { version = "0.12" } +trie-bench = { version = "0.14.0" } +trie-standardmap = { version = "0.14.0" } +keccak-hasher = { version = "0.14.0" } criterion = "0.2" hex-literal = "0.2.0" diff --git a/core/trie/src/lib.rs b/core/trie/src/lib.rs index ec6f50d6e32cc5ab6bd5a44d6735af5c52cb3aa5..fe45c4aaf1985e08fcd2462f1c0b4836bdee8942 100644 --- a/core/trie/src/lib.rs +++ b/core/trie/src/lib.rs @@ -36,6 +36,8 @@ pub use node_codec::NodeCodec; pub use trie_db::{Trie, TrieMut, DBValue, Recorder, Query}; /// Various re-exports from the `memory-db` crate. pub use memory_db::{KeyFunction, prefixed_key}; +/// Various re-exports from the `hash-db` crate. +pub use hash_db::HashDB as HashDBT; /// As in `trie_db`, but less generic, error type for the crate. pub type TrieError = trie_db::TrieError; diff --git a/core/util/fork-tree/Cargo.toml b/core/utils/fork-tree/Cargo.toml similarity index 68% rename from core/util/fork-tree/Cargo.toml rename to core/utils/fork-tree/Cargo.toml index d65f65d51afbe3bad6a20f8a309a9067fd5e0c55..c41914e07acb408565655a425709c41671a9f68b 100644 --- a/core/util/fork-tree/Cargo.toml +++ b/core/utils/fork-tree/Cargo.toml @@ -5,4 +5,4 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -parity-codec = { version = "3.3", features = ["derive"] } +parity-codec = { version = "4.1.1", features = ["derive"] } diff --git a/core/util/fork-tree/src/lib.rs b/core/utils/fork-tree/src/lib.rs similarity index 84% rename from core/util/fork-tree/src/lib.rs rename to core/utils/fork-tree/src/lib.rs index 7a2e2f422a7fa7d67540c31d09bcc4688a45283c..d202ba6c84068e57436a413c7b8089bffe54e48b 100644 --- a/core/util/fork-tree/src/lib.rs +++ b/core/utils/fork-tree/src/lib.rs @@ -81,6 +81,62 @@ pub struct ForkTree { best_finalized_number: Option, } +impl ForkTree where + H: PartialEq + Clone, + 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( + &mut self, + hash: &H, + number: N, + is_descendent_of: &F + ) -> Result<(), Error> + where E: std::error::Error, + F: Fn(&H, &H) -> Result + { + 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; + } + + // 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; + } + + new_root = Some(node); + break; + } + + if let Some(root) = new_root { + self.roots = vec![root.clone()]; + } + + Ok(()) + } + +} + impl ForkTree where H: PartialEq, N: Ord, @@ -152,6 +208,34 @@ impl ForkTree where self.node_iter().map(|node| (&node.hash, &node.number, &node.data)) } + /// Find a node in the tree that is the lowest ancestor of the given + /// block hash and which passes the given predicate. The given function + /// `is_descendent_of` should return `true` if the second hash (target) + /// is a descendent of the first hash (base). + pub fn find_node_where( + &self, + hash: &H, + number: &N, + is_descendent_of: &F, + predicate: &P, + ) -> Result>, Error> + where E: std::error::Error, + F: Fn(&H, &H) -> Result, + P: Fn(&V) -> bool, + { + // search for node starting from all roots + for root in self.roots.iter() { + let node = root.find_node_where(hash, number, is_descendent_of, predicate)?; + + // found the node, early exit + if node.is_some() { + return Ok(node); + } + } + + Ok(None) + } + /// Finalize a root in the tree and return it, return `None` in case no root /// with the given hash exists. All other roots are pruned, and the children /// of the finalized node become the new roots. @@ -167,7 +251,7 @@ impl ForkTree where } /// Finalize a node in the tree. This method will make sure that the node - /// being finalized is either an existing root (an return its data), or a + /// being finalized is either an existing root (and return its data), or a /// node from a competing branch (not in the tree), tree pruning is done /// accordingly. The given function `is_descendent_of` should return `true` /// if the second hash (target) is a descendent of the first hash (base). @@ -400,6 +484,49 @@ mod node_implementation { Ok(Some((hash, number, data))) } } + + /// Find a node in the tree that is the lowest ancestor of the given + /// block hash and which passes the given predicate. The given function + /// `is_descendent_of` should return `true` if the second hash (target) + /// is a descendent of the first hash (base). + // FIXME: it would be useful if this returned a mutable reference but + // rustc can't deal with lifetimes properly. an option would be to try + // an iterative definition instead. + pub fn find_node_where( + &self, + hash: &H, + number: &N, + is_descendent_of: &F, + predicate: &P, + ) -> Result>, Error> + where E: std::error::Error, + F: Fn(&H, &H) -> Result, + P: Fn(&V) -> bool, + { + // stop searching this branch + if *number < self.number { + return Ok(None); + } + + // continue depth-first search through all children + for node in self.children.iter() { + let node = node.find_node_where(hash, number, is_descendent_of, predicate)?; + + // found node, early exit + if node.is_some() { + return Ok(node); + } + } + + // node not found in any of the descendents, if the node we're + // searching for is a descendent of this node and it passes the + // predicate, then it is this one. + if predicate(&self.data) && is_descendent_of(&self.hash, hash)? { + Ok(Some(self)) + } else { + Ok(None) + } + } } } @@ -870,4 +997,40 @@ mod test { ); } } + + #[test] + fn find_node_works() { + let (tree, is_descendent_of) = test_fork_tree(); + + let node = tree.find_node_where( + &"D", + &4, + &is_descendent_of, + &|_| true, + ).unwrap().unwrap(); + + assert_eq!(node.hash, "C"); + assert_eq!(node.number, 3); + } + + #[test] + fn prune_works() { + let (mut tree, is_descendent_of) = test_fork_tree(); + + tree.prune( + &"C", + 3, + &is_descendent_of, + ).unwrap(); + + assert_eq!( + tree.roots.iter().map(|node| node.hash).collect::>(), + vec!["C"], + ); + + assert_eq!( + tree.iter().map(|(hash, _, _)| *hash).collect::>(), + vec!["C", "D", "E"], + ); + } } diff --git a/core/utils/wasm-builder-runner/Cargo.toml b/core/utils/wasm-builder-runner/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..4046b7e4e2f2551284fabff97501a3731c204edb --- /dev/null +++ b/core/utils/wasm-builder-runner/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "substrate-wasm-builder-runner" +version = "1.0.2" +authors = ["Parity Technologies "] +description = "Runner for substrate-wasm-builder" +edition = "2018" +readme = "README.md" +repository = "https://github.com/paritytech/substrate" +license = "GPL-3.0" + +[dependencies] diff --git a/core/utils/wasm-builder-runner/README.md b/core/utils/wasm-builder-runner/README.md new file mode 100644 index 0000000000000000000000000000000000000000..1b9e2b08ca44405cf307c559b29b743eb8039b10 --- /dev/null +++ b/core/utils/wasm-builder-runner/README.md @@ -0,0 +1,12 @@ +## WASM builder runner + +Since cargo contains many bugs when it comes to correct dependency and feature +resolution, we need this little tool. See for +more information. + +It will create a project that will call `substrate-wasm-builder` to prevent any dependencies +from `substrate-wasm-builder` influencing the main project's dependencies. + +For more information see + +License: GPL-3.0 diff --git a/core/utils/wasm-builder-runner/src/lib.rs b/core/utils/wasm-builder-runner/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..1558dbf22044f1e2f414a7e8c2ba73546c895cc2 --- /dev/null +++ b/core/utils/wasm-builder-runner/src/lib.rs @@ -0,0 +1,241 @@ +// 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 . + +//! # WASM builder runner +//! +//! Since cargo contains many bugs when it comes to correct dependency and feature +//! resolution, we need this little tool. See for +//! more information. +//! +//! It will create a project that will call `substrate-wasm-builder` to prevent any dependencies +//! from `substrate-wasm-builder` influencing the main project's dependencies. +//! +//! For more information see + +use std::{env, process::{Command, self}, fs, path::{PathBuf, Path}}; + +/// Environment variable that tells us to skip building the WASM binary. +const SKIP_BUILD_ENV: &str = "SKIP_WASM_BUILD"; + +/// Environment variable to extend the `RUSTFLAGS` variable given to the WASM build. +const WASM_BUILD_RUSTFLAGS_ENV: &str = "WASM_BUILD_RUSTFLAGS"; + +/// Environment variable that tells us to create a dummy WASM binary. +/// +/// This is useful for `cargo check` to speed-up the compilation. +/// +/// # Caution +/// +/// Enabling this option will just provide `&[]` as WASM binary. +const DUMMY_WASM_BINARY_ENV: &str = "BUILD_DUMMY_WASM_BINARY"; + +/// Environment variable that makes sure the WASM build is triggered. +const TRIGGER_WASM_BUILD_ENV: &str = "TRIGGER_WASM_BUILD"; + +/// Replace all backslashes with slashes. +fn replace_back_slashes(path: T) -> String { + path.to_string().replace("\\", "/") +} + +/// The `wasm-builder` dependency source. +pub enum WasmBuilderSource { + /// The relative path to the source code from the current manifest dir. + Path(&'static str), + /// The git repository that contains the source code. + Git { + repo: &'static str, + rev: &'static str, + }, + /// Use the given version released on crates.io. + Crates(&'static str), + /// Use the given version released on crates.io or from the given path. + CratesOrPath { + version: &'static str, + path: &'static str, + } +} + +impl WasmBuilderSource { + /// Convert to a valid cargo source declaration. + /// + /// `absolute_path` - The manifest dir. + fn to_cargo_source(&self, manifest_dir: &Path) -> String { + match self { + WasmBuilderSource::Path(path) => { + replace_back_slashes(format!("path = \"{}\"", manifest_dir.join(path).display())) + } + WasmBuilderSource::Git { repo, rev } => { + format!("git = \"{}\", rev=\"{}\"", repo, rev) + } + WasmBuilderSource::Crates(version) => { + format!("version = \"{}\"", version) + } + WasmBuilderSource::CratesOrPath { version, path } => { + replace_back_slashes( + format!( + "path = \"{}\", version = \"{}\"", + manifest_dir.join(path).display(), + version + ) + ) + } + } + } +} + +/// Build the currently built project as WASM binary and extend `RUSTFLAGS` with the given rustflags. +/// +/// For more information, see [`build_current_project`]. +pub fn build_current_project_with_rustflags( + file_name: &str, + wasm_builder_source: WasmBuilderSource, + rustflags: &str, +) { + let given_rustflags = env::var(WASM_BUILD_RUSTFLAGS_ENV).unwrap_or_default(); + env::set_var(WASM_BUILD_RUSTFLAGS_ENV, format!("{} {}", given_rustflags, rustflags)); + + build_current_project(file_name, wasm_builder_source) +} + +/// Build the currently built project as WASM binary. +/// +/// The current project is determined using the `CARGO_MANIFEST_DIR` environment variable. +/// +/// `file_name` - The name of the file being generated in the `OUT_DIR`. The file contains the +/// constant `WASM_BINARY` which contains the build wasm binary. +/// `wasm_builder_path` - Path to the wasm-builder project, relative to `CARGO_MANIFEST_DIR`. +pub fn build_current_project(file_name: &str, wasm_builder_source: WasmBuilderSource) { + if check_skip_build() { + // If we skip the build, we still want to make sure to be called when an env variable changes + generate_rerun_if_changed_instructions(); + return; + } + + let manifest_dir = PathBuf::from( + env::var("CARGO_MANIFEST_DIR").expect( + "`CARGO_MANIFEST_DIR` is always set for `build.rs` files; qed" + ) + ); + + let cargo_toml_path = manifest_dir.join("Cargo.toml"); + let out_dir = PathBuf::from(env::var("OUT_DIR").expect("`OUT_DIR` is set by cargo!")); + let file_path = out_dir.join(file_name); + let project_folder = out_dir.join("wasm_build_runner"); + + if check_provide_dummy_wasm_binary() { + provide_dummy_wasm_binary(&file_path); + } else { + create_project(&project_folder, &file_path, &manifest_dir, wasm_builder_source, &cargo_toml_path); + run_project(&project_folder); + } + + // As last step we need to generate our `rerun-if-changed` stuff. If a build fails, we don't + // want to spam the output! + generate_rerun_if_changed_instructions(); +} + +fn create_project( + project_folder: &Path, + file_path: &Path, + manifest_dir: &Path, + wasm_builder_source: WasmBuilderSource, + cargo_toml_path: &Path, +) { + fs::create_dir_all(project_folder.join("src")) + .expect("WASM build runner dir create can not fail; qed"); + + fs::write( + project_folder.join("Cargo.toml"), + format!( + r#" + [package] + name = "wasm-build-runner-impl" + version = "1.0.0" + edition = "2018" + + [dependencies] + substrate-wasm-builder = {{ {wasm_builder_source} }} + + [workspace] + "#, + wasm_builder_source = wasm_builder_source.to_cargo_source(manifest_dir), + ) + ).expect("WASM build runner `Cargo.toml` writing can not fail; qed"); + + fs::write( + project_folder.join("src/main.rs"), + format!( + r#" + fn main() {{ + substrate_wasm_builder::build_project("{file_path}", "{cargo_toml_path}") + }} + "#, + file_path = replace_back_slashes(file_path.display()), + cargo_toml_path = replace_back_slashes(cargo_toml_path.display()), + ) + ).expect("WASM build runner `main.rs` writing can not fail; qed"); +} + +fn run_project(project_folder: &Path) { + let cargo = env::var("CARGO").expect("`CARGO` env variable is always set when executing `build.rs`."); + let mut cmd = Command::new(cargo); + cmd.arg("run").arg(format!("--manifest-path={}", project_folder.join("Cargo.toml").display())); + + if env::var("DEBUG") != Ok(String::from("true")) { + cmd.arg("--release"); + } + + if !cmd.status().map(|s| s.success()).unwrap_or(false) { + // Don't spam the output with backtraces when a build failed! + process::exit(1); + } +} + +/// Generate the name of the skip build environment variable for the current crate. +fn generate_crate_skip_build_env_name() -> String { + format!( + "SKIP_{}_WASM_BUILD", + env::var("CARGO_PKG_NAME").expect("Package name is set").to_uppercase().replace('-', "_"), + ) +} + +/// Checks if the build of the WASM binary should be skipped. +fn check_skip_build() -> bool { + env::var(SKIP_BUILD_ENV).is_ok() || env::var(generate_crate_skip_build_env_name()).is_ok() +} + +/// Check if we should provide a dummy WASM binary. +fn check_provide_dummy_wasm_binary() -> bool { + env::var(DUMMY_WASM_BINARY_ENV).is_ok() +} + +/// Provide the dummy WASM binary +fn provide_dummy_wasm_binary(file_path: &Path) { + fs::write( + file_path, + "pub const WASM_BINARY: &[u8] = &[]; pub const WASM_BINARY_BLOATY: &[u8] = &[];" + ).expect("Writing dummy WASM binary should not fail"); +} + +/// Generate the `rerun-if-changed` instructions for cargo to make sure that the WASM binary is +/// rebuilt when needed. +fn generate_rerun_if_changed_instructions() { + // Make sure that the `build.rs` is called again if one of the following env variables changes. + println!("cargo:rerun-if-env-changed={}", SKIP_BUILD_ENV); + println!("cargo:rerun-if-env-changed={}", DUMMY_WASM_BINARY_ENV); + println!("cargo:rerun-if-env-changed={}", TRIGGER_WASM_BUILD_ENV); + println!("cargo:rerun-if-env-changed={}", generate_crate_skip_build_env_name()); +} diff --git a/core/utils/wasm-builder/Cargo.toml b/core/utils/wasm-builder/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..44288dbcd268f97844230cc9b9f5a701cfcaac9f --- /dev/null +++ b/core/utils/wasm-builder/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "substrate-wasm-builder" +version = "1.0.4" +authors = ["Parity Technologies "] +description = "Utility for building WASM binaries" +edition = "2018" +readme = "README.md" +repository = "https://github.com/paritytech/substrate" +license = "GPL-3.0" + +[dependencies] +build-helper = "0.1.1" +cargo_metadata = "0.8" +tempfile = "3.1.0" +toml = "0.5.1" +walkdir = "2.2.8" +fs2 = "0.4.3" diff --git a/core/utils/wasm-builder/README.md b/core/utils/wasm-builder/README.md new file mode 100644 index 0000000000000000000000000000000000000000..8a8c67d6a8c1d164e10d1d4f68a714d83086f5d9 --- /dev/null +++ b/core/utils/wasm-builder/README.md @@ -0,0 +1,66 @@ +## WASM builder is a utility for building a project as a WASM binary + +The WASM builder is a tool that integrates the process of building the WASM binary of your project into the main +`cargo` build process. + +### Project setup + +A project that should be compiled as a WASM binary needs to: + +1. Add a `build.rs` file. +2. Add `substrate-wasm-builder-runner` as dependency into `build-dependencies`. +3. Add a feature called `no-std`. + +The `build.rs` file needs to contain the following code: + +```rust +use wasm_builder_runner::{build_current_project, WasmBuilderSource}; + +fn main() { + build_current_project( + // The name of the file being generated in out-dir. + "wasm_binary.rs", + // How to include wasm-builder, in this case from crates.io. + WasmBuilderSource::Crates("1.0.0"), + ); +} +``` + +The `no-std` feature will be enabled by WASM builder while compiling your project to WASM. + +As the final step, you need to add the following to your project: + +```rust +include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); +``` + +This will include the generated WASM binary as two constants `WASM_BINARY` and `WASM_BINARY_BLOATY`. +The former is a compact WASM binary and the latter is not compacted. + +### Environment variables + +By using environment variables, you can configure which WASM binaries are built and how: + +- `SKIP_WASM_BUILD` - Skips building any WASM binary. This is useful when only native should be recompiled. +- `BUILD_DUMMY_WASM_BINARY` - Builds dummy WASM binaries. These dummy binaries are empty and useful + for `cargo check` runs. +- `WASM_BUILD_TYPE` - Sets the build type for building WASM binaries. Supported values are `release` or `debug`. + By default the build type is equal to the build type used by the main build. +- `TRIGGER_WASM_BUILD` - Can be set to trigger a WASM build. On subsequent calls the value of the variable + needs to change. As WASM builder instructs `cargo` to watch for file changes + this environment variable should only be required in certain circumstances. +- `WASM_BUILD_RUSTFLAGS` - Extend `RUSTFLAGS` given to `cargo build` while building the WASM binary. + +Each project can be skipped individually by using the environment variable `SKIP_PROJECT_NAME_WASM_BUILD`. +Where `PROJECT_NAME` needs to be replaced by the name of the cargo project, e.g. `node-runtime` will +be `NODE_RUNTIME`. + +### Prerequisites: + +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 new file mode 100644 index 0000000000000000000000000000000000000000..779fa8db095a2a4f381df4cadfe80fc0f8e8e490 --- /dev/null +++ b/core/utils/wasm-builder/src/lib.rs @@ -0,0 +1,168 @@ +// 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 . + +//! # WASM builder is a utility for building a project as a WASM binary +//! +//! The WASM builder is a tool that integrates the process of building the WASM binary of your project into the main +//! `cargo` build process. +//! +//! ## Project setup +//! +//! A project that should be compiled as a WASM binary needs to: +//! +//! 1. Add a `build.rs` file. +//! 2. Add `substrate-wasm-builder-runner` as dependency into `build-dependencies`. +//! 3. Add a feature called `no-std`. +//! +//! The `build.rs` file needs to contain the following code: +//! +//! ```ignore +//! use wasm_builder_runner::{build_current_project, WasmBuilderSource}; +//! +//! fn main() { +//! build_current_project( +//! // The name of the file being generated in out-dir. +//! "wasm_binary.rs", +//! // How to include wasm-builder, in this case from crates.io. +//! WasmBuilderSource::Crates("1.0.0"), +//! ); +//! } +//! ``` +//! +//! The `no-std` feature will be enabled by WASM builder while compiling your project to WASM. +//! +//! As the final step, you need to add the following to your project: +//! +//! ```ignore +//! include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); +//! ``` +//! +//! This will include the generated WASM binary as two constants `WASM_BINARY` and `WASM_BINARY_BLOATY`. +//! The former is a compact WASM binary and the latter is not compacted. +//! +//! ## Environment variables +//! +//! By using environment variables, you can configure which WASM binaries are built and how: +//! +//! - `SKIP_WASM_BUILD` - Skips building any WASM binary. This is useful when only native should be recompiled. +//! - `BUILD_DUMMY_WASM_BINARY` - Builds dummy WASM binaries. These dummy binaries are empty and useful +//! for `cargo check` runs. +//! - `WASM_BUILD_TYPE` - Sets the build type for building WASM binaries. Supported values are `release` or `debug`. +//! By default the build type is equal to the build type used by the main build. +//! - `TRIGGER_WASM_BUILD` - Can be set to trigger a WASM build. On subsequent calls the value of the variable +//! needs to change. As WASM builder instructs `cargo` to watch for file changes +//! this environment variable should only be required in certain circumstances. +//! - `WASM_BUILD_RUSTFLAGS` - Extend `RUSTFLAGS` given to `cargo build` while building the WASM binary. +//! +//! Each project can be skipped individually by using the environment variable `SKIP_PROJECT_NAME_WASM_BUILD`. +//! Where `PROJECT_NAME` needs to be replaced by the name of the cargo project, e.g. `node-runtime` will +//! be `NODE_RUNTIME`. +//! +//! ## Prerequisites: +//! +//! 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}}; + +mod prerequisites; +mod wasm_project; + +/// Environment variable that tells us to skip building the WASM binary. +const SKIP_BUILD_ENV: &str = "SKIP_WASM_BUILD"; + +/// Environment variable to force a certain build type when building the WASM binary. +/// Expects "debug" or "release" as value. +/// +/// By default the WASM binary uses the same build type as the main cargo build. +const WASM_BUILD_TYPE_ENV: &str = "WASM_BUILD_TYPE"; + +/// Environment variable to extend the `RUSTFLAGS` variable given to the WASM build. +const WASM_BUILD_RUSTFLAGS_ENV: &str = "WASM_BUILD_RUSTFLAGS"; + +/// Build the currently built project as WASM binary. +/// +/// The current project is determined by using the `CARGO_MANIFEST_DIR` environment variable. +/// +/// `file_name` - The name + path of the file being generated. The file contains the +/// constant `WASM_BINARY`, which contains the built WASM binary. +/// `cargo_manifest` - The path to the `Cargo.toml` of the project that should be built. +pub fn build_project(file_name: &str, cargo_manifest: &str) { + if check_skip_build() { + return; + } + + let cargo_manifest = PathBuf::from(cargo_manifest); + + if !cargo_manifest.exists() { + panic!("'{}' does not exist!", cargo_manifest.display()); + } + + if !cargo_manifest.ends_with("Cargo.toml") { + panic!("'{}' no valid path to a `Cargo.toml`!", cargo_manifest.display()); + } + + if let Some(err_msg) = prerequisites::check() { + eprintln!("{}", err_msg); + process::exit(1); + } + + let (wasm_binary, bloaty) = wasm_project::create_and_compile(&cargo_manifest); + + create_out_file( + file_name, + format!( + r#" + pub const WASM_BINARY: &[u8] = include_bytes!("{wasm_binary}"); + pub const WASM_BINARY_BLOATY: &[u8] = include_bytes!("{wasm_binary_bloaty}"); + "#, + wasm_binary = wasm_binary.wasm_binary_path(), + wasm_binary_bloaty = bloaty.wasm_binary_bloaty_path(), + ), + ); +} + +/// Checks if the build of the WASM binary should be skipped. +fn check_skip_build() -> bool { + env::var(SKIP_BUILD_ENV).is_ok() +} + +fn create_out_file(file_name: &str, content: String) { + fs::write( + file_name, + content + ).expect("Creating and writing can not fail; qed"); +} + +/// Get a cargo command that compiles with nightly +fn get_nightly_cargo() -> Command { + if Command::new("rustup") + .args(&["run", "nightly", "cargo"]) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .status() + .map(|s| s.success()).unwrap_or(false) + { + let mut cmd = Command::new("rustup"); + cmd.args(&["run", "nightly", "cargo"]); + cmd + } else { + Command::new("cargo") + } +} diff --git a/core/utils/wasm-builder/src/prerequisites.rs b/core/utils/wasm-builder/src/prerequisites.rs new file mode 100644 index 0000000000000000000000000000000000000000..5835d322b1f15dd1878eb05734c4f0331ec179d0 --- /dev/null +++ b/core/utils/wasm-builder/src/prerequisites.rs @@ -0,0 +1,96 @@ +// 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::{process::{Command, Stdio}, fs}; + +use tempfile::tempdir; + +/// Checks that all prerequisites are installed. +/// +/// # 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() { + 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!") + } + + if !check_wasm_toolchain_installed() { + return Some("Rust WASM toolchain not installed, please install it!") + } + + None +} + +fn check_nightly_installed() -> bool { + let version = Command::new("cargo") + .arg("--version") + .output() + .map_err(|_| ()) + .and_then(|o| String::from_utf8(o.stdout).map_err(|_| ())) + .unwrap_or_default(); + + let version2 = Command::new("rustup") + .args(&["run", "nightly", "cargo", "--version"]) + .output() + .map_err(|_| ()) + .and_then(|o| String::from_utf8(o.stdout).map_err(|_| ())) + .unwrap_or_default(); + + version.contains("-nightly") || version2.contains("-nightly") +} + +fn check_wasm_toolchain_installed() -> bool { + let temp = tempdir().expect("Creating temp dir does not fail; qed"); + fs::create_dir_all(temp.path().join("src")).expect("Creating src dir does not fail; qed"); + + let test_file = temp.path().join("src/lib.rs"); + let manifest_path = temp.path().join("Cargo.toml"); + + fs::write(&manifest_path, + r#" + [package] + name = "wasm-test" + version = "1.0.0" + edition = "2018" + + [lib] + name = "wasm_test" + crate-type = ["cdylib"] + + [workspace] + "#, + ).expect("Writing wasm-test manifest does not fail; qed"); + fs::write(&test_file, "pub fn test() {}") + .expect("Writing to the test file does not fail; qed"); + + let manifest_path = manifest_path.display().to_string(); + crate::get_nightly_cargo() + .args(&["build", "--target=wasm32-unknown-unknown", "--manifest-path", &manifest_path]) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .status() + .map(|s| s.success()) + .unwrap_or(false) +} \ No newline at end of file diff --git a/core/utils/wasm-builder/src/wasm_project.rs b/core/utils/wasm-builder/src/wasm_project.rs new file mode 100644 index 0000000000000000000000000000000000000000..0d348a5cf4ed898311d46fbbf338344282f9a36a --- /dev/null +++ b/core/utils/wasm-builder/src/wasm_project.rs @@ -0,0 +1,352 @@ +// 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}, borrow::ToOwned, process::{Command, self}, env}; + +use toml::value::Table; + +use build_helper::rerun_if_changed; + +use cargo_metadata::MetadataCommand; + +use walkdir::WalkDir; + +use fs2::FileExt; + +/// Holds the path to the bloaty WASM binary. +pub struct WasmBinaryBloaty(PathBuf); + +impl WasmBinaryBloaty { + /// Returns the path to the bloaty wasm binary. + pub fn wasm_binary_bloaty_path(&self) -> String { + self.0.display().to_string().replace('\\', "/") + } +} + +/// Holds the path to the WASM binary. +pub struct WasmBinary(PathBuf); + +impl WasmBinary { + /// Returns the path to the wasm binary. + pub fn wasm_binary_path(&self) -> String { + self.0.display().to_string().replace('\\', "/") + } +} + +/// A lock for the WASM workspace. +struct WorkspaceLock(fs::File); + +impl WorkspaceLock { + /// Create a new lock + fn new(wasm_workspace_root: &Path) -> Self { + let lock = fs::OpenOptions::new() + .read(true) + .write(true) + .create(true) + .open(wasm_workspace_root.join("wasm_workspace.lock")) + .expect("Opening the lock file does not fail"); + + lock.lock_exclusive().expect("Locking `wasm_workspace.lock` failed"); + + WorkspaceLock(lock) + } +} + +impl Drop for WorkspaceLock { + fn drop(&mut self) { + let _ = self.0.unlock(); + } +} + +/// Creates the WASM project, compiles the WASM binary and compacts the WASM binary. +/// +/// # Returns +/// The path to the compact WASM binary and the bloaty WASM binary. +pub fn create_and_compile(cargo_manifest: &Path) -> (WasmBinary, WasmBinaryBloaty) { + let wasm_workspace_root = get_wasm_workspace_root(); + let wasm_workspace = wasm_workspace_root.join("wbuild"); + + // Lock the workspace exclusively for us + let _lock = WorkspaceLock::new(&wasm_workspace_root); + + let project = create_project(cargo_manifest, &wasm_workspace); + create_wasm_workspace_project(&wasm_workspace); + + build_project(&project); + let (wasm_binary, bloaty) = compact_wasm_file(&project, cargo_manifest, &wasm_workspace); + + generate_rerun_if_changed_instructions(cargo_manifest, &project, &wasm_workspace); + + (wasm_binary, bloaty) +} + +/// Find the `Cargo.lock` relative to the `OUT_DIR` environment variable. +/// +/// If the `Cargo.lock` cannot be found, we emit a warning and return `None`. +fn find_cargo_lock(cargo_manifest: &Path) -> Option { + fn find_impl(mut path: PathBuf) -> Option { + loop { + if path.join("Cargo.lock").exists() { + return Some(path.join("Cargo.lock")) + } + + if !path.pop() { + return None; + } + } + } + + if let Some(path) = find_impl(build_helper::out_dir()) { + return Some(path); + } + + if let Some(path) = find_impl(cargo_manifest.to_path_buf()) { + return Some(path); + } + + build_helper::warning!( + "Could not find `Cargo.lock` for `{}`, while searching from `{}`.", + cargo_manifest.display(), + build_helper::out_dir().display() + ); + None +} + +/// Extract the crate name from the given `Cargo.toml`. +fn get_crate_name(cargo_manifest: &Path) -> String { + let cargo_toml: Table = toml::from_str( + &fs::read_to_string(cargo_manifest).expect("File exists as checked before; qed") + ).expect("Cargo manifest is a valid toml file; qed"); + + let package = cargo_toml + .get("package") + .and_then(|t| t.as_table()) + .expect("`package` key exists in valid `Cargo.toml`; qed"); + + package.get("name").and_then(|p| p.as_str()).map(ToOwned::to_owned).expect("Package name exists; qed") +} + +/// Returns the name for the wasm binary. +fn get_wasm_binary_name(cargo_manifest: &Path) -> String { + get_crate_name(cargo_manifest).replace('-', "_") +} + +/// Returns the root path of the wasm workspace. +fn get_wasm_workspace_root() -> PathBuf { + let mut out_dir = build_helper::out_dir(); + + loop { + match out_dir.parent() { + Some(parent) if out_dir.ends_with("build") => return parent.to_path_buf(), + _ => if !out_dir.pop() { + break; + } + } + } + + panic!("Could not find target dir in: {}", build_helper::out_dir().display()) +} + +fn create_wasm_workspace_project(wasm_workspace: &Path) { + let members = WalkDir::new(wasm_workspace) + .min_depth(1) + .max_depth(1) + .into_iter() + .filter_map(|p| p.ok()) + .map(|d| d.into_path()) + .filter(|p| p.is_dir() && !p.ends_with("target")) + .filter_map(|p| p.file_name().map(|f| f.to_owned()).and_then(|s| s.into_string().ok())) + .map(|s| format!("\"{}\", ", s)) + .collect::(); + + fs::write( + wasm_workspace.join("Cargo.toml"), + format!( + r#" + [profile.release] + panic = "abort" + lto = true + + [profile.dev] + panic = "abort" + + [workspace] + members = [ {members} ] + "#, + members = members, + ) + ).expect("WASM workspace `Cargo.toml` writing can not fail; qed"); +} + +/// Create the project used to build the wasm binary. +/// +/// # Returns +/// The path to the created project. +fn create_project(cargo_manifest: &Path, wasm_workspace: &Path) -> PathBuf { + let crate_name = get_crate_name(cargo_manifest); + let crate_path = cargo_manifest.parent().expect("Parent path exists; qed"); + let wasm_binary = get_wasm_binary_name(cargo_manifest); + let project_folder = wasm_workspace.join(&crate_name); + + fs::create_dir_all(project_folder.join("src")).expect("Wasm project dir create can not fail; qed"); + + fs::write( + project_folder.join("Cargo.toml"), + format!( + r#" + [package] + name = "{crate_name}-wasm" + version = "1.0.0" + edition = "2018" + + [lib] + name = "{wasm_binary}" + crate-type = ["cdylib"] + + [dependencies] + wasm_project = {{ package = "{crate_name}", path = "{crate_path}", default-features = false, features = [ "no_std" ] }} + "#, + crate_name = crate_name, + crate_path = crate_path.display(), + wasm_binary = wasm_binary, + ) + ).expect("Project `Cargo.toml` writing can not fail; qed"); + + fs::write( + project_folder.join("src/lib.rs"), + "#![no_std] pub use wasm_project::*;", + ).expect("Project `lib.rs` writing can not fail; qed"); + + if let Some(crate_lock_file) = find_cargo_lock(cargo_manifest) { + // Use the `Cargo.lock` of the main project. + fs::copy(crate_lock_file, wasm_workspace.join("Cargo.lock")) + .expect("Copying the `Cargo.lock` can not fail; qed"); + } + + project_folder +} + +/// Returns if the project should be built as a release. +fn is_release_build() -> bool { + if let Ok(var) = env::var(crate::WASM_BUILD_TYPE_ENV) { + match var.as_str() { + "release" => true, + "debug" => false, + var => panic!( + "Unexpected value for `{}` env variable: {}\nOne of the following are expected: `debug` or `release`.", + crate::WASM_BUILD_TYPE_ENV, + var, + ), + } + } else { + !build_helper::debug() + } +} + +/// Build the project to create the WASM binary. +fn build_project(project: &Path) { + let manifest_path = project.join("Cargo.toml"); + let mut build_cmd = crate::get_nightly_cargo(); + + let rustflags = format!( + "-C link-arg=--export-table {}", + env::var(crate::WASM_BUILD_RUSTFLAGS_ENV).unwrap_or_default(), + ); + + build_cmd.args(&["build", "--target=wasm32-unknown-unknown"]) + .arg(format!("--manifest-path={}", manifest_path.display())) + .env("RUSTFLAGS", rustflags) + // We don't want to call ourselves recursively + .env(crate::SKIP_BUILD_ENV, ""); + + if is_release_build() { + build_cmd.arg("--release"); + }; + + println!("Executing build command: {:?}", build_cmd); + + match build_cmd.status().map(|s| s.success()) { + Ok(true) => {}, + // Use `process.exit(1)` to have a clean error output. + _ => process::exit(1), + } +} + +/// Compact the WASM binary using `wasm-gc`. Returns the path to the bloaty WASM binary. +fn compact_wasm_file( + project: &Path, + cargo_manifest: &Path, + wasm_workspace: &Path, +) -> (WasmBinary, WasmBinaryBloaty) { + let target = if is_release_build() { "release" } else { "debug" }; + let wasm_binary = get_wasm_binary_name(cargo_manifest); + let wasm_file = wasm_workspace.join("target/wasm32-unknown-unknown") + .join(target) + .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."); + } + + (WasmBinary(wasm_compact_file), WasmBinaryBloaty(wasm_file)) +} + +/// Generate the `rerun-if-changed` instructions for cargo to make sure that the WASM binary is +/// rebuilt when needed. +fn generate_rerun_if_changed_instructions( + cargo_manifest: &Path, + project_folder: &Path, + wasm_workspace: &Path, +) { + // Rerun `build.rs` if the `Cargo.lock` changes + if let Some(cargo_lock) = find_cargo_lock(cargo_manifest) { + rerun_if_changed(cargo_lock); + } + + let metadata = MetadataCommand::new() + .manifest_path(project_folder.join("Cargo.toml")) + .exec() + .expect("`cargo metadata` can not fail!"); + + // Make sure that if any file/folder of a depedency change, we need to rerun the `build.rs` + metadata.packages.into_iter() + .filter(|package| !package.manifest_path.starts_with(wasm_workspace)) + .for_each(|package| { + let mut manifest_path = package.manifest_path; + if manifest_path.ends_with("Cargo.toml") { + manifest_path.pop(); + } + + rerun_if_changed(&manifest_path); + + WalkDir::new(manifest_path) + .into_iter() + .filter_map(|p| p.ok()) + .for_each(|p| rerun_if_changed(p.path())); + }); + + // Register our env variables + println!("cargo:rerun-if-env-changed={}", crate::SKIP_BUILD_ENV); + println!("cargo:rerun-if-env-changed={}", crate::WASM_BUILD_TYPE_ENV); + println!("cargo:rerun-if-env-changed={}", crate::WASM_BUILD_RUSTFLAGS_ENV); +} diff --git a/node-template/Cargo.toml b/node-template/Cargo.toml index e78bb2d27e4447142823e27a7c90eda5cea80d44..4c85ef38174cad7a05336c8a5d37de1f053dda87 100644 --- a/node-template/Cargo.toml +++ b/node-template/Cargo.toml @@ -17,8 +17,8 @@ log = "0.4" tokio = "0.1" exit-future = "0.1" parking_lot = "0.8.0" -parity-codec = "3.3" -trie-root = "0.12.2" +parity-codec = "4.1.1" +trie-root = "0.14.0" sr-io = { path = "../core/sr-io" } substrate-cli = { path = "../core/cli" } primitives = { package = "substrate-primitives", path = "../core/primitives" } diff --git a/node-template/README.md b/node-template/README.md index 6924fa55762b2b4b0d284d7afd2de771d77c7cc0..497ed84a52bcd33920af114154ed34e17d5cde3d 100644 --- a/node-template/README.md +++ b/node-template/README.md @@ -16,12 +16,6 @@ Install required tools: ./scripts/init.sh ``` -Build the WebAssembly binary: - -```bash -./scripts/build.sh -``` - Build all native code: ```bash diff --git a/node-template/build.rs b/node-template/build.rs index afc39d3b63c5eab92b362d436f3f2b4786088ed0..bab46f579d06830f81a91607841bdd64e03e1eb8 100644 --- a/node-template/build.rs +++ b/node-template/build.rs @@ -1,8 +1,24 @@ +use std::{env, path::PathBuf}; + use vergen::{ConstantsFlags, generate_cargo_keys}; const ERROR_MSG: &str = "Failed to generate metadata files"; fn main() { - generate_cargo_keys(ConstantsFlags::all()).expect(ERROR_MSG); - println!("cargo:rerun-if-changed=.git/HEAD"); + generate_cargo_keys(ConstantsFlags::SHA_SHORT).expect(ERROR_MSG); + + let mut manifest_dir = PathBuf::from( + env::var("CARGO_MANIFEST_DIR").expect("`CARGO_MANIFEST_DIR` is always set by cargo.") + ); + + while manifest_dir.parent().is_some() { + if manifest_dir.join(".git/HEAD").exists() { + println!("cargo:rerun-if-changed={}", manifest_dir.join(".git/HEAD").display()); + return + } + + manifest_dir.pop(); + } + + println!("cargo:warning=Could not find `.git/HEAD` from manifest dir!"); } diff --git a/node-template/runtime/Cargo.toml b/node-template/runtime/Cargo.toml index 90cc85317adb287a0a50c3c9c5722c7f0630efce..1bde2479304a617e960f7567660eb55300e15074 100644 --- a/node-template/runtime/Cargo.toml +++ b/node-template/runtime/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" [dependencies] serde = { version = "1.0", optional = true, features = ["derive"] } safe-mix = { version = "1.0", default-features = false } -parity-codec = { version = "3.3", default-features = false, features = ["derive"] } +parity-codec = { version = "4.1.1", 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 } version = { package = "sr-version", path = "../../core/sr-version", default_features = false } @@ -20,11 +20,14 @@ indices = { package = "srml-indices", path = "../../srml/indices", default_featu 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 } -runtime-primitives = { package = "sr-primitives", path = "../../core/sr-primitives", default_features = false } +sr-primitives = { path = "../../core/sr-primitives", default_features = false } client = { package = "substrate-client", path = "../../core/client", default_features = false } consensus-aura = { package = "substrate-consensus-aura-primitives", path = "../../core/consensus/aura/primitives", 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" } + [features] default = ["std"] std = [ @@ -38,7 +41,7 @@ std = [ "aura/std", "indices/std", "primitives/std", - "runtime-primitives/std", + "sr-primitives/std", "system/std", "timestamp/std", "sudo/std", @@ -48,3 +51,4 @@ std = [ "consensus-aura/std", "offchain-primitives/std", ] +no_std = [] diff --git a/core/test-runtime/wasm/src/lib.rs b/node-template/runtime/build.rs similarity index 76% rename from core/test-runtime/wasm/src/lib.rs rename to node-template/runtime/build.rs index 6620f276d0397fbd44840aef3f1500b2877798cb..ccf58b138f0799ca2cfacc70988c173b096012f1 100644 --- a/core/test-runtime/wasm/src/lib.rs +++ b/node-template/runtime/build.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,8 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -//! The Substrate test runtime reexported for WebAssembly compile. +use wasm_builder_runner::{build_current_project, WasmBuilderSource}; -#![cfg_attr(not(feature = "std"), no_std)] - -pub use substrate_test_runtime::*; +fn main() { + build_current_project("wasm_binary.rs", WasmBuilderSource::Crates("1.0.4")); +} diff --git a/node-template/runtime/src/lib.rs b/node-template/runtime/src/lib.rs index 9b99e7f08f49594952bec0b8f3f0f8754bc1b574..332df0ec0dfaeabb42ce6c97a19d4f432ca115a8 100644 --- a/node-template/runtime/src/lib.rs +++ b/node-template/runtime/src/lib.rs @@ -4,6 +4,10 @@ // `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. #![recursion_limit="256"] +// Make the WASM binary available. +#[cfg(feature = "std")] +include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); + #[cfg(feature = "std")] use serde::{Serialize, Deserialize}; use parity_codec::{Encode, Decode}; @@ -11,9 +15,9 @@ use rstd::prelude::*; #[cfg(feature = "std")] use primitives::bytes; use primitives::{ed25519, sr25519, OpaqueMetadata}; -use runtime_primitives::{ +use sr_primitives::{ ApplyResult, transaction_validity::TransactionValidity, generic, create_runtime_str, - traits::{self, NumberFor, BlakeTwo256, Block as BlockT, StaticLookup, Verify} + traits::{self, NumberFor, BlakeTwo256, Block as BlockT, StaticLookup, Verify}, weights::Weight, }; use client::{ block_builder::api::{CheckInherentsResult, InherentData, self as block_builder_api}, @@ -25,12 +29,11 @@ use version::NativeVersion; // A few exports that help ease life for downstream crates. #[cfg(any(feature = "std", test))] -pub use runtime_primitives::BuildStorage; +pub use sr_primitives::BuildStorage; pub use timestamp::Call as TimestampCall; pub use balances::Call as BalancesCall; -pub use runtime_primitives::{Permill, Perbill}; -pub use timestamp::BlockPeriod; -pub use support::{StorageValue, construct_runtime}; +pub use sr_primitives::{Permill, Perbill}; +pub use support::{StorageValue, construct_runtime, parameter_types}; /// Alias to the signature scheme used for Aura authority signatures. pub type AuraSignature = ed25519::Signature; @@ -53,6 +56,9 @@ pub type BlockNumber = u64; /// Index of an account's extrinsic in the chain. pub type Nonce = u64; +/// Balance type for the node. +pub type Balance = u128; + /// Used for the module template in `./template.rs` mod template; @@ -63,21 +69,8 @@ mod template; pub mod opaque { use super::*; - /// Opaque, encoded, unchecked extrinsic. - #[derive(PartialEq, Eq, Clone, Default, Encode, Decode)] - #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] - pub struct UncheckedExtrinsic(#[cfg_attr(feature = "std", serde(with="bytes"))] pub Vec); - #[cfg(feature = "std")] - impl std::fmt::Debug for UncheckedExtrinsic { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(fmt, "{}", primitives::hexdisplay::HexDisplay::from(&self.0)) - } - } - impl traits::Extrinsic for UncheckedExtrinsic { - fn is_signed(&self) -> Option { - None - } - } + pub use sr_primitives::OpaqueExtrinsic as UncheckedExtrinsic; + /// Opaque block header type. pub type Header = generic::Header; /// Opaque block type. @@ -107,6 +100,12 @@ pub fn native_version() -> NativeVersion { } } +parameter_types! { + pub const BlockHashCount: BlockNumber = 250; + pub const MaximumBlockWeight: Weight = 4 * 1024 * 1024; + pub const MaximumBlockLength: u32 = 4 * 1024 * 1024; +} + impl system::Trait for Runtime { /// The identifier used to distinguish between accounts. type AccountId = AccountId; @@ -124,8 +123,16 @@ 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. + type MaximumBlockWeight = MaximumBlockWeight; + /// Maximum size of all encoded transactions (in bytes) that are allowed in one block. + type MaximumBlockLength = MaximumBlockLength; } impl aura::Trait for Runtime { @@ -145,15 +152,27 @@ impl indices::Trait for Runtime { type Event = Event; } +parameter_types! { + pub const MinimumPeriod: u64 = 5; +} impl timestamp::Trait for Runtime { /// A timestamp: seconds since the unix epoch. type Moment = u64; type OnTimestampSet = Aura; + type MinimumPeriod = MinimumPeriod; +} + +parameter_types! { + pub const ExistentialDeposit: u128 = 500; + pub const TransferFee: u128 = 0; + pub const CreationFee: u128 = 0; + pub const TransactionBaseFee: u128 = 1; + pub const TransactionByteFee: u128 = 0; } impl balances::Trait for Runtime { /// The type for recording an account's balance. - type Balance = u128; + type Balance = Balance; /// What to do if an account's free balance gets zeroed. type OnFreeBalanceZero = (); /// What to do if a new account is created. @@ -164,6 +183,11 @@ impl balances::Trait for Runtime { type TransactionPayment = (); type DustRemoval = (); type TransferPayment = (); + type ExistentialDeposit = ExistentialDeposit; + type TransferFee = TransferFee; + type CreationFee = CreationFee; + type TransactionBaseFee = TransactionBaseFee; + type TransactionByteFee = TransactionByteFee; } impl sudo::Trait for Runtime { @@ -183,8 +207,8 @@ construct_runtime!( NodeBlock = opaque::Block, UncheckedExtrinsic = UncheckedExtrinsic { - System: system::{default, Config}, - Timestamp: timestamp::{Module, Call, Storage, Config, Inherent}, + System: system::{Module, Call, Storage, Config, Event}, + Timestamp: timestamp::{Module, Call, Storage, Inherent}, Aura: aura::{Module, Config, Inherent(Timestamp)}, Indices: indices::{default, Config}, Balances: balances, @@ -204,12 +228,14 @@ pub type Header = generic::Header; pub type Block = generic::Block; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; +/// The SignedExtension to the basic transaction logic. +pub type SignedExtra = (system::CheckNonce, system::CheckWeight, balances::TakeFees); /// Unchecked extrinsic type as expected by this runtime. -pub type UncheckedExtrinsic = generic::UncheckedMortalCompactExtrinsic; +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; /// Extrinsic type that has already been checked. -pub type CheckedExtrinsic = generic::CheckedExtrinsic; +pub type CheckedExtrinsic = generic::CheckedExtrinsic; /// Executive: handles dispatch to the various modules. -pub type Executive = executive::Executive; +pub type Executive = executive::Executive; // Implement our runtime API endpoints. This is just a bunch of proxying. impl_runtime_apis! { diff --git a/node-template/runtime/src/template.rs b/node-template/runtime/src/template.rs index c8b75f43f5f5b5f75b34cad4391cdb68ea5307ee..63b5e27b9674eb3ac374b62eb97f81fc7746931d 100644 --- a/node-template/runtime/src/template.rs +++ b/node-template/runtime/src/template.rs @@ -46,7 +46,7 @@ decl_module! { // TODO: Code to execute when something calls this. // For example: the following line stores the passed in u32 in the storage - >::put(something); + Something::put(something); // here we are raising the Something event Self::deposit_event(RawEvent::SomethingStored(something, who)); @@ -71,12 +71,9 @@ mod tests { use runtime_io::with_externalities; use primitives::{H256, Blake2Hasher}; - use support::{impl_outer_origin, assert_ok}; - use runtime_primitives::{ - BuildStorage, - traits::{BlakeTwo256, IdentityLookup}, - testing::Header, - }; + use support::{impl_outer_origin, assert_ok, parameter_types}; + use sr_primitives::{traits::{BlakeTwo256, IdentityLookup}, testing::Header}; + use sr_primitives::weights::Weight; impl_outer_origin! { pub enum Origin for Test {} @@ -87,6 +84,11 @@ mod tests { // configuration traits of modules we want to use. #[derive(Clone, Eq, PartialEq)] pub struct Test; + parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: Weight = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + } impl system::Trait for Test { type Origin = Origin; type Index = u64; @@ -96,7 +98,11 @@ mod tests { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; + type WeightMultiplierUpdate = (); type Event = (); + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; } impl Trait for Test { type Event = (); @@ -106,7 +112,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 { - system::GenesisConfig::::default().build_storage().unwrap().0.into() + system::GenesisConfig::default().build_storage::().unwrap().0.into() } #[test] diff --git a/node-template/runtime/wasm/Cargo.lock b/node-template/runtime/wasm/Cargo.lock deleted file mode 100644 index df4773de1e64d936bc27f6f5b3749488bc280add..0000000000000000000000000000000000000000 --- a/node-template/runtime/wasm/Cargo.lock +++ /dev/null @@ -1,3816 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "adler32" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "aes-ctr" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ctr 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "stream-cipher 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "aes-soft" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "aesni" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "stream-cipher 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "aho-corasick" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "aio-limited" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "arrayref" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "arrayvec" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "asn1_der" -version = "0.6.1" -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)", -] - -[[package]] -name = "asn1_der_derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "atty" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "autocfg" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "backtrace" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "backtrace-sys" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "base58" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "base64" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bigint" -version = "4.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bitflags" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bitmask" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "blake2" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "blake2-rfc" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "block-buffer" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "block-buffer" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "block-cipher-trait" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "block-padding" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bs58" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "build_const" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bumpalo" -version = "2.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "byte-tools" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "byte-tools" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "byteorder" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "byteorder" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bytes" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "c_linked_list" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "cc" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "cfg-if" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "chrono" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "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)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "clear_on_drop" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "cloudabi" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "constant_time_eq" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "crc" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crc32fast" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-channel" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-deque" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-deque" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-deque" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-queue" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-utils" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-utils" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crunchy" -version = "0.1.6" -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 = "crypto-mac" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crypto-mac" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ctr" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "stream-cipher 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "cuckoofilter" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "curve25519-dalek" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "data-encoding" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "derive_more" -version = "0.14.1" -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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "digest" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "digest" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "dns-parser" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ed25519-dalek" -version = "1.0.0-pre.1" -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.1.4 (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)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "either" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "elastic-array" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "env_logger" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "environmental" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "erased-serde" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "failure" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "backtrace 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "failure_derive" -version = "0.1.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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "fake-simd" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "fixed-hash" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.6 (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 = "flate2" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "miniz-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "miniz_oxide_c_api 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "fnv" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "fuchsia-zircon" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "fuchsia-zircon-sys" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "futures" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "futures-cpupool" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "gcc" -version = "0.3.55" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -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)", - "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "generic-array" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "get_if_addrs" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "c_linked_list 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "get_if_addrs-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "get_if_addrs-sys" -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.55 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hash-db" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "hash256-std-hasher" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hashbrown" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hashmap_core" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "heapsize" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "heck" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hex" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "hex-literal" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "hex-literal-impl 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hex-literal-impl" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro-hack 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hmac" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crypto-mac 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hmac" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hmac-drbg" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", - "hmac 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "http" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "httparse" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "humantime" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "idna" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "impl-codec" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "impl-serde" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "impl-serde" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "serde 1.0.91 (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 = "iovec" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.55 (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" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "itoa" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "js-sys" -version = "0.3.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "wasm-bindgen 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "keccak" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "kernel32-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "kvdb" -version = "0.1.0" -source = "git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7987b8475878fc4d2e1f81d#b0317f649ab2c665b7987b8475878fc4d2e1f81d" -dependencies = [ - "elastic-array 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7987b8475878fc4d2e1f81d)", -] - -[[package]] -name = "lazy_static" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "libc" -version = "0.2.55" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "libp2p" -version = "0.9.1" -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.27 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core-derive 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-deflate 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-dns 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-floodsub 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-identify 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-kad 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-mdns 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-mplex 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-noise 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-ping 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-plaintext 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-ratelimit 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-secio 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-tcp 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-uds 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-wasm-ext 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-websocket 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-yamux 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multihash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (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.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-core" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "asn1_der 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bs58 0.2.2 (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)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libsecp256k1 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "multistream-select 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multihash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rw-stream-sink 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (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)", - "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.1 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-core-derive" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-deflate" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "flate2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-dns" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-dns-unofficial 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-floodsub" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bs58 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "cuckoofilter 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (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)", -] - -[[package]] -name = "libp2p-identify" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (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)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-timer 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-kad" -version = "0.9.0" -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.10 (registry+https://github.com/rust-lang/crates.io-index)", - "bigint 4.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bs58 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multihash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.6.1 (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)", - "smallvec 0.6.9 (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)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-timer 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-mdns" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "data-encoding 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "dns-parser 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-udp 0.1.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.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-mplex" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "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)", -] - -[[package]] -name = "libp2p-noise" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "curve25519-dalek 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", - "snow 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "x25519-dalek 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-ping" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-timer 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-plaintext" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-ratelimit" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aio-limited 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-secio" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aes-ctr 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "asn1_der 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "ctr 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "hmac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-send-wrapper 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rw-stream-sink 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "twofish 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-futures 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", - "web-sys 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-tcp" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (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)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tk-listen 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-uds" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-wasm-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-send-wrapper 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-futures 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-websocket" -version = "0.9.1" -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.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rw-stream-sink 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "soketto 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-rustls 0.10.0-alpha.3 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki-roots 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-yamux" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "yamux 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libsecp256k1" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hmac-drbg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "lock_api" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "lock_api" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "log" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "matches" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "memchr" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "memoffset" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "memory-db" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hashmap_core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "memory_units" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "merlin" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "miniz-sys" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "miniz_oxide" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "miniz_oxide_c_api" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", - "crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "miniz_oxide 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "mio" -version = "0.6.19" -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)", - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -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)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "miow" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "multistream-select" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (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)", -] - -[[package]] -name = "net2" -version = "0.2.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "node-template-runtime" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.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.91 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-io 2.0.0", - "sr-primitives 2.0.0", - "sr-std 2.0.0", - "sr-version 2.0.0", - "srml-aura 2.0.0", - "srml-balances 2.0.0", - "srml-executive 2.0.0", - "srml-indices 2.0.0", - "srml-sudo 2.0.0", - "srml-support 2.0.0", - "srml-system 2.0.0", - "srml-timestamp 2.0.0", - "substrate-client 2.0.0", - "substrate-consensus-aura-primitives 2.0.0", - "substrate-offchain-primitives 2.0.0", - "substrate-primitives 2.0.0", -] - -[[package]] -name = "node-template-runtime-wasm" -version = "2.0.0" -dependencies = [ - "node-template-runtime 2.0.0", -] - -[[package]] -name = "nodrop" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "nohash-hasher" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "nom" -version = "4.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "version_check 0.1.5 (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.4 (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.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num_cpus" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "numtoa" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "once_cell" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "opaque-debug" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "owning_ref" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "owning_ref" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parity-bytes" -version = "0.1.0" -source = "git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7987b8475878fc4d2e1f81d#b0317f649ab2c665b7987b8475878fc4d2e1f81d" - -[[package]] -name = "parity-codec" -version = "3.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec-derive 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parity-codec-derive" -version = "3.3.0" -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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parity-multiaddr" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "bs58 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "data-encoding 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multihash 0.1.1 (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.91 (registry+https://github.com/rust-lang/crates.io-index)", - "unsigned-varint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parity-multihash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "blake2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "unsigned-varint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parity-send-wrapper" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "parity-wasm" -version = "0.31.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parking_lot" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parking_lot" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parking_lot" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parking_lot" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lock_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.5.0 (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 = "parking_lot_core" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parking_lot_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parking_lot_core" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parking_lot_core" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "paste" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "paste-impl 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "paste-impl" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro-hack 0.5.7 (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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "pbkdf2" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "percent-encoding" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "primitive-types" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "fixed-hash 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "impl-codec 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "impl-serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "uint 0.7.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.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "proc-macro-hack" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro-hack-impl 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "proc-macro-hack" -version = "0.5.7" -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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "proc-macro-hack-impl" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "proc-macro2" -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "protobuf" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "quick-error" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "quick-error" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "quote" -version = "0.6.12" -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 = "rand" -version = "0.3.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_chacha" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_core" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "rand_hc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_isaac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_jitter" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_os" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_pcg" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_xorshift" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rayon" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rayon-core" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "redox_syscall" -version = "0.1.54" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "redox_termios" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "regex" -version = "1.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aho-corasick 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "regex-syntax" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ring" -version = "0.14.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.15" -source = "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 = "rustls" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", - "sct 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rw-stream-sink" -version = "0.1.2" -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.27 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ryu" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "safe-mix" -version = "1.0.0" -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)", -] - -[[package]] -name = "schnorrkel" -version = "0.1.1" -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.1.4 (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)", - "merlin 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.1.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)", - "subtle 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "scopeguard" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "scopeguard" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "sct" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.6.2 (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 = "send_wrapper" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "serde" -version = "1.0.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "serde_derive 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "serde_derive" -version = "1.0.91" -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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "serde_json" -version = "1.0.39" -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 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "sha-1" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "sha1" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "sha2" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-buffer 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "sha2" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "sha3" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "slab" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "slog" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "erased-serde 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "slog-json" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "chrono 0.4.6 (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.91 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", - "slog 2.4.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "slog-scope" -version = "4.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slog 2.4.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "smallvec" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "snow" -version = "0.5.2" -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.1 (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)", - "rand_core 0.4.0 (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)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", - "static_slice 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "soketto" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (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)", -] - -[[package]] -name = "sourcefile" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "spin" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "sr-api-macros" -version = "2.0.0" -dependencies = [ - "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "sr-io" -version = "2.0.0" -dependencies = [ - "environmental 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libsecp256k1 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (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-primitives 2.0.0", - "substrate-state-machine 2.0.0", - "substrate-trie 2.0.0", - "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "sr-primitives" -version = "2.0.0" -dependencies = [ - "integer-sqrt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-io 2.0.0", - "sr-std 2.0.0", - "substrate-primitives 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 = "sr-version" -version = "2.0.0" -dependencies = [ - "impl-serde 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-primitives 2.0.0", - "sr-std 2.0.0", -] - -[[package]] -name = "srml-aura" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-primitives 2.0.0", - "sr-std 2.0.0", - "srml-session 2.0.0", - "srml-staking 2.0.0", - "srml-support 2.0.0", - "srml-system 2.0.0", - "srml-timestamp 2.0.0", - "substrate-consensus-aura-primitives 2.0.0", - "substrate-inherents 2.0.0", - "substrate-primitives 2.0.0", -] - -[[package]] -name = "srml-balances" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.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.91 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-primitives 2.0.0", - "sr-std 2.0.0", - "srml-support 2.0.0", - "srml-system 2.0.0", - "substrate-keyring 2.0.0", -] - -[[package]] -name = "srml-executive" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (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", -] - -[[package]] -name = "srml-indices" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.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.91 (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-keyring 2.0.0", - "substrate-primitives 2.0.0", -] - -[[package]] -name = "srml-metadata" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-std 2.0.0", - "substrate-primitives 2.0.0", -] - -[[package]] -name = "srml-session" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.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.91 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-primitives 2.0.0", - "sr-std 2.0.0", - "srml-support 2.0.0", - "srml-system 2.0.0", - "srml-timestamp 2.0.0", -] - -[[package]] -name = "srml-staking" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.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.91 (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-session 2.0.0", - "srml-support 2.0.0", - "srml-system 2.0.0", - "substrate-keyring 2.0.0", -] - -[[package]] -name = "srml-sudo" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (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-support-procedural 2.0.0", - "srml-system 2.0.0", -] - -[[package]] -name = "srml-support" -version = "2.0.0" -dependencies = [ - "bitmask 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "paste 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (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-metadata 2.0.0", - "srml-support-procedural 2.0.0", - "substrate-inherents 2.0.0", -] - -[[package]] -name = "srml-support-procedural" -version = "2.0.0" -dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-api-macros 2.0.0", - "srml-support-procedural-tools 2.0.0", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "srml-support-procedural-tools" -version = "2.0.0" -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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "srml-support-procedural-tools-derive 2.0.0", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "srml-support-procedural-tools-derive" -version = "2.0.0" -dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "srml-system" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.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.91 (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", - "substrate-primitives 2.0.0", -] - -[[package]] -name = "srml-timestamp" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-primitives 2.0.0", - "sr-std 2.0.0", - "srml-support 2.0.0", - "srml-system 2.0.0", - "substrate-inherents 2.0.0", -] - -[[package]] -name = "stable_deref_trait" -version = "1.1.1" -source = "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 = "static_slice" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "stream-cipher" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "strum" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "strum_macros" -version = "0.14.0" -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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-bip39" -version = "0.2.1" -source = "git+https://github.com/paritytech/substrate-bip39#44307fda4ea17fe97aeb93af317fbc8f6ed34193" -dependencies = [ - "hmac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "schnorrkel 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-client" -version = "2.0.0" -dependencies = [ - "derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hex-literal 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb 0.1.0 (git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7987b8475878fc4d2e1f81d)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-api-macros 2.0.0", - "sr-primitives 2.0.0", - "sr-std 2.0.0", - "sr-version 2.0.0", - "substrate-consensus-common 2.0.0", - "substrate-executor 2.0.0", - "substrate-inherents 2.0.0", - "substrate-keyring 2.0.0", - "substrate-primitives 2.0.0", - "substrate-state-machine 2.0.0", - "substrate-telemetry 2.0.0", - "substrate-trie 2.0.0", -] - -[[package]] -name = "substrate-consensus-aura-primitives" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.1 (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", -] - -[[package]] -name = "substrate-consensus-common" -version = "2.0.0" -dependencies = [ - "derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-primitives 2.0.0", - "sr-std 2.0.0", - "sr-version 2.0.0", - "substrate-inherents 2.0.0", - "substrate-primitives 2.0.0", - "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-executor" -version = "2.0.0" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libsecp256k1 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-io 2.0.0", - "sr-version 2.0.0", - "substrate-panic-handler 2.0.0", - "substrate-primitives 2.0.0", - "substrate-serializer 2.0.0", - "substrate-state-machine 2.0.0", - "substrate-trie 2.0.0", - "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmi 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-inherents" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-primitives 2.0.0", - "sr-std 2.0.0", -] - -[[package]] -name = "substrate-keyring" -version = "2.0.0" -dependencies = [ - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-primitives 2.0.0", - "strum 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "strum_macros 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-primitives 2.0.0", -] - -[[package]] -name = "substrate-offchain-primitives" -version = "2.0.0" -dependencies = [ - "sr-primitives 2.0.0", - "substrate-client 2.0.0", -] - -[[package]] -name = "substrate-panic-handler" -version = "2.0.0" -dependencies = [ - "backtrace 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-primitives" -version = "2.0.0" -dependencies = [ - "base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.1 (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)", - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hash256-std-hasher 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "impl-serde 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "primitive-types 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "schnorrkel 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-std 2.0.0", - "substrate-bip39 0.2.1 (git+https://github.com/paritytech/substrate-bip39)", - "tiny-bip39 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "twox-hash 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmi 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-serializer" -version = "2.0.0" -dependencies = [ - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-state-machine" -version = "2.0.0" -dependencies = [ - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-panic-handler 2.0.0", - "substrate-primitives 2.0.0", - "substrate-trie 2.0.0", - "trie-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-root 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-telemetry" -version = "2.0.0" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "slog 2.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "slog-json 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slog-scope 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-trie" -version = "2.0.0" -dependencies = [ - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "memory-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-std 2.0.0", - "substrate-primitives 2.0.0", - "trie-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-root 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "subtle" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "subtle" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "syn" -version = "0.15.34" -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.12 (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 = "synstructure" -version = "0.10.2" -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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (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 = "termcolor" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "termion" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "thread_local" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "time" -version = "0.1.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -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)", - "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "hmac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tiny-keccak" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tk-listen" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio" -version = "0.1.20" -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.27 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.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-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-sync 0.1.5 (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.14 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-trace-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-codec" -version = "0.1.1" -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.27 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-current-thread" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-dns-unofficial" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-executor" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-fs" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-io" -version = "0.1.12" -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.27 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-reactor" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (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.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-rustls" -version = "0.10.0-alpha.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)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (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)", -] - -[[package]] -name = "tokio-sync" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-tcp" -version = "0.1.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)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (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.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-threadpool" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-timer" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-trace-core" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-udp" -version = "0.1.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)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-uds" -version = "0.2.5" -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.27 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "toml" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "trie-db" -version = "0.12.2" -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.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hashmap_core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.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 = "trie-root" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "twofish" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "twox-hash" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "typenum" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "ucd-util" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "uint" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.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-bidi" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "unicode-normalization" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "unicode-segmentation" -version = "1.3.0" -source = "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 = "unsigned-varint" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "untrusted" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "url" -version = "1.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "utf8-ranges" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "version_check" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "wasm-bindgen" -version = "0.2.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "wasm-bindgen-macro 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bumpalo 2.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-shared 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.3.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-macro-support 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.45" -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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-backend 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-shared 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.45" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "wasm-bindgen-webidl" -version = "0.2.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "failure 0.1.5 (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.6 (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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-backend 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "weedle 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "wasm-timer" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "send_wrapper 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "web-sys 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "wasmi" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-wasm 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmi-validation 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "wasmi-validation" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "parity-wasm 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "web-sys" -version = "0.3.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.22 (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.45 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-webidl 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "webpki" -version = "0.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "webpki-roots" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "weedle" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "winapi" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi" -version = "0.3.7" -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-build" -version = "0.1.1" -source = "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-util" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.3.7 (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" - -[[package]] -name = "wincolor" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ws2_32-sys" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "x25519-dalek" -version = "0.5.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.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "yamux" -version = "0.2.1" -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.27 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "nohash-hasher 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "quick-error 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "zeroize" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "zeroize" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "zeroize_derive 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "zeroize_derive" -version = "0.8.0" -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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[metadata] -"checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" -"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 aho-corasick 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e6f484ae0c99fec2e858eb6134949117399f222608d84cadb3f58c1f97c2364c" -"checksum aio-limited 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7f10b352bc3fc08ae24dc5d2d3ddcac153678533986122dc283d747b12071000" -"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" -"checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" -"checksum asn1_der 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9893d63fc3b1c44231e667da6836a33f27d8b6b3bdc82f83da5dfd579d1b6528" -"checksum asn1_der_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e7f92edafad155aff997fa5b727c6429b91e996b5a5d62a2b0adbae1306b5fe" -"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" -"checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf" -"checksum backtrace 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)" = "1a13fc43f04daf08ab4f71e3d27e1fc27fc437d3e95ac0063a796d92fb40f39b" -"checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" -"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 bigint 4.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ebecac13b3c745150d7b6c3ea7572d372f09d627c2077e893bf26c5c7f70d282" -"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" -"checksum bitmask 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5da9b3d9f6f585199287a473f4f8dfab6566cf827d15c00c219f53c645687ead" -"checksum blake2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "91721a6330935673395a0607df4d49a9cb90ae12d259f1b3e0a3f6e1d486872e" -"checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" -"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" -"checksum block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4dc3af3ee2e12f3e5d224e5e1e3d73668abbeb69e566d361f7d5563a4fdf09" -"checksum bs58 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0de79cfb98e7aa9988188784d8664b4b5dad6eaaa0863b91d9a4ed871d4f7a42" -"checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" -"checksum bumpalo 2.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "84dca3afd8e01b9526818b7963e5b4916063b3cdf9f10cf6b73ef0bd0ec37aa5" -"checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" -"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" -"checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" -"checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" -"checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" -"checksum c_linked_list 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4964518bd3b4a8190e832886cdc0da9794f12e8e6c1613a9e90ff331c4c8724b" -"checksum cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "39f75544d7bbaf57560d2168f28fd649ff9c76153874db88bdbdfd839b1a7e7d" -"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" -"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878" -"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 constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e" -"checksum crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" -"checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" -"checksum crossbeam 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad4c7ea749d9fb09e23c5cb17e3b70650860553a0e2744e38446b1803bf7db94" -"checksum crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f0ed1a4de2235cabda8558ff5840bffb97fcb64c97827f354a451307df5f72b" -"checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" -"checksum crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "05e44b8cf3e1a625844d1750e1f7820da46044ff6d28f4d43e455ba3e5bb2c13" -"checksum crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" -"checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150" -"checksum crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "04c9e3102cc2d69cd681412141b390abd55a362afc1540965dad0ad4d34280b4" -"checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" -"checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" -"checksum crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f8306fcef4a7b563b76b7dd949ca48f52bc1141aa067d2ea09565f3e2652aa5c" -"checksum crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" -"checksum crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" -"checksum crypto-mac 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "779015233ac67d65098614aec748ac1c756ab6677fa2e14cf8b37c08dfed1198" -"checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" -"checksum ctr 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "022cd691704491df67d25d006fe8eca083098253c4d43516c2206479c58c6736" -"checksum cuckoofilter 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8dd43f7cfaffe0a386636a10baea2ee05cc50df3b77bea4a456c9572a939bf1f" -"checksum curve25519-dalek 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "750226d75fc2f5a8daec6e7477624e258674023eb73d8d647f63b943ca182a4a" -"checksum data-encoding 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4f47ca1860a761136924ddd2422ba77b2ea54fe8cc75b9040804a0d9d32ad97" -"checksum derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6d944ac6003ed268757ef1ee686753b57efc5fcf0ebe7b64c9fc81e7e32ff839" -"checksum digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e5b29bf156f3f4b3c4f610a25ff69370616ae6e0657d416de22645483e72af0a" -"checksum digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c" -"checksum dns-parser 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c4d33be9473d06f75f58220f71f7a9317aca647dc061dbd3c361b0bef505fbea" -"checksum ed25519-dalek 1.0.0-pre.1 (registry+https://github.com/rust-lang/crates.io-index)" = "81956bcf7ef761fb4e1d88de3fa181358a0d26cbcb9755b587a08f9119824b86" -"checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b" -"checksum elastic-array 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "073be79b6538296faf81c631872676600616073817dd9a440c477ad09b408983" -"checksum env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b61fa891024a945da30a9581546e8cfaf5602c7b3f4c137a2805cf388f92075a" -"checksum environmental 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c7464757b80de8930c91c9afe77ddce501826bf9d134a87db2c67d9dc177e2c" -"checksum erased-serde 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3beee4bc16478a1b26f2e80ad819a52d24745e292f521a63c16eea5f74b7eb60" -"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 fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" -"checksum fixed-hash 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d1a683d1234507e4f3bf2736eeddf0de1dc65996dc0164d57eba0a74bcf29489" -"checksum flate2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f87e68aa82b2de08a6e037f1385455759df6e445a8df5e005b4297191dbf18aa" -"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" -"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" -"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" -"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" -"checksum futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)" = "a2037ec1c6c1c4f79557762eab1f7eae1f64f6cb418ace90fae88f0942b60139" -"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" -"checksum gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)" = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" -"checksum generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c0f28c2f5bfb5960175af447a2da7c18900693738343dc896ffbcabd9839592" -"checksum generic-array 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fceb69994e330afed50c93524be68c42fa898c2d9fd4ee8da03bd7363acd26f2" -"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 hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ba7fb417e5c470acdd61068c79767d0e65962e70836cf6c9dfd2409f06345ce0" -"checksum hash256-std-hasher 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f8b2027c19ec91eb304999abae7307d225cf93be42af53b0039f76e98ed5af86" -"checksum hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3bae29b6653b3412c2e71e9d486db9f9df5d701941d86683005efb9f2d28e3da" -"checksum hashmap_core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8e04cb7a5051270ef3fa79f8c7604d581ecfa73d520e74f554e45541c4b5881a" -"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" -"checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" -"checksum hex-literal 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ddc2928beef125e519d69ae1baa8c37ea2e0d3848545217f6db0179c5eb1d639" -"checksum hex-literal-impl 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "520870c3213943eb8d7803e80180d12a6c7ceb4ae74602544529d1643dc4ddda" -"checksum hmac 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7a13f4163aa0c5ca1be584aace0e2212b2e41be5478218d4f657f5f778b2ae2a" -"checksum hmac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f127a908633569f208325f86f71255d3363c79721d7f9fe31cd5569908819771" -"checksum hmac-drbg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4fe727d41d2eec0a6574d887914347e5ff96a3b87177817e2a9820c5c87fecc2" -"checksum http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "eed324f0f0daf6ec10c474f150505af2c143f251722bf9dbd1261bd1f2ee2c1a" -"checksum httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e8734b0cfd3bc3e101ec59100e101c2eecd19282202e87808b3037b442777a83" -"checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" -"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" -"checksum impl-codec 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d2050d823639fbeae26b2b5ba09aca8907793117324858070ade0673c49f793b" -"checksum impl-serde 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5158079de9d4158e0ce1de3ae0bd7be03904efc40b3d7dd8b8c301cbf6b52b56" -"checksum impl-serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d26be4b97d738552ea423f76c4f681012ff06c3fa36fa968656b3679f60b4a1" -"checksum integer-sqrt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ea155abb3ba6f382a75f1418988c05fe82959ed9ce727de427f9cfd425b0c903" -"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 itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" -"checksum js-sys 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "9987e7c13a91d9cf0efe59cca48a3a7a70e2b11695d5a4640f85ae71e28f5e73" -"checksum keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" -"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -"checksum kvdb 0.1.0 (git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7987b8475878fc4d2e1f81d)" = "" -"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" -"checksum libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)" = "42914d39aad277d9e176efbdad68acb1d5443ab65afe0e0e4f0d49352a950880" -"checksum libp2p 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6abde4e6fc777dc06ae2a15202ddedb1a38d7c71ed16bc10fa704b03f73aec37" -"checksum libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b4ceb4791289534d4c1ad8e4bd3c6f06d3670efa55ce71482951a287df93ddd1" -"checksum libp2p-core-derive 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "851a59dcaab66c96777ae0cace96de88a700243c3b8360ab51c7e093f3727066" -"checksum libp2p-deflate 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "902b44e92e1f8b7e697b3a186d15c841e0e38037f14286513207a5407650a635" -"checksum libp2p-dns 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71a6630a84552b39e5f752e1f6a951d31f3211079465d2e7af73491b6f48fc3f" -"checksum libp2p-floodsub 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9fced4da0c31e0dc8a759472c65fab41db40c01de2d93bc45e1431c13f0564f0" -"checksum libp2p-identify 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1ba5e882d72c71cdf77f45ab68dd715451d3b78a23085f8d385c7a31ec1b4272" -"checksum libp2p-kad 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d71966dbbb4cedcfcdb1d4c87d5dbb6f3f07b465d1ca74f2624256669997d1f2" -"checksum libp2p-mdns 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cdbdaea6f0049cc09ba5db00308f5b93105a8a33b65ba2e36bd35da707850ea2" -"checksum libp2p-mplex 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b351bfd67e97154e7b60f62402237671486c8a89f83eabdb6838f37d4d5f006" -"checksum libp2p-noise 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44324032b2f9260d2b862c741d79d250dc02298dbba56354a992528a826ee2d5" -"checksum libp2p-ping 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1ac43ffd01de4210cf1b969bbb55a008c77f9ec22b74df26a6590bb6bd4c93f" -"checksum libp2p-plaintext 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0506e10770bcbcb59f2a6154ce93c8fd5cb9730b6ceb5aa1463164af1fd0b9c6" -"checksum libp2p-ratelimit 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3886b79a35c0348497bab763517a9a2b4965173f4b4c7438d59f1e4dcf5122ff" -"checksum libp2p-secio 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b811272e5cd86d39bd71fb94687025d9802b13daf0998ebe0d3f2885c636c51a" -"checksum libp2p-tcp 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b2c54cb75f17557de6ce0149aa03e729455e2d240f84d854272bc4b11012a324" -"checksum libp2p-uds 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fbedf4a1e72a5f67523915414e9e12d71d128731873f0f24d8b878398fb47aa4" -"checksum libp2p-wasm-ext 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c1f615b56aa2a6f4ec07bf9667be9fff8877b9c5bd5335601af47490eda341" -"checksum libp2p-websocket 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a0d1bfe60577558f48a9fdf9f35c0ee2dc5baa01f685ff847d3b5cf4f12ee135" -"checksum libp2p-yamux 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bf4bfc7ff127cd622502dbe56f10513dd6776b970e33d8ebb6e367f0752324f6" -"checksum libsecp256k1 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "688e8d65e495567c2c35ea0001b26b9debf0b4ea11f8cccc954233b75fc3428a" -"checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" -"checksum lock_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed946d4529956a20f2d63ebe1b69996d5a2137c91913fe3ebbeff957f5bca7ff" -"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" -"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" -"checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39" -"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" -"checksum memory-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7623b01a4f1b7acb7cf8e3f678f05e15e6ae26cb0b738dfeb5cc186fd6b82ef4" -"checksum memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" -"checksum merlin 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8c39467de91b004f5b9c06fac5bbc8e7d28309a205ee66905166b70804a71fea" -"checksum miniz-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9e3ae51cea1576ceba0dde3d484d30e6e5b86dee0b2d412fe3a16a15c98202" -"checksum miniz_oxide 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c468f2369f07d651a5d0bb2c9079f8488a66d5466efe42d0c5c6466edcb7f71e" -"checksum miniz_oxide_c_api 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b7fe927a42e3807ef71defb191dc87d4e24479b221e67015fe38ae2b7b447bab" -"checksum mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "83f51996a3ed004ef184e16818edc51fadffe8e7ca68be67f9dee67d84d0ff23" -"checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" -"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" -"checksum multistream-select 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f989d40aab0ed0d83c1cdb4856b5790e980b96548d1a921f280e985eb049f38d" -"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" -"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" -"checksum nohash-hasher 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0d138afcce92d219ccb6eb53d9b1e8a96ac0d633cfd3c53cd9856d96d1741bb8" -"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" -"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 num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a23f0ed30a54abaa0c7e83b1d2d87ada7c3c23078d1d87815af3e3b6385fbba" -"checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" -"checksum once_cell 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "532c29a261168a45ce28948f9537ddd7a5dd272cc513b3017b1e82a88f962c37" -"checksum opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "93f5bb2e8e8dec81642920ccff6b61f1eb94fa3020c5a325c9851ff604152409" -"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" -"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)" = "" -"checksum parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dcb43c05fb71c03b4ea7327bf15694da1e0f23f19d5b1e95bab6c6d74097e336" -"checksum parity-codec-derive 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "00a486fd383382ddcb2de928364b1f82571c1e48274fc43b7667a4738ee4056c" -"checksum parity-multiaddr 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "045b3c7af871285146300da35b1932bb6e4639b66c7c98e85d06a32cbc4e8fa7" -"checksum parity-multihash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05d6a68e07ab34a9e87bd8dd4936f6bb5be21e4f6dbcdbaf04d8e854eba0af01" -"checksum parity-send-wrapper 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aa9777aa91b8ad9dd5aaa04a9b6bcb02c7f1deb952fca5a66034d5e63afc5c6f" -"checksum parity-wasm 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)" = "511379a8194230c2395d2f5fa627a5a7e108a9f976656ce723ae68fca4097bfc" -"checksum parking_lot 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d4d05f1349491390b1730afba60bb20d55761bef489a954546b58b4b34e1e2ac" -"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" -"checksum parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa7767817701cce701d5585b9c4db3cdd02086398322c1d7e8bf5094a96a2ce7" -"checksum parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4db1a8ccf734a7bce794cc19b3df06ed87ab2f3907036b693c68f56b4d4537fa" -"checksum parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad7f7e6ebdc79edff6fdcb87a55b620174f7a989e3eb31b65231f4af57f00b8c" -"checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" -"checksum parking_lot_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb88cb1cb3790baa6776844f968fea3be44956cf184fa1be5a03341f5491278c" -"checksum paste 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1f4a4a1c555c6505821f9d58b8779d0f630a6b7e4e1be24ba718610acf01fa79" -"checksum paste-impl 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "26e796e623b8b257215f27e6c80a5478856cae305f5b59810ff9acdaa34570e6" -"checksum pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" -"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" -"checksum primitive-types 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6e8612a8dc70f26276fed6131c153ca277cf275ee0a5e2a50cd8a69c697beb8f" -"checksum proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e10d4b51f154c8a7fb96fd6dad097cb74b863943ec010ac94b9fd1be8861fe1e" -"checksum proc-macro-hack 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2c725b36c99df7af7bf9324e9c999b9e37d92c8f8caf106d82e1d7953218d2d8" -"checksum proc-macro-hack 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0c1dd4172a1e1f96f709341418f49b11ea6c2d95d53dca08c0f74cbd332d9cf3" -"checksum proc-macro-hack-impl 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2b753ad9ed99dd8efeaa7d2fb8453c8f6bc3e54b97966d35f1bc77ca6865254a" -"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -"checksum protobuf 2.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a151c11a92df0059d6ab446fafa3b21a1210aad4bc2293e1c946e8132b10db01" -"checksum quick-error 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5fb6ccf8db7bbcb9c2eae558db5ab4f3da1c2a87e4e597ed394726bc8ea6ca1d" -"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" -"checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db" -"checksum rand 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)" = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" -"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" -"checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" -"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" -"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" -"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -"checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0" -"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" -"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_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 rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "373814f27745b2686b350dd261bfd24576a6fb0e2c5919b3a2b6005f820b0473" -"checksum rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b055d1e92aba6877574d8fe604a63c8b5df60f60e5982bf7ccbb1338ea527356" -"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -"checksum redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)" = "12229c14a0f65c4f1cb046a3b52047cdd9da1f4b30f8a39c5063c8bae515e252" -"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" -"checksum regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8f0a0bcab2fd7d1d7c54fa9eae6f43eddeb9ce2e7352f8518a814a4f65d60c58" -"checksum regex-syntax 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "dcfd8681eebe297b81d98498869d4aae052137651ad7b96822f09ceb690d0a96" -"checksum ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)" = "426bc186e3e95cac1e4a4be125a4aca7e84c2d616ffc02244eef36e2a60a093c" -"checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af" -"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 rw-stream-sink 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9cbe61c20455d3015b2bb7be39e1872310283b8e5a52f5b242b0ac7581fe78" -"checksum ryu 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "b96a9549dc8d48f2c283938303c4b5a77aa29bfbc5b54b084fb1630408899a8f" -"checksum safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7f7bf422d23a88c16d5090d455f182bc99c60af4df6a345c63428acf5129e347" -"checksum schnorrkel 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b5eff518f9bed3d803a0d002af0ab96339b0ebbedde3bec98a684986134b7a39" -"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 sct 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f5adf8fbd58e1b1b52699dc8bed2630faecb6d8c7bee77d009d6bbe4af569b9" -"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum send_wrapper 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a0eddf2e8f50ced781f288c19f18621fa72a3779e3cb58dbf23b07469b0abeb4" -"checksum serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)" = "a72e9b96fa45ce22a4bc23da3858dfccfd60acd28a25bcd328a98fdd6bea43fd" -"checksum serde_derive 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)" = "101b495b109a3e3ca8c4cbe44cf62391527cdfb6ba15821c5ce80bcd5ea23f9f" -"checksum serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "5a23aa71d4a4d43fdbfaac00eff68ba8a06a51759a89ac3304323e800c4dd40d" -"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" -"checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d" -"checksum sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd26bc0e7a2e3a7c959bc494caf58b72ee0c71d67704e9520f736ca7e4853ecf" -"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" -"checksum slog 2.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1e1a2eec401952cd7b12a84ea120e2d57281329940c3f93c2bf04f462539508e" -"checksum slog-json 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ddc0d2aff1f8f325ef660d9a0eb6e6dcd20b30b3f581a5897f58bf42d061c37a" -"checksum slog-scope 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "60c04b4726fa04595ccf2c2dad7bcd15474242c4c5e109a8a376e8a2c9b1539a" -"checksum smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c4488ae950c49d403731982257768f48fada354a5203fe81f9bb6f43ca9002be" -"checksum snow 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5a64f02fd208ef15bd2d1a65861df4707e416151e1272d02c8faafad1c138100" -"checksum soketto 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8cf3ae22c0bce5437c7dce6a2b00e492c19da1feb21ad64a7b6fd7058438c3f2" -"checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3" -"checksum spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44363f6f51401c34e7be73db0db371c04705d35efbe9f7d6082e03a921a32c55" -"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" -"checksum static_assertions 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c19be23126415861cb3a23e501d34a708f7f9b2183c5252d690941c2e69199d5" -"checksum static_slice 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "92a7e0c5e3dfb52e8fbe0e63a1b947bbb17b4036408b151353c4491374931362" -"checksum stream-cipher 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8861bc80f649f5b4c9bd38b696ae9af74499d479dbfb327f0607de6b326a36bc" -"checksum strum 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1810e25f576e7ffce1ff5243b37066da5ded0310b3274c20baaeccb1145b2806" -"checksum strum_macros 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "572a2f4e53dd4c3483fd79e5cc10ddd773a3acb1169bbfe8762365e107110579" -"checksum substrate-bip39 0.2.1 (git+https://github.com/paritytech/substrate-bip39)" = "" -"checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" -"checksum subtle 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "01dca13cf6c3b179864ab3292bd794e757618d35a7766b7c46050c614ba00829" -"checksum syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)" = "a1393e4a97a19c01e900df2aec855a29f71cf02c402e2f443b8d2747c25c5dbe" -"checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" -"checksum termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4096add70612622289f2fdcdbd5086dc81c1e2675e6ae58d6c4f62a16c6d7f2f" -"checksum termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dde0593aeb8d47accea5392b39350015b5eccb12c0d98044d856983d89548dea" -"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" -"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.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e9175261fbdb60781fcd388a4d6cc7e14764a2b629a7ad94abb439aed223a44f" -"checksum tk-listen 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5462b0f968c0457efe38fcd2df7e487096b992419e4f5337b06775a614bbda4b" -"checksum tokio 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "94a1f9396aec29d31bb16c24d155cfa144d1af91c40740125db3131bdaf76da8" -"checksum tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c501eceaf96f0e1793cf26beb63da3d11c738c4a943fdf3746d81d64684c39f" -"checksum tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d16217cad7f1b840c5a97dfb3c43b0c871fef423a6e8d2118c604e843662a443" -"checksum tokio-dns-unofficial 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "82c65483db54eb91b4ef3a9389a3364558590faf30ce473141707c0e16fda975" -"checksum tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "83ea44c6c0773cc034771693711c35c677b4b5a4b21b9e7071704c54de7d555e" -"checksum tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe6dc22b08d6993916647d108a1a7d15b9cd29c4f4496c62b92c45b5041b7af" -"checksum tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5090db468dad16e1a7a54c8c67280c5e4b544f3d3e018f0b913b400261f85926" -"checksum tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6af16bfac7e112bea8b0442542161bfc41cbfa4466b580bdda7d18cb88b911ce" -"checksum tokio-rustls 0.10.0-alpha.3 (registry+https://github.com/rust-lang/crates.io-index)" = "316fdbc899efec48b3b492bd0f339e6d81c4ee96a409257572147ec341943452" -"checksum tokio-sync 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "5b2f843ffdf8d6e1f90bddd48da43f99ab071660cd92b7ec560ef3cdfd7a409a" -"checksum tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1d14b10654be682ac43efee27401d792507e30fd8d26389e1da3b185de2e4119" -"checksum tokio-threadpool 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72558af20be886ea124595ea0f806dd5703b8958e4705429dd58b3d8231f72f2" -"checksum tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "f2106812d500ed25a4f38235b9cae8f78a09edf43203e16e59c3b769a342a60e" -"checksum tokio-trace-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "350c9edade9830dc185ae48ba45667a445ab59f6167ef6d0254ec9d2430d9dd3" -"checksum tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "66268575b80f4a4a710ef83d087fdfeeabdce9b74c797535fbac18a2cb906e92" -"checksum tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "037ffc3ba0e12a0ab4aca92e5234e0dedeb48fddf6ccd260f1f150a36a9f2445" -"checksum toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b8c96d7873fa7ef8bdeb3a9cda3ac48389b4154f32b9803b4bc26220b677b039" -"checksum trie-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1ba73747fd3a64ab531274c04cb588dfa9d30d972d62990831e63fbce2cfec59" -"checksum trie-root 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cfa2e20c4f1418ac2e71ddc418e35e1b56e34022e2146209ffdbf1b2de8b1bd9" -"checksum twofish 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712d261e83e727c8e2dbb75dacac67c36e35db36a958ee504f2164fc052434e1" -"checksum twox-hash 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6c7bcecad121018bdcd6b709fa2325b004878fcb3d3067934ce90749f0faff9a" -"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" -"checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" -"checksum uint 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2143cded94692b156c356508d92888acc824db5bffc0b4089732264c6fcf86d4" -"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" -"checksum unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "141339a08b982d942be2ca06ff8b076563cbe223d1befd5450716790d44e2426" -"checksum unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1967f4cdfc355b37fd76d2a954fb2ed3871034eb4f26d60537d88795cfc332a9" -"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" -"checksum unsigned-varint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2c64cdf40b4a9645534a943668681bcb219faf51874d4b65d2e0abda1b10a2ab" -"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 utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737" -"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" -"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum wasm-bindgen 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "b7ccc7b93cfd13e26700a9e2e41e6305f1951b87e166599069f77d10358100e6" -"checksum wasm-bindgen-backend 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "1953f91b1608eb1522513623c7739f047bb0fed4128ce51a93f08e12cc314645" -"checksum wasm-bindgen-futures 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "fa1af11c73eca3dc8c51c76ea475a4416e912da6402064a49fc6c0214701866d" -"checksum wasm-bindgen-macro 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "0f69da5696545d7ca6607a2e4b1a0edf5a6b36b2c49dbb0f1df6ad1d92884047" -"checksum wasm-bindgen-macro-support 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d4246f3bc73223bbb846f4f2430a60725826a96c9389adf715ed1d5af46dec6" -"checksum wasm-bindgen-shared 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "c08381e07e7a79e5e229ad7c60d15833d19033542cc5dd91d085df59d235f4a6" -"checksum wasm-bindgen-webidl 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "1f42ff7adb8102bf5ad8adbc45b1635c520c8175f9fdf6eb2c54479d485d435a" -"checksum wasm-timer 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad9ac33c834103916e373d648adf65f58c83fb3d8a0f3e6b9a64bca7253a4dca" -"checksum wasmi 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "aebbaef470840d157a5c47c8c49f024da7b1b80e90ff729ca982b2b80447e78b" -"checksum wasmi-validation 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab380192444b3e8522ae79c0a1976e42a82920916ccdfbce3def89f456ea33f3" -"checksum web-sys 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "540b8259eb242ff3a566fa0140bda03a4ece4e5c226e1284b5c95dddcd4341f6" -"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" -"checksum weedle 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc44aa200daee8b1f3a004beaf16554369746f1b4486f0cf93b0caf8a3c2d1e" -"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" -"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" -"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -"checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba" -"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" -"checksum x25519-dalek 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7ee1585dc1484373cbc1cee7aafda26634665cf449436fd6e24bfd1fad230538" -"checksum yamux 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "01bd67889938c48f0049fc60a77341039e6c3eaf16cb7693e6ead7c0ba701295" -"checksum zeroize 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8ddfeb6eee2fb3b262ef6e0898a52b7563bb8e0d5955a313b3cf2f808246ea14" -"checksum zeroize 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b60a6c572b91d8ecb0a460950d84fe5b40699edd07d65f73789b31237afc8f66" -"checksum zeroize_derive 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9dac4b660d969bff9c3fe1847a891cacaa8b21dd5f2aae6e0a3e0975aea96431" diff --git a/node-template/runtime/wasm/Cargo.toml b/node-template/runtime/wasm/Cargo.toml deleted file mode 100644 index 2b89c6e4db807296e0bb58655f0587ccfbb5f34c..0000000000000000000000000000000000000000 --- a/node-template/runtime/wasm/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "node-template-runtime-wasm" -version = "2.0.0" -authors = ["Anonymous"] -edition = "2018" - -[lib] -crate-type = ["cdylib"] - -[dependencies] -node-template-runtime = { path = "..", default-features = false } - -[features] -default = [] -std = [ - "node-template-runtime/std", -] - -[profile.release] -panic = "abort" -lto = true - -[workspace] -members = [] diff --git a/node-template/runtime/wasm/build.sh b/node-template/runtime/wasm/build.sh deleted file mode 100755 index a549eeb50a5f14168ff41313b61c98edc381bd87..0000000000000000000000000000000000000000 --- a/node-template/runtime/wasm/build.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash -set -e - -if cargo --version | grep -q "nightly"; then - CARGO_CMD="cargo" -else - CARGO_CMD="cargo +nightly" -fi -CARGO_INCREMENTAL=0 RUSTFLAGS="-C link-arg=--export-table" $CARGO_CMD build --target=wasm32-unknown-unknown --release "$@" -for i in node_template_runtime_wasm -do - wasm-gc target/wasm32-unknown-unknown/release/$i.wasm target/wasm32-unknown-unknown/release/$i.compact.wasm -done diff --git a/node-template/runtime/wasm/src/lib.rs b/node-template/runtime/wasm/src/lib.rs deleted file mode 100644 index 224f854812b9fd9f7a875770bc4421b4d8fa277d..0000000000000000000000000000000000000000 --- a/node-template/runtime/wasm/src/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -//! The Substrate node template runtime reexported for WebAssembly compile. - -#![cfg_attr(not(feature = "std"), no_std)] - -pub use node_template_runtime::*; diff --git a/node-template/scripts/build.sh b/node-template/scripts/build.sh deleted file mode 100755 index 980a8fa802d02f9f66f58ec8aebea522d690f19a..0000000000000000000000000000000000000000 --- a/node-template/scripts/build.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env bash - -set -e - -PROJECT_ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." >/dev/null && pwd )" - -export CARGO_INCREMENTAL=0 - -bold=$(tput bold) -normal=$(tput sgr0) - -# Save current directory. -pushd . >/dev/null - -for SRC in runtime/wasm -do - echo "${bold}Building webassembly binary in $SRC...${normal}" - cd "$PROJECT_ROOT/$SRC" - - ./build.sh "$@" - - cd - >> /dev/null -done - -# Restore initial directory. -popd >/dev/null diff --git a/node-template/src/chain_spec.rs b/node-template/src/chain_spec.rs index f14cf41464a1b8e9b14641e3d4dc39ffb892a3d0..3970522b37ab5c2c2a4227e7c52c06a9a4645974 100644 --- a/node-template/src/chain_spec.rs +++ b/node-template/src/chain_spec.rs @@ -1,7 +1,7 @@ use primitives::{ed25519, sr25519, Pair}; use node_template_runtime::{ - AccountId, AuraId as AuthorityId, GenesisConfig, AuraConfig, TimestampConfig, BalancesConfig, - SudoConfig, IndicesConfig, SystemConfig + AccountId, GenesisConfig, AuraConfig, BalancesConfig, + SudoConfig, IndicesConfig, SystemConfig, WASM_BINARY, AuraId }; use substrate_service; @@ -22,7 +22,7 @@ pub enum Alternative { LocalTestnet, } -fn authority_key(s: &str) -> AuthorityId { +fn authority_key(s: &str) -> AuraId { ed25519::Pair::from_string(&format!("//{}", s), None) .expect("static values are valid; qed") .public() @@ -88,28 +88,19 @@ impl Alternative { } } -fn testnet_genesis(initial_authorities: Vec, endowed_accounts: Vec, root_key: AccountId) -> GenesisConfig { +fn testnet_genesis(initial_authorities: Vec, endowed_accounts: Vec, root_key: AccountId) -> GenesisConfig { GenesisConfig { system: Some(SystemConfig { - code: include_bytes!("../runtime/wasm/target/wasm32-unknown-unknown/release/node_template_runtime_wasm.compact.wasm").to_vec(), + code: WASM_BINARY.to_vec(), changes_trie_config: Default::default(), - _genesis_phantom_data: Default::default(), }), aura: Some(AuraConfig { authorities: initial_authorities.clone(), }), - timestamp: Some(TimestampConfig { - minimum_period: 5, // 10 second block time. - }), indices: Some(IndicesConfig { ids: endowed_accounts.clone(), }), balances: Some(BalancesConfig { - transaction_base_fee: 1, - transaction_byte_fee: 0, - existential_deposit: 500, - transfer_fee: 0, - creation_fee: 0, balances: endowed_accounts.iter().cloned().map(|k|(k, 1 << 60)).collect(), vesting: vec![], }), diff --git a/node-template/src/cli.rs b/node-template/src/cli.rs index cd148f3462dce8cac7ffa981d37439967954ba3c..b799a5d9aee4233988302bb22fcd706eda724f79 100644 --- a/node-template/src/cli.rs +++ b/node-template/src/cli.rs @@ -25,16 +25,15 @@ pub fn run(args: I, exit: E, version: VersionInfo) -> error::Result<()> info!("Node name: {}", config.name); info!("Roles: {:?}", config.roles); let runtime = Runtime::new().map_err(|e| format!("{:?}", e))?; - let executor = runtime.executor(); match config.roles { ServiceRoles::LIGHT => run_until_exit( runtime, - service::Factory::new_light(config, executor).map_err(|e| format!("{:?}", e))?, + service::Factory::new_light(config).map_err(|e| format!("{:?}", e))?, exit ), _ => run_until_exit( runtime, - service::Factory::new_full(config, executor).map_err(|e| format!("{:?}", e))?, + service::Factory::new_full(config).map_err(|e| format!("{:?}", e))?, exit ), }.map_err(|e| format!("{:?}", e)) @@ -55,7 +54,7 @@ fn run_until_exit( e: E, ) -> error::Result<()> where - T: Deref>, + T: Deref> + Future + Send + 'static, C: substrate_service::Components, E: IntoExit, { @@ -64,13 +63,13 @@ fn run_until_exit( let informant = informant::build(&service); runtime.executor().spawn(exit.until(informant).map(|_| ())); - let _ = runtime.block_on(e.into_exit()); - exit_send.fire(); - // 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(); - drop(service); + + let _ = runtime.block_on(service.select(e.into_exit())); + exit_send.fire(); + Ok(()) } diff --git a/node-template/src/service.rs b/node-template/src/service.rs index 1de7bb47675242df4a45a84b9eaf56b4f40f2b47..2a981c3bcc348e0bc92f982ef9d5914177650f45 100644 --- a/node-template/src/service.rs +++ b/node-template/src/service.rs @@ -5,11 +5,10 @@ use std::sync::Arc; use log::info; use transaction_pool::{self, txpool::{Pool as TransactionPool}}; -use node_template_runtime::{self, GenesisConfig, opaque::Block, RuntimeApi}; +use node_template_runtime::{self, GenesisConfig, opaque::Block, RuntimeApi, WASM_BINARY}; use substrate_service::{ FactoryFullConfiguration, LightComponents, FullComponents, FullBackend, FullClient, LightClient, LightBackend, FullExecutor, LightExecutor, - TaskExecutor, error::{Error as ServiceError}, }; use basic_authorship::ProposerFactory; @@ -18,7 +17,7 @@ use futures::prelude::*; use substrate_client::{self as client, LongestChain}; use primitives::{ed25519::Pair, Pair as PairT}; use inherents::InherentDataProviders; -use network::construct_simple_protocol; +use network::{config::DummyFinalityProofRequestBuilder, construct_simple_protocol}; use substrate_executor::native_executor_instance; use substrate_service::construct_service_factory; @@ -28,7 +27,7 @@ native_executor_instance!( pub Executor, node_template_runtime::api::dispatch, node_template_runtime::native_version, - include_bytes!("../runtime/wasm/target/wasm32-unknown-unknown/release/node_template_runtime_wasm.compact.wasm") + WASM_BINARY ); #[derive(Default)] @@ -44,6 +43,8 @@ construct_simple_protocol! { construct_service_factory! { struct Factory { Block = Block, + ConsensusPair = Pair, + FinalityPair = Pair, RuntimeApi = RuntimeApi, NetworkProtocol = NodeProtocol { |config| Ok(NodeProtocol::new()) }, RuntimeDispatch = Executor, @@ -62,12 +63,12 @@ construct_service_factory! { Genesis = GenesisConfig, Configuration = NodeConfig, FullService = FullComponents - { |config: FactoryFullConfiguration, executor: TaskExecutor| - FullComponents::::new(config, executor) + { |config: FactoryFullConfiguration| + FullComponents::::new(config) }, AuthoritySetup = { - |service: Self::FullService, executor: TaskExecutor, key: Option>| { - if let Some(key) = key { + |service: Self::FullService| { + if let Some(key) = service.authority_key() { info!("Using authority key {}", key.public()); let proposer = Arc::new(ProposerFactory { client: service.client(), @@ -78,7 +79,7 @@ construct_service_factory! { .ok_or_else(|| ServiceError::SelectChainRequired)?; let aura = start_aura( SlotDuration::get_or_compute(&*client)?, - key.clone(), + Arc::new(key), client.clone(), select_chain, client, @@ -87,22 +88,21 @@ construct_service_factory! { service.config.custom.inherent_data_providers.clone(), service.config.force_authoring, )?; - executor.spawn(aura.select(service.on_exit()).then(|_| Ok(()))); + service.spawn_task(Box::new(aura.select(service.on_exit()).then(|_| Ok(())))); } Ok(service) } }, LightService = LightComponents - { |config, executor| >::new(config, executor) }, + { |config| >::new(config) }, FullImportQueue = AuraImportQueue< Self::Block, > { |config: &mut FactoryFullConfiguration , client: Arc>, _select_chain: Self::SelectChain| { import_queue::<_, _, Pair>( SlotDuration::get_or_compute(&*client)?, - client.clone(), - None, + Box::new(client.clone()), None, None, client, @@ -114,15 +114,15 @@ construct_service_factory! { Self::Block, > { |config: &mut FactoryFullConfiguration, client: Arc>| { + let fprb = Box::new(DummyFinalityProofRequestBuilder::default()) as Box<_>; import_queue::<_, _, Pair>( SlotDuration::get_or_compute(&*client)?, - client.clone(), - None, + Box::new(client.clone()), None, None, client, config.custom.inherent_data_providers.clone(), - ).map_err(Into::into) + ).map(|q| (q, fprb)).map_err(Into::into) } }, SelectChain = LongestChain, Self::Block> diff --git a/node/cli/Cargo.toml b/node/cli/Cargo.toml index 14a5b5c32d10bdb5254243d5ae1b50b6eaceca81..17efcc400f35b97574dfba4dacc214e4bfdbbb34 100644 --- a/node/cli/Cargo.toml +++ b/node/cli/Cargo.toml @@ -12,7 +12,7 @@ tokio = "0.1.7" futures = "0.1" exit-future = "0.1" cli = { package = "substrate-cli", path = "../../core/cli" } -parity-codec = { version = "3.3" } +parity-codec = { version = "4.1.1" } sr-io = { path = "../../core/sr-io" } client = { package = "substrate-client", path = "../../core/client" } primitives = { package = "substrate-primitives", path = "../../core/primitives" } @@ -24,10 +24,11 @@ substrate-basic-authorship = { path = "../../core/basic-authorship" } substrate-service = { path = "../../core/service" } transaction_pool = { package = "substrate-transaction-pool", path = "../../core/transaction-pool" } network = { package = "substrate-network", path = "../../core/network" } -consensus = { package = "substrate-consensus-aura", path = "../../core/consensus/aura" } +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" } sr-primitives = { path = "../../core/sr-primitives" } -aura_primitives = { package = "substrate-consensus-aura-primitives", path = "../../core/consensus/aura/primitives" } node-executor = { path = "../executor" } substrate-keystore = { path = "../../core/keystore" } substrate-telemetry = { package = "substrate-telemetry", path = "../../core/telemetry" } @@ -38,6 +39,9 @@ indices = { package = "srml-indices", path = "../../srml/indices" } timestamp = { package = "srml-timestamp", path = "../../srml/timestamp", default-features = false } rand = "0.6" finality_tracker = { package = "srml-finality-tracker", path = "../../srml/finality-tracker", default-features = false } +contracts = { package = "srml-contracts", path = "../../srml/contracts" } +system = { package = "srml-system", path = "../../srml/system" } +balances = { package = "srml-balances", path = "../../srml/balances" } [dev-dependencies] consensus-common = { package = "substrate-consensus-common", path = "../../core/consensus/common" } diff --git a/node/cli/src/chain_spec.rs b/node/cli/src/chain_spec.rs index 672c75b1a94c6b0438784a06be83fdb6b7c59a72..fc07ae4cea4d92837d6c9da38e996b176ad8a626 100644 --- a/node/cli/src/chain_spec.rs +++ b/node/cli/src/chain_spec.rs @@ -17,10 +17,13 @@ //! Substrate chain configurations. use primitives::{ed25519, sr25519, Pair, crypto::UncheckedInto}; -use node_primitives::{AccountId, AuraId}; -use node_runtime::{CouncilSeatsConfig, AuraConfig, DemocracyConfig, SystemConfig, - SessionConfig, StakingConfig, StakerStatus, TimestampConfig, BalancesConfig, TreasuryConfig, - SudoConfig, ContractsConfig, GrandpaConfig, IndicesConfig, Permill, Perbill, SessionKeys}; +use node_primitives::{AccountId, AuraId, Balance}; +use node_runtime::{ + GrandpaConfig, BalancesConfig, ContractsConfig, ElectionsConfig, DemocracyConfig, + CouncilConfig, AuraConfig, ImOnlineConfig, IndicesConfig, SessionConfig, StakingConfig, + SudoConfig, TechnicalCommitteeConfig, SystemConfig, WASM_BINARY, Perbill, SessionKeys, + StakerStatus, DAYS, DOLLARS, MILLICENTS, +}; pub use node_runtime::GenesisConfig; use substrate_service; use hex_literal::hex; @@ -37,6 +40,10 @@ pub fn flaming_fir_config() -> Result { ChainSpec::from_embedded(include_bytes!("../res/flaming-fir.json")) } +fn session_keys(key: ed25519::Public) -> SessionKeys { + SessionKeys { ed25519: key } +} + fn staging_testnet_config_genesis() -> GenesisConfig { // stash, controller, session-key // generated with secret: @@ -88,34 +95,19 @@ fn staging_testnet_config_genesis() -> GenesisConfig { hex!["9ee5e5bdc0ec239eb164f865ecc345ce4c88e76ee002e0f7e318097347471809"].unchecked_into(), ]; - const MILLICENTS: u128 = 1_000_000_000; - const CENTS: u128 = 1_000 * MILLICENTS; // assume this is worth about a cent. - const DOLLARS: u128 = 100 * CENTS; - - const SECS_PER_BLOCK: u64 = 6; - const MINUTES: u64 = 60 / SECS_PER_BLOCK; - const HOURS: u64 = MINUTES * 60; - const DAYS: u64 = HOURS * 24; - - const ENDOWMENT: u128 = 10_000_000 * DOLLARS; - const STASH: u128 = 100 * DOLLARS; + const ENDOWMENT: Balance = 10_000_000 * DOLLARS; + const STASH: Balance = 100 * DOLLARS; GenesisConfig { system: Some(SystemConfig { - code: include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm").to_vec(), // FIXME change once we have #1252 - _genesis_phantom_data: Default::default(), + code: WASM_BINARY.to_vec(), changes_trie_config: Default::default(), }), balances: Some(BalancesConfig { - transaction_base_fee: 1 * CENTS, - transaction_byte_fee: 10 * MILLICENTS, balances: endowed_accounts.iter().cloned() .map(|k| (k, ENDOWMENT)) .chain(initial_authorities.iter().map(|x| (x.0.clone(), STASH))) .collect(), - existential_deposit: 1 * DOLLARS, - transfer_fee: 1 * CENTS, - creation_fee: 1 * CENTS, vesting: vec![], }), indices: Some(IndicesConfig { @@ -124,8 +116,7 @@ fn staging_testnet_config_genesis() -> GenesisConfig { .collect::>(), }), session: Some(SessionConfig { - validators: initial_authorities.iter().map(|x| x.1.clone()).collect(), - keys: initial_authorities.iter().map(|x| (x.1.clone(), SessionKeys(x.2.clone(),x.2.clone()))).collect::>(), + keys: initial_authorities.iter().map(|x| (x.0.clone(), session_keys(x.2.clone()))).collect::>(), }), staking: Some(StakingConfig { current_era: 0, @@ -136,50 +127,26 @@ fn staging_testnet_config_genesis() -> GenesisConfig { offline_slash_grace: 4, 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.1.clone()).collect(), + invulnerables: initial_authorities.iter().map(|x| x.0.clone()).collect(), }), democracy: Some(DemocracyConfig::default()), - council_seats: Some(CouncilSeatsConfig { - active_council: vec![], - candidacy_bond: 10 * DOLLARS, - voter_bond: 1 * DOLLARS, - voting_fee: 2 * DOLLARS, - present_slash_per_voter: 1 * CENTS, - carry_count: 6, + 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, - approval_voting_period: 2 * DAYS, term_duration: 28 * DAYS, desired_seats: 0, - decay_ratio: 0, - inactive_grace_period: 1, // one additional vote should go by before an inactive voter can be reaped. - }), - timestamp: Some(TimestampConfig { - minimum_period: SECS_PER_BLOCK / 2, // due to the nature of aura the slots are 2*period - }), - treasury: Some(TreasuryConfig { - proposal_bond: Permill::from_percent(5), - proposal_bond_minimum: 1 * DOLLARS, - spend_period: 1 * DAYS, - burn: Permill::from_percent(50), }), contracts: Some(ContractsConfig { - signed_claim_handicap: 2, - rent_byte_price: 4, - rent_deposit_offset: 1000, - storage_size_offset: 8, - surcharge_reward: 150, - tombstone_deposit: 16, - transaction_base_fee: 1 * CENTS, - transaction_byte_fee: 10 * MILLICENTS, - transfer_fee: 1 * CENTS, - creation_fee: 1 * CENTS, - contract_fee: 1 * CENTS, - call_base_fee: 1000, - create_base_fee: 1000, - gas_price: 1 * MILLICENTS, - max_depth: 1024, - block_gas_limit: 10_000_000, current_schedule: Default::default(), + gas_price: 1 * MILLICENTS, }), sudo: Some(SudoConfig { key: endowed_accounts[0].clone(), @@ -187,9 +154,12 @@ fn staging_testnet_config_genesis() -> GenesisConfig { aura: Some(AuraConfig { authorities: initial_authorities.iter().map(|x| x.2.clone()).collect(), }), + im_online: Some(ImOnlineConfig { + gossip_at: 0, + last_new_era_start: 0, + }), grandpa: Some(GrandpaConfig { authorities: initial_authorities.iter().map(|x| (x.3.clone(), 1)).collect(), - _genesis_phantom_data: Default::default(), }), } } @@ -264,53 +234,25 @@ pub fn testnet_genesis( ] }); - const STASH: u128 = 1 << 20; - const ENDOWMENT: u128 = 1 << 20; - - let council_desired_seats = (endowed_accounts.len() / 2 - initial_authorities.len()) as u32; - let mut contracts_config = ContractsConfig { - signed_claim_handicap: 2, - rent_byte_price: 4, - rent_deposit_offset: 1000, - storage_size_offset: 8, - surcharge_reward: 150, - tombstone_deposit: 16, - transaction_base_fee: 1, - transaction_byte_fee: 0, - transfer_fee: 0, - creation_fee: 0, - contract_fee: 21, - call_base_fee: 135, - create_base_fee: 175, - gas_price: 1, - max_depth: 1024, - block_gas_limit: 10_000_000, - current_schedule: Default::default(), - }; - // this should only be enabled on development chains - contracts_config.current_schedule.enable_println = enable_println; + 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: include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm").to_vec(), - _genesis_phantom_data: Default::default(), + code: WASM_BINARY.to_vec(), changes_trie_config: Default::default(), }), indices: Some(IndicesConfig { ids: endowed_accounts.clone(), }), balances: Some(BalancesConfig { - transaction_base_fee: 1, - transaction_byte_fee: 0, - existential_deposit: 500, - transfer_fee: 0, - creation_fee: 0, balances: endowed_accounts.iter().map(|k| (k.clone(), ENDOWMENT)).collect(), vesting: vec![], }), session: Some(SessionConfig { - validators: initial_authorities.iter().map(|x| x.1.clone()).collect(), - keys: initial_authorities.iter().map(|x| (x.1.clone(), SessionKeys(x.2.clone(), x.2.clone()))).collect::>(), + keys: initial_authorities.iter().map(|x| (x.0.clone(), session_keys(x.2.clone()))).collect::>(), }), staking: Some(StakingConfig { current_era: 0, @@ -321,44 +263,44 @@ pub fn testnet_genesis( current_session_reward: 0, offline_slash_grace: 0, stakers: initial_authorities.iter().map(|x| (x.0.clone(), x.1.clone(), STASH, StakerStatus::Validator)).collect(), - invulnerables: initial_authorities.iter().map(|x| x.1.clone()).collect(), + invulnerables: initial_authorities.iter().map(|x| x.0.clone()).collect(), }), democracy: Some(DemocracyConfig::default()), - council_seats: Some(CouncilSeatsConfig { - active_council: endowed_accounts.iter() + collective_Instance1: Some(CouncilConfig { + members: vec![], + phantom: Default::default(), + }), + collective_Instance2: Some(TechnicalCommitteeConfig { + 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(), - candidacy_bond: 10, - voter_bond: 2, - voting_fee: 5, - present_slash_per_voter: 1, - carry_count: 4, presentation_duration: 10, - approval_voting_period: 20, term_duration: 1000000, - desired_seats: council_desired_seats, - decay_ratio: council_desired_seats / 3, - inactive_grace_period: 1, + desired_seats: desired_seats, }), - timestamp: Some(TimestampConfig { - minimum_period: 2, // 2*2=4 second block time. - }), - treasury: Some(TreasuryConfig { - proposal_bond: Permill::from_percent(5), - proposal_bond_minimum: 1_000_000, - spend_period: 12 * 60 * 24, - burn: Permill::from_percent(50), + contracts: Some(ContractsConfig { + current_schedule: contracts::Schedule { + enable_println, // this should only be enabled on development chains + ..Default::default() + }, + gas_price: 1 * MILLICENTS, }), - contracts: Some(contracts_config), sudo: Some(SudoConfig { key: root_key, }), aura: Some(AuraConfig { authorities: initial_authorities.iter().map(|x| x.2.clone()).collect(), }), + im_online: Some(ImOnlineConfig{ + gossip_at: 0, + last_new_era_start: 0, + }), grandpa: Some(GrandpaConfig { authorities: initial_authorities.iter().map(|x| (x.3.clone(), 1)).collect(), - _genesis_phantom_data: Default::default(), }), } } @@ -402,23 +344,15 @@ pub(crate) mod tests { use service_test; use crate::service::Factory; - fn local_testnet_genesis_instant() -> GenesisConfig { - let mut genesis = local_testnet_genesis(); - genesis.timestamp = Some(TimestampConfig { minimum_period: 1 }); - genesis - } - fn local_testnet_genesis_instant_single() -> GenesisConfig { - let mut genesis = testnet_genesis( + testnet_genesis( vec![ get_authority_keys_from_seed("Alice"), ], get_account_id_from_seed("Alice"), None, false, - ); - genesis.timestamp = Some(TimestampConfig { minimum_period: 1 }); - genesis + ) } /// Local testnet config (single validator - Alice) @@ -437,7 +371,7 @@ pub(crate) mod tests { /// Local testnet config (multivalidator Alice + Bob) pub fn integration_test_config_with_two_authorities() -> ChainSpec { - ChainSpec::from_genesis("Integration Test", "test", local_testnet_genesis_instant, vec![], None, None, None, None) + ChainSpec::from_genesis("Integration Test", "test", local_testnet_genesis, vec![], None, None, None, None) } #[test] diff --git a/node/cli/src/factory_impl.rs b/node/cli/src/factory_impl.rs index 0d94610362fdc84b3d9f1e2aa81ba477bc312f25..182cf4080e4a24312b3c54543212b938a31e1a0b 100644 --- a/node/cli/src/factory_impl.rs +++ b/node/cli/src/factory_impl.rs @@ -23,13 +23,10 @@ use rand::rngs::StdRng; use parity_codec::Decode; use keyring::sr25519::Keyring; -use node_primitives::Hash; -use node_runtime::{Call, CheckedExtrinsic, UncheckedExtrinsic, BalancesCall}; -use primitives::sr25519; -use primitives::crypto::Pair; +use node_runtime::{Call, CheckedExtrinsic, UncheckedExtrinsic, SignedExtra, BalancesCall}; +use primitives::{sr25519, crypto::Pair}; use parity_codec::Encode; -use sr_primitives::generic::Era; -use sr_primitives::traits::{Block as BlockT, Header as HeaderT}; +use sr_primitives::{generic::Era, traits::{Block as BlockT, Header as HeaderT, SignedExtension}}; use substrate_service::ServiceFactory; use transaction_factory::RuntimeAdapter; use transaction_factory::modes::Mode; @@ -38,7 +35,7 @@ use inherents::InherentData; use timestamp; use finality_tracker; -// TODO get via api: >::minimum_period(). See #2587. +// TODO get via api: ::MinimumPeriod::get(). See #2587. const MINIMUM_PERIOD: u64 = 99; pub struct FactoryState { @@ -54,6 +51,17 @@ pub struct FactoryState { type Number = <::Header as HeaderT>::Number; +impl FactoryState { + fn build_extra(index: node_primitives::Index, phase: u64) -> node_runtime::SignedExtra { + ( + system::CheckEra::from(Era::mortal(256, phase)), + system::CheckNonce::from(index), + system::CheckWeight::from(), + balances::TakeFees::from(0) + ) + } +} + impl RuntimeAdapter for FactoryState { type AccountId = node_primitives::AccountId; type Balance = node_primitives::Balance; @@ -132,16 +140,14 @@ impl RuntimeAdapter for FactoryState { let phase = self.extract_phase(*prior_block_hash); sign::(CheckedExtrinsic { - signed: Some((sender.clone(), index)), + signed: Some((sender.clone(), Self::build_extra(index, phase))), function: Call::Balances( BalancesCall::transfer( - indices::address::Address::Id( - destination.clone().into() - ), + indices::address::Address::Id(destination.clone().into()), (*amount).into() ) ) - }, key, &prior_block_hash, phase) + }, key, (prior_block_hash.clone(), (), (), ())) } fn inherent_extrinsics(&self) -> InherentData { @@ -229,13 +235,11 @@ fn gen_seed_bytes(seed: u64) -> [u8; 32] { fn sign( xt: CheckedExtrinsic, key: &sr25519::Pair, - prior_block_hash: &Hash, - phase: u64, + additional_signed: ::AdditionalSigned, ) -> ::Extrinsic { let s = match xt.signed { - Some((signed, index)) => { - let era = Era::mortal(256, phase); - let payload = (index.into(), xt.function, era, prior_block_hash); + Some((signed, extra)) => { + let payload = (xt.function, extra.clone(), additional_signed); let signature = payload.using_encoded(|b| { if b.len() > 256 { key.sign(&sr_io::blake2_256(b)) @@ -244,8 +248,8 @@ fn sign( } }).into(); UncheckedExtrinsic { - signature: Some((indices::address::Address::Id(signed), signature, payload.0, era)), - function: payload.1, + signature: Some((indices::address::Address::Id(signed), signature, extra)), + function: payload.0, } } None => UncheckedExtrinsic { diff --git a/node/cli/src/lib.rs b/node/cli/src/lib.rs index ab1fd03ae7b24eb7afc4a4ba44a710f143bc09c9..b18fa57411ce5bdd04b971dbcbf7e0ab4c2b275a 100644 --- a/node/cli/src/lib.rs +++ b/node/cli/src/lib.rs @@ -156,16 +156,15 @@ pub fn run(args: I, exit: E, version: cli::VersionInfo) -> error::Resul info!("Roles: {:?}", config.roles); let runtime = RuntimeBuilder::new().name_prefix("main-tokio-").build() .map_err(|e| format!("{:?}", e))?; - let executor = runtime.executor(); match config.roles { ServiceRoles::LIGHT => run_until_exit( runtime, - service::Factory::new_light(config, executor).map_err(|e| format!("{:?}", e))?, + service::Factory::new_light(config).map_err(|e| format!("{:?}", e))?, exit ), _ => run_until_exit( runtime, - service::Factory::new_full(config, executor).map_err(|e| format!("{:?}", e))?, + service::Factory::new_full(config).map_err(|e| format!("{:?}", e))?, exit ), }.map_err(|e| format!("{:?}", e)) @@ -207,7 +206,7 @@ fn run_until_exit( e: E, ) -> error::Result<()> where - T: Deref>, + T: Deref> + Future + Send + 'static, C: substrate_service::Components, E: IntoExit, { @@ -216,13 +215,12 @@ fn run_until_exit( let informant = cli::informant::build(&service); runtime.executor().spawn(exit.until(informant).map(|_| ())); - let _ = runtime.block_on(e.into_exit()); - exit_send.fire(); - // 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(); - drop(service); + + let _ = runtime.block_on(service.select(e.into_exit())); + exit_send.fire(); // TODO [andre]: timeout this future #1318 let _ = runtime.shutdown_on_idle().wait(); diff --git a/node/cli/src/service.rs b/node/cli/src/service.rs index 058f58c9c2c7e1c8b9d631934e20dde1c77f53e7..53d6f927b485bb5db30d72665efad6f049ad958d 100644 --- a/node/cli/src/service.rs +++ b/node/cli/src/service.rs @@ -21,17 +21,18 @@ use std::sync::Arc; use std::time::Duration; +use aura::{import_queue, start_aura, AuraImportQueue, SlotDuration}; use client::{self, LongestChain}; -use consensus::{import_queue, start_aura, AuraImportQueue, SlotDuration}; use grandpa::{self, FinalityProofProvider as GrandpaFinalityProofProvider}; use node_executor; -use primitives::{Pair as PairT, ed25519}; +use primitives::Pair; +use grandpa_primitives::AuthorityPair as GrandpaPair; use futures::prelude::*; -use node_primitives::Block; +use node_primitives::{AuraPair, Block}; use node_runtime::{GenesisConfig, RuntimeApi}; use substrate_service::{ FactoryFullConfiguration, LightComponents, FullComponents, FullBackend, - FullClient, LightClient, LightBackend, FullExecutor, LightExecutor, TaskExecutor, + FullClient, LightClient, LightBackend, FullExecutor, LightExecutor, error::{Error as ServiceError}, }; use transaction_pool::{self, txpool::{Pool as TransactionPool}}; @@ -50,7 +51,7 @@ construct_simple_protocol! { pub struct NodeConfig { /// grandpa connection to import block // FIXME #1134 rather than putting this on the config, let's have an actual intermediate setup state - pub grandpa_import_setup: Option<(Arc>, grandpa::LinkHalfForService)>, + pub grandpa_import_setup: Option<(grandpa::BlockImportForService, grandpa::LinkHalfForService)>, inherent_data_providers: InherentDataProviders, } @@ -66,6 +67,8 @@ impl Default for NodeConfig where F: substrate_service::ServiceFactory { construct_service_factory! { struct Factory { Block = Block, + ConsensusPair = AuraPair, + FinalityPair = GrandpaPair, RuntimeApi = RuntimeApi, NetworkProtocol = NodeProtocol { |config| Ok(NodeProtocol::new()) }, RuntimeDispatch = node_executor::Executor, @@ -76,15 +79,16 @@ construct_service_factory! { Genesis = GenesisConfig, Configuration = NodeConfig, FullService = FullComponents - { |config: FactoryFullConfiguration, executor: TaskExecutor| - FullComponents::::new(config, executor) }, + { |config: FactoryFullConfiguration| + FullComponents::::new(config) }, AuthoritySetup = { - |mut service: Self::FullService, executor: TaskExecutor, local_key: Option>| { + |mut service: Self::FullService| { let (block_import, link_half) = service.config.custom.grandpa_import_setup.take() .expect("Link Half and Block Import are present for Full Services or setup failed before. qed"); - if let Some(ref key) = local_key { - info!("Using authority key {}", key.public()); + if let Some(aura_key) = service.authority_key() { + info!("Using aura key {}", aura_key.public()); + let proposer = Arc::new(substrate_basic_authorship::ProposerFactory { client: service.client(), transaction_pool: service.transaction_pool(), @@ -93,30 +97,30 @@ construct_service_factory! { let client = service.client(); let select_chain = service.select_chain() .ok_or(ServiceError::SelectChainRequired)?; + let aura = start_aura( SlotDuration::get_or_compute(&*client)?, - key.clone(), + Arc::new(aura_key), client, select_chain, - block_import.clone(), + block_import, proposer, service.network(), service.config.custom.inherent_data_providers.clone(), service.config.force_authoring, )?; - executor.spawn(aura.select(service.on_exit()).then(|_| Ok(()))); - - info!("Running Grandpa session as Authority {}", key.public()); + let select = aura.select(service.on_exit()).then(|_| Ok(())); + service.spawn_task(Box::new(select)); } - let local_key = if service.config.disable_grandpa { + let grandpa_key = if service.config.disable_grandpa { None } else { - local_key + service.fg_authority_key() }; let config = grandpa::Config { - local_key, + local_key: grandpa_key.map(Arc::new), // FIXME #1578 make this available through chainspec gossip_duration: Duration::from_millis(333), justification_period: 4096, @@ -124,19 +128,18 @@ construct_service_factory! { }; match config.local_key { - None => { - executor.spawn(grandpa::run_grandpa_observer( + None if !service.config.grandpa_voter => { + service.spawn_task(Box::new(grandpa::run_grandpa_observer( config, link_half, service.network(), service.on_exit(), - )?); + )?)); }, - Some(_) => { + // Either config.local_key is set, or user forced voter service via `--grandpa-voter` flag. + _ => { let telemetry_on_connect = TelemetryOnConnect { - on_exit: Box::new(service.on_exit()), telemetry_connection_sinks: service.telemetry_on_connect_stream(), - executor: &executor, }; let grandpa_config = grandpa::GrandpaParams { config: config, @@ -146,7 +149,7 @@ construct_service_factory! { on_exit: service.on_exit(), telemetry_on_connect: Some(telemetry_on_connect), }; - executor.spawn(grandpa::run_grandpa_voter(grandpa_config)?); + service.spawn_task(Box::new(grandpa::run_grandpa_voter(grandpa_config)?)); }, } @@ -154,7 +157,7 @@ construct_service_factory! { } }, LightService = LightComponents - { |config, executor| >::new(config, executor) }, + { |config| >::new(config) }, FullImportQueue = AuraImportQueue { |config: &mut FactoryFullConfiguration , client: Arc>, select_chain: Self::SelectChain| { let slot_duration = SlotDuration::get_or_compute(&*client)?; @@ -162,16 +165,14 @@ construct_service_factory! { grandpa::block_import::<_, _, _, RuntimeApi, FullClient, _>( client.clone(), client.clone(), select_chain )?; - let block_import = Arc::new(block_import); let justification_import = block_import.clone(); config.custom.grandpa_import_setup = Some((block_import.clone(), link_half)); - import_queue::<_, _, ed25519::Pair>( + import_queue::<_, _, AuraPair>( slot_duration, - block_import, - Some(justification_import), - None, + Box::new(block_import), + Some(Box::new(justification_import)), None, client, config.custom.inherent_data_providers.clone(), @@ -187,19 +188,17 @@ construct_service_factory! { let block_import = grandpa::light_block_import::<_, _, _, RuntimeApi, LightClient>( client.clone(), Arc::new(fetch_checker), client.clone() )?; - let block_import = Arc::new(block_import); let finality_proof_import = block_import.clone(); let finality_proof_request_builder = finality_proof_import.create_finality_proof_request_builder(); - import_queue::<_, _, ed25519::Pair>( + import_queue::<_, _, AuraPair>( SlotDuration::get_or_compute(&*client)?, - block_import, + Box::new(block_import), None, - Some(finality_proof_import), - Some(finality_proof_request_builder), + Some(Box::new(finality_proof_import)), client, config.custom.inherent_data_providers.clone(), - ).map_err(Into::into) + ).map(|q| (q, finality_proof_request_builder)).map_err(Into::into) }}, SelectChain = LongestChain, Self::Block> { |config: &FactoryFullConfiguration, client: Arc>| { @@ -217,11 +216,11 @@ construct_service_factory! { #[cfg(test)] mod tests { use std::sync::Arc; - use consensus::CompatibleDigestItem; - use consensus_common::{Environment, Proposer, ImportBlock, BlockOrigin, ForkChoiceStrategy}; + use aura::CompatibleDigestItem; + use consensus_common::{Environment, Proposer, BlockImportParams, BlockOrigin, ForkChoiceStrategy}; use node_primitives::DigestItem; - use node_runtime::{Call, BalancesCall, UncheckedExtrinsic}; - use parity_codec::{Compact, Encode, Decode}; + use node_runtime::{BalancesCall, Call, CENTS, SECS_PER_BLOCK, UncheckedExtrinsic}; + use parity_codec::{Encode, Decode}; use primitives::{ crypto::Pair as CryptoPair, ed25519::Pair, blake2_256, sr25519::Public as AddressPublic, H256, @@ -231,12 +230,13 @@ mod tests { use finality_tracker; use keyring::{ed25519::Keyring as AuthorityKeyring, sr25519::Keyring as AccountKeyring}; use substrate_service::ServiceFactory; + use service_test::SyncService; use crate::service::Factory; #[cfg(feature = "rhd")] fn test_sync() { use {service_test, Factory}; - use client::{ImportBlock, BlockOrigin}; + use client::{BlockImportParams, BlockOrigin}; let alice: Arc = Arc::new(Keyring::Alice.into()); let bob: Arc = Arc::new(Keyring::Bob.into()); @@ -256,7 +256,7 @@ mod tests { }; let (proposer, _, _) = proposer_factory.init(&parent_header, &validators, alice.clone()).unwrap(); let block = proposer.propose().expect("Error making test block"); - ImportBlock { + BlockImportParams { origin: BlockOrigin::File, justification: Vec::new(), internal_justification: Vec::new(), @@ -266,8 +266,13 @@ mod tests { auxiliary: Vec::new(), } }; - let extrinsic_factory = |service: &::FullService| { - let payload = (0, Call::Balances(BalancesCall::transfer(RawAddress::Id(bob.public().0.into()), 69.into())), Era::immortal(), service.client().genesis_hash()); + let extrinsic_factory = |service: &SyncService<::FullService>| { + let payload = ( + 0, + Call::Balances(BalancesCall::transfer(RawAddress::Id(bob.public().0.into()), 69.into())), + Era::immortal(), + service.client().genesis_hash() + ); let signature = alice.sign(&payload.encode()).into(); let id = alice.public().0.into(); let xt = UncheckedExtrinsic { @@ -277,7 +282,11 @@ mod tests { let v: Vec = Decode::decode(&mut xt.as_slice()).unwrap(); OpaqueExtrinsic(v) }; - service_test::sync::(chain_spec::integration_test_config(), block_factory, extrinsic_factory); + service_test::sync::( + chain_spec::integration_test_config(), + block_factory, + extrinsic_factory, + ); } #[test] @@ -287,11 +296,16 @@ mod tests { let alice = Arc::new(AuthorityKeyring::Alice.pair()); let mut slot_num = 1u64; - let block_factory = |service: &::FullService| { - let mut inherent_data = service.config.custom.inherent_data_providers - .create_inherent_data().unwrap(); + let block_factory = |service: &SyncService<::FullService>| { + let service = service.get(); + let mut inherent_data = service + .config + .custom + .inherent_data_providers + .create_inherent_data() + .expect("Creates inherent data."); inherent_data.replace_data(finality_tracker::INHERENT_IDENTIFIER, &1u64); - inherent_data.replace_data(timestamp::INHERENT_IDENTIFIER, &(slot_num * 10)); + inherent_data.replace_data(timestamp::INHERENT_IDENTIFIER, &(slot_num * SECS_PER_BLOCK)); let parent_id = BlockId::number(service.client().info().chain.best_number); let parent_header = service.client().header(&parent_id).unwrap().unwrap(); @@ -299,13 +313,14 @@ mod tests { client: service.client(), transaction_pool: service.transaction_pool(), }); + let mut digest = Digest::::default(); - digest.push(>::aura_pre_digest(slot_num * 10 / 2)); + digest.push(>::aura_pre_digest(slot_num)); let proposer = proposer_factory.init(&parent_header).unwrap(); let new_block = proposer.propose( inherent_data, digest, - ::std::time::Duration::from_secs(1), + std::time::Duration::from_secs(1), ).expect("Error making test block"); let (new_header, new_body) = new_block.deconstruct(); @@ -319,7 +334,7 @@ mod tests { ); slot_num += 1; - ImportBlock { + BlockImportParams { origin: BlockOrigin::File, header: new_header, justification: None, @@ -335,27 +350,32 @@ mod tests { let charlie = Arc::new(AccountKeyring::Charlie.pair()); let mut index = 0; - let extrinsic_factory = |service: &::FullService| { - let amount = 1000; + let extrinsic_factory = |service: &SyncService<::FullService>| { + let amount = 5 * CENTS; let to = AddressPublic::from_raw(bob.public().0); let from = AddressPublic::from_raw(charlie.public().0); - let genesis_hash = service.client().block_hash(0).unwrap().unwrap(); + let genesis_hash = service.get().client().block_hash(0).unwrap().unwrap(); let signer = charlie.clone(); let function = Call::Balances(BalancesCall::transfer(to.into(), amount)); - let era = Era::immortal(); - let raw_payload = (Compact(index), function, era, genesis_hash); + + let check_era = system::CheckEra::from(Era::Immortal); + let check_nonce = system::CheckNonce::from(index); + let check_weight = system::CheckWeight::from(); + let take_fees = balances::TakeFees::from(0); + let extra = (check_era, check_nonce, check_weight, take_fees); + + let raw_payload = (function, extra.clone(), genesis_hash); let signature = raw_payload.using_encoded(|payload| if payload.len() > 256 { signer.sign(&blake2_256(payload)[..]) } else { signer.sign(payload) }); let xt = UncheckedExtrinsic::new_signed( - index, - raw_payload.1, + raw_payload.0, from.into(), signature.into(), - era, + extra, ).encode(); let v: Vec = Decode::decode(&mut xt.as_slice()).unwrap(); diff --git a/node/executor/Cargo.toml b/node/executor/Cargo.toml index a5e5958f6a67ec3490109ebee243acd0639d0167..345401141913c781e8970f136a9ff7fecbc12c96 100644 --- a/node/executor/Cargo.toml +++ b/node/executor/Cargo.toml @@ -6,8 +6,8 @@ description = "Substrate node implementation in Rust." edition = "2018" [dependencies] -trie-root = "0.12" -parity-codec = "3.3" +trie-root = "0.14.0" +parity-codec = "4.1.1" runtime_io = { package = "sr-io", path = "../../core/sr-io" } state_machine = { package = "substrate-state-machine", path = "../../core/state-machine" } substrate-executor = { path = "../../core/executor" } diff --git a/node/executor/src/lib.rs b/node/executor/src/lib.rs index 5f84085749d208771274aba4c3f45e4aebac8084..b55461492625da305e5f4a7b15e6a6d71918ec35 100644 --- a/node/executor/src/lib.rs +++ b/node/executor/src/lib.rs @@ -30,9 +30,7 @@ native_executor_instance!( pub Executor, node_runtime::api::dispatch, node_runtime::native_version, - include_bytes!( - "../../runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm" - ) + node_runtime::WASM_BINARY ); #[cfg(test)] @@ -42,19 +40,26 @@ mod tests { use substrate_executor::{WasmExecutor, NativeExecutionDispatch}; use parity_codec::{Encode, Decode, Joiner}; use keyring::{AuthorityKeyring, AccountKeyring}; - use runtime_support::{Hashable, StorageValue, StorageMap, traits::Currency}; + use runtime_support::{Hashable, StorageValue, StorageMap, traits::{Currency, Get}}; use state_machine::{CodeExecutor, Externalities, TestExternalities as CoreTestExternalities}; - use primitives::{twox_128, blake2_256, Blake2Hasher, ChangesTrieConfiguration, NeverNativeValue, - NativeOrEncoded}; - use node_primitives::{Hash, BlockNumber, AccountId}; + use primitives::{ + twox_128, blake2_256, Blake2Hasher, ChangesTrieConfiguration, NeverNativeValue, + NativeOrEncoded + }; + use node_primitives::{Hash, BlockNumber, AccountId, Balance, Index}; use runtime_primitives::traits::{Header as HeaderT, Hash as HashT}; use runtime_primitives::{generic::Era, ApplyOutcome, ApplyError, ApplyResult, Perbill}; - use {balances, indices, system, staking, timestamp, treasury, contracts}; + use runtime_primitives::weights::{WeightMultiplier, SimpleDispatchInfo, WeighData}; + use {balances, contracts, indices, staking, system, timestamp}; use contracts::ContractAddressFor; use system::{EventRecord, Phase}; - use node_runtime::{Header, Block, UncheckedExtrinsic, CheckedExtrinsic, Call, Runtime, Balances, - BuildStorage, GenesisConfig, BalancesConfig, SessionConfig, StakingConfig, System, - SystemConfig, GrandpaConfig, IndicesConfig, Event, SessionKeys}; + use node_runtime::{ + Header, Block, UncheckedExtrinsic, CheckedExtrinsic, Call, Runtime, Balances, BuildStorage, + GenesisConfig, BalancesConfig, SessionConfig, StakingConfig, System, SystemConfig, + GrandpaConfig, IndicesConfig, ContractsConfig, Event, SessionKeys, CreationFee, + CENTS, DOLLARS, MILLICENTS, SignedExtra, TransactionBaseFee, TransactionByteFee, + MaximumBlockWeight, + }; use wabt; use primitives::map; @@ -64,21 +69,35 @@ mod tests { /// making the binary slimmer. There is a convention to use compact version of the runtime /// as canonical. This is why `native_executor_instance` also uses the compact version of the /// runtime. - const COMPACT_CODE: &[u8] = - include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm"); + const COMPACT_CODE: &[u8] = node_runtime::WASM_BINARY; /// The wasm runtime binary which hasn't undergone the compacting process. /// /// The idea here is to pass it as the current runtime code to the executor so the executor will /// have to execute provided wasm code instead of the native equivalent. This trick is used to /// test code paths that differ between native and wasm versions. - const BLOATY_CODE: &[u8] = - include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.wasm"); + const BLOATY_CODE: &[u8] = node_runtime::WASM_BINARY_BLOATY; const GENESIS_HASH: [u8; 32] = [69u8; 32]; type TestExternalities = CoreTestExternalities; + fn transfer_fee(extrinsic: &E) -> Balance { + let length_fee = >::get() + + >::get() * + (extrinsic.encode().len() as Balance); + let weight_fee = SimpleDispatchInfo::default().weigh_data(()) as Balance; + length_fee + weight_fee + } + + fn multiplier_ideal() -> u32 { + >::get() / 4 / 4 + } + + fn creation_fee() -> Balance { + >::get() + } + fn alice() -> AccountId { AccountKeyring::Alice.into() } @@ -105,9 +124,8 @@ mod tests { fn sign(xt: CheckedExtrinsic) -> UncheckedExtrinsic { match xt.signed { - Some((signed, index)) => { - let era = Era::mortal(256, 0); - let payload = (index.into(), xt.function, era, GENESIS_HASH); + Some((signed, extra)) => { + let payload = (xt.function, extra.clone(), GENESIS_HASH); let key = AccountKeyring::from_public(&signed).unwrap(); let signature = payload.using_encoded(|b| { if b.len() > 256 { @@ -117,8 +135,8 @@ mod tests { } }).into(); UncheckedExtrinsic { - signature: Some((indices::address::Address::Id(signed), signature, payload.0, era)), - function: payload.1, + signature: Some((indices::address::Address::Id(signed), signature, extra)), + function: payload.0, } } None => UncheckedExtrinsic { @@ -128,10 +146,19 @@ mod tests { } } + fn signed_extra(nonce: Index, extra_fee: Balance) -> SignedExtra { + ( + system::CheckEra::from(Era::mortal(256, 0)), + system::CheckNonce::from(nonce), + system::CheckWeight::from(), + balances::TakeFees::from(extra_fee) + ) + } + fn xt() -> UncheckedExtrinsic { sign(CheckedExtrinsic { - signed: Some((alice(), 0)), - function: Call::Balances(balances::Call::transfer::(bob().into(), 69)), + signed: Some((alice(), signed_extra(0, 0))), + function: Call::Balances(balances::Call::transfer::(bob().into(), 69 * DOLLARS)), }) } @@ -140,38 +167,23 @@ mod tests { } fn executor() -> ::substrate_executor::NativeExecutor { - ::substrate_executor::NativeExecutor::new(None) + substrate_executor::NativeExecutor::new(None) } #[test] fn panic_execution_with_foreign_code_gives_error() { let mut t = TestExternalities::::new_with_code(BLOATY_CODE, map![ blake2_256(&>::key_for(alice())).to_vec() => { - vec![69u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 69_u128.encode() }, twox_128(>::key()).to_vec() => { - vec![69u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - }, - twox_128(>::key()).to_vec() => { - vec![0u8; 16] - }, - twox_128(>::key()).to_vec() => { - vec![0u8; 16] - }, - twox_128(>::key()).to_vec() => { - vec![0u8; 16] + 69_u128.encode() }, twox_128(>::key()).to_vec() => { - vec![0u8; 16] + 0_u128.encode() }, blake2_256(&>::key_for(0)).to_vec() => { vec![0u8; 32] - }, - twox_128(>::key()).to_vec() => { - vec![70u8; 16] - }, - twox_128(>::key()).to_vec() => { - vec![0u8; 16] } ]); @@ -198,31 +210,16 @@ mod tests { fn bad_extrinsic_with_native_equivalent_code_gives_error() { let mut t = TestExternalities::::new_with_code(COMPACT_CODE, map![ blake2_256(&>::key_for(alice())).to_vec() => { - vec![69u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 69_u128.encode() }, twox_128(>::key()).to_vec() => { - vec![69u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - }, - twox_128(>::key()).to_vec() => { - vec![0u8; 16] - }, - twox_128(>::key()).to_vec() => { - vec![0u8; 16] - }, - twox_128(>::key()).to_vec() => { - vec![0u8; 16] + 69_u128.encode() }, twox_128(>::key()).to_vec() => { - vec![0u8; 16] + 0_u128.encode() }, blake2_256(&>::key_for(0)).to_vec() => { vec![0u8; 32] - }, - twox_128(>::key()).to_vec() => { - vec![70u8; 16] - }, - twox_128(>::key()).to_vec() => { - vec![0u8; 16] } ]); @@ -249,18 +246,13 @@ mod tests { fn successful_execution_with_native_equivalent_code_gives_ok() { let mut t = TestExternalities::::new_with_code(COMPACT_CODE, map![ blake2_256(&>::key_for(alice())).to_vec() => { - vec![111u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + (111 * DOLLARS).encode() }, twox_128(>::key()).to_vec() => { - vec![111u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + (111 * DOLLARS).encode() }, - twox_128(>::key()).to_vec() => vec![0u8; 16], - twox_128(>::key()).to_vec() => vec![0u8; 16], - twox_128(>::key()).to_vec() => vec![0u8; 16], twox_128(>::key()).to_vec() => vec![0u8; 16], - blake2_256(&>::key_for(0)).to_vec() => vec![0u8; 32], - twox_128(>::key()).to_vec() => vec![0u8; 16], - twox_128(>::key()).to_vec() => vec![0u8; 16] + blake2_256(&>::key_for(0)).to_vec() => vec![0u8; 32] ]); let r = executor().call::<_, NeverNativeValue, fn() -> _>( @@ -281,8 +273,8 @@ mod tests { assert!(r.is_ok()); runtime_io::with_externalities(&mut t, || { - assert_eq!(Balances::total_balance(&alice()), 42); - assert_eq!(Balances::total_balance(&bob()), 69); + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt()) - creation_fee()); + assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); }); } @@ -290,18 +282,13 @@ mod tests { fn successful_execution_with_foreign_code_gives_ok() { let mut t = TestExternalities::::new_with_code(BLOATY_CODE, map![ blake2_256(&>::key_for(alice())).to_vec() => { - vec![111u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + (111 * DOLLARS).encode() }, twox_128(>::key()).to_vec() => { - vec![111u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + (111 * DOLLARS).encode() }, - twox_128(>::key()).to_vec() => vec![0u8; 16], - twox_128(>::key()).to_vec() => vec![0u8; 16], - twox_128(>::key()).to_vec() => vec![0u8; 16], twox_128(>::key()).to_vec() => vec![0u8; 16], - blake2_256(&>::key_for(0)).to_vec() => vec![0u8; 32], - twox_128(>::key()).to_vec() => vec![0u8; 16], - twox_128(>::key()).to_vec() => vec![0u8; 16] + blake2_256(&>::key_for(0)).to_vec() => vec![0u8; 32] ]); let r = executor().call::<_, NeverNativeValue, fn() -> _>( @@ -322,18 +309,19 @@ mod tests { assert!(r.is_ok()); runtime_io::with_externalities(&mut t, || { - assert_eq!(Balances::total_balance(&alice()), 42); - assert_eq!(Balances::total_balance(&bob()), 69); + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt()) - creation_fee()); + assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); }); } fn to_session_keys(ring: &AuthorityKeyring) -> SessionKeys { - SessionKeys(ring.to_owned().into(), ring.to_owned().into()) + SessionKeys { + ed25519: ring.to_owned().into(), + } } fn new_test_ext(code: &[u8], support_changes_trie: bool) -> TestExternalities { - let three = AccountId::from_raw([3u8; 32]); - let mut ext = TestExternalities::new_with_code(code, GenesisConfig { + let mut ext = TestExternalities::new_with_code_with_children(code, GenesisConfig { aura: Some(Default::default()), system: Some(SystemConfig { changes_trie_config: if support_changes_trie { Some(ChangesTrieConfiguration { @@ -346,23 +334,17 @@ mod tests { ids: vec![alice(), bob(), charlie(), dave(), eve(), ferdie()], }), balances: Some(BalancesConfig { - transaction_base_fee: 1, - transaction_byte_fee: 0, balances: vec![ - (alice(), 111), - (bob(), 100), - (charlie(), 100_000_000), - (dave(), 111), - (eve(), 101), - (ferdie(), 100), + (alice(), 111 * DOLLARS), + (bob(), 100 * DOLLARS), + (charlie(), 100_000_000 * DOLLARS), + (dave(), 111 * DOLLARS), + (eve(), 101 * DOLLARS), + (ferdie(), 100 * DOLLARS), ], - existential_deposit: 0, - transfer_fee: 0, - creation_fee: 0, vesting: vec![], }), session: Some(SessionConfig { - validators: vec![AccountKeyring::One.into(), AccountKeyring::Two.into(), three], keys: vec![ (alice(), to_session_keys(&AuthorityKeyring::Alice)), (bob(), to_session_keys(&AuthorityKeyring::Bob)), @@ -372,9 +354,9 @@ mod tests { staking: Some(StakingConfig { current_era: 0, stakers: vec![ - (dave(), alice(), 111, staking::StakerStatus::Validator), - (eve(), bob(), 100, staking::StakerStatus::Validator), - (ferdie(), charlie(), 100, staking::StakerStatus::Validator) + (dave(), alice(), 111 * DOLLARS, staking::StakerStatus::Validator), + (eve(), bob(), 100 * DOLLARS, staking::StakerStatus::Validator), + (ferdie(), charlie(), 100 * DOLLARS, staking::StakerStatus::Validator) ], validator_count: 3, minimum_validator_count: 0, @@ -385,16 +367,19 @@ mod tests { invulnerables: vec![alice(), bob(), charlie()], }), democracy: Some(Default::default()), - council_seats: Some(Default::default()), - timestamp: Some(Default::default()), - treasury: Some(Default::default()), - contracts: Some(Default::default()), + collective_Instance1: Some(Default::default()), + collective_Instance2: Some(Default::default()), + elections: Some(Default::default()), + contracts: Some(ContractsConfig { + current_schedule: Default::default(), + gas_price: 1 * MILLICENTS, + }), sudo: Some(Default::default()), + im_online: Some(Default::default()), grandpa: Some(GrandpaConfig { - _genesis_phantom_data: Default::default(), authorities: vec![], }), - }.build_storage().unwrap().0); + }.build_storage().unwrap()); ext.changes_trie_storage().insert(0, GENESIS_HASH.into(), Default::default()); ext } @@ -469,8 +454,8 @@ mod tests { function: Call::Timestamp(timestamp::Call::set(42)), }, CheckedExtrinsic { - signed: Some((alice(), 0)), - function: Call::Balances(balances::Call::transfer(bob().into(), 69)), + signed: Some((alice(), signed_extra(0, 0))), + function: Call::Balances(balances::Call::transfer(bob().into(), 69 * DOLLARS)), }, ] ) @@ -491,8 +476,8 @@ mod tests { function: Call::Timestamp(timestamp::Call::set(42)), }, CheckedExtrinsic { - signed: Some((alice(), 0)), - function: Call::Balances(balances::Call::transfer(bob().into(), 69)), + signed: Some((alice(), signed_extra(0, 0))), + function: Call::Balances(balances::Call::transfer(bob().into(), 69 * DOLLARS)), }, ] ); @@ -506,12 +491,12 @@ mod tests { function: Call::Timestamp(timestamp::Call::set(52)), }, CheckedExtrinsic { - signed: Some((bob(), 0)), - function: Call::Balances(balances::Call::transfer(alice().into(), 5)), + signed: Some((bob(), signed_extra(0, 0))), + function: Call::Balances(balances::Call::transfer(alice().into(), 5 * DOLLARS)), }, CheckedExtrinsic { - signed: Some((alice(), 1)), - function: Call::Balances(balances::Call::transfer(bob().into(), 15)), + signed: Some((alice(), signed_extra(1, 0))), + function: Call::Balances(balances::Call::transfer(bob().into(), 15 * DOLLARS)), } ] ); @@ -519,12 +504,11 @@ mod tests { // session change => consensus authorities change => authorities change digest item appears let digest = Header::decode(&mut &block2.0[..]).unwrap().digest; assert_eq!(digest.logs().len(), 0); -// assert!(digest.logs()[0].as_consensus().is_some()); (block1, block2) } - fn big_block() -> (Vec, Hash) { + fn block_with_size(time: u64, nonce: u64, size: usize) -> (Vec, Hash) { construct_block( &mut new_test_ext(COMPACT_CODE, false), 1, @@ -532,11 +516,11 @@ mod tests { vec![ CheckedExtrinsic { signed: None, - function: Call::Timestamp(timestamp::Call::set(42)), + function: Call::Timestamp(timestamp::Call::set(time)), }, CheckedExtrinsic { - signed: Some((alice(), 0)), - function: Call::System(system::Call::remark(vec![0; 120000])), + signed: Some((alice(), signed_extra(nonce, 0))), + function: Call::System(system::Call::remark(vec![0; size])), } ] ) @@ -557,11 +541,9 @@ mod tests { ).0.unwrap(); runtime_io::with_externalities(&mut t, || { - // block1 transfers from alice 69 to bob. - // -1 is the default fee - assert_eq!(Balances::total_balance(&alice()), 111 - 69 - 1); - assert_eq!(Balances::total_balance(&bob()), 100 + 69); - assert_eq!(System::events(), vec![ + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt()) - creation_fee()); + assert_eq!(Balances::total_balance(&bob()), 169 * DOLLARS); + let events = vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), event: Event::system(system::Event::ExtrinsicSuccess), @@ -572,8 +554,8 @@ mod tests { event: Event::balances(balances::RawEvent::Transfer( alice().into(), bob().into(), - 69, - 0 + 69 * DOLLARS, + 1 * CENTS )), topics: vec![], }, @@ -582,24 +564,9 @@ mod tests { event: Event::system(system::Event::ExtrinsicSuccess), topics: vec![], }, - EventRecord { - phase: Phase::Finalization, - event: Event::treasury(treasury::RawEvent::Spending(0)), - topics: vec![], - }, - EventRecord { - phase: Phase::Finalization, - event: Event::treasury(treasury::RawEvent::Burnt(0)), - topics: vec![], - }, - EventRecord { - phase: Phase::Finalization, - event: Event::treasury(treasury::RawEvent::Rollover(0)), - topics: vec![], - }, - ]); + ]; + assert_eq!(System::events(), events); }); - executor().call::<_, NeverNativeValue, fn() -> _>( &mut t, "Core_execute_block", @@ -609,12 +576,10 @@ mod tests { ).0.unwrap(); runtime_io::with_externalities(&mut t, || { - // bob sends 5, alice sends 15 | bob += 10, alice -= 10 - // 111 - 69 - 1 - 10 - 1 = 30 - assert_eq!(Balances::total_balance(&alice()), 111 - 69 - 1 - 10 - 1); - // 100 + 69 + 10 - 1 = 178 - assert_eq!(Balances::total_balance(&bob()), 100 + 69 + 10 - 1); - assert_eq!(System::events(), vec![ + // TODO TODO: this needs investigating: why are we deducting creation fee twice here? and why bob also pays it? + assert_eq!(Balances::total_balance(&alice()), 32 * DOLLARS - 2 * transfer_fee(&xt()) - 2 * creation_fee()); + assert_eq!(Balances::total_balance(&bob()), 179 * DOLLARS - transfer_fee(&xt()) - creation_fee()); + let events = vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), event: Event::system(system::Event::ExtrinsicSuccess), @@ -626,8 +591,8 @@ mod tests { balances::RawEvent::Transfer( bob().into(), alice().into(), - 5, - 0 + 5 * DOLLARS, + 1 * CENTS, ) ), topics: vec![], @@ -643,8 +608,8 @@ mod tests { balances::RawEvent::Transfer( alice().into(), bob().into(), - 15, - 0 + 15 * DOLLARS, + 1 * CENTS, ) ), topics: vec![], @@ -654,22 +619,8 @@ mod tests { event: Event::system(system::Event::ExtrinsicSuccess), topics: vec![], }, - EventRecord { - phase: Phase::Finalization, - event: Event::treasury(treasury::RawEvent::Spending(0)), - topics: vec![], - }, - EventRecord { - phase: Phase::Finalization, - event: Event::treasury(treasury::RawEvent::Burnt(0)), - topics: vec![], - }, - EventRecord { - phase: Phase::Finalization, - event: Event::treasury(treasury::RawEvent::Rollover(0)), - topics: vec![], - }, - ]); + ]; + assert_eq!(System::events(), events); }); } @@ -682,20 +633,15 @@ mod tests { WasmExecutor::new().call(&mut t, 8, COMPACT_CODE, "Core_execute_block", &block1.0).unwrap(); runtime_io::with_externalities(&mut t, || { - // block1 transfers from alice 69 to bob. - // -1 is the default fee - assert_eq!(Balances::total_balance(&alice()), 111 - 69 - 1); - assert_eq!(Balances::total_balance(&bob()), 100 + 69); + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt()) - creation_fee()); + assert_eq!(Balances::total_balance(&bob()), 169 * DOLLARS); }); WasmExecutor::new().call(&mut t, 8, COMPACT_CODE, "Core_execute_block", &block2.0).unwrap(); runtime_io::with_externalities(&mut t, || { - // bob sends 5, alice sends 15 | bob += 10, alice -= 10 - // 111 - 69 - 1 - 10 - 1 = 30 - assert_eq!(Balances::total_balance(&alice()), 111 - 69 - 1 - 10 - 1); - // 100 + 69 + 10 - 1 = 178 - assert_eq!(Balances::total_balance(&bob()), 100 + 69 + 10 - 1); + assert_eq!(Balances::total_balance(&alice()), 32 * DOLLARS - 2 * transfer_fee(&xt()) - 2 * creation_fee()); + assert_eq!(Balances::total_balance(&bob()), 179 * DOLLARS - 1 * transfer_fee(&xt()) - creation_fee()); }); } @@ -793,7 +739,6 @@ mod tests { #[test] fn deploying_wasm_contract_should_work() { - let transfer_code = wabt::wat2wasm(CODE_TRANSFER).unwrap(); let transfer_ch = ::Hashing::hash(&transfer_code); @@ -813,19 +758,19 @@ mod tests { function: Call::Timestamp(timestamp::Call::set(42)), }, CheckedExtrinsic { - signed: Some((charlie(), 0)), + signed: Some((charlie(), signed_extra(0, 0))), function: Call::Contracts( contracts::Call::put_code::(10_000, transfer_code) ), }, CheckedExtrinsic { - signed: Some((charlie(), 1)), + signed: Some((charlie(), signed_extra(1, 0))), function: Call::Contracts( - contracts::Call::create::(10, 10_000, transfer_ch, Vec::new()) + contracts::Call::create::(1 * DOLLARS, 10_000, transfer_ch, Vec::new()) ), }, CheckedExtrinsic { - signed: Some((charlie(), 2)), + signed: Some((charlie(), signed_extra(2, 0))), function: Call::Contracts( contracts::Call::call::( indices::address::Address::Id(addr.clone()), @@ -864,7 +809,7 @@ mod tests { 4, COMPACT_CODE, "Core_execute_block", - &big_block().0 + &block_with_size(42, 0, 120_000).0 ).is_err() ); } @@ -876,7 +821,7 @@ mod tests { Executor::new(None).call::<_, NeverNativeValue, fn() -> _>( &mut t, "Core_execute_block", - &big_block().0, + &block_with_size(42, 0, 120_000).0, true, None, ).0.unwrap(); @@ -890,7 +835,7 @@ mod tests { Executor::new(None).call::<_, NeverNativeValue, fn() -> _>( &mut t, "Core_execute_block", - &big_block().0, + &block_with_size(42, 0, 120_000).0, false, None, ).0.is_err() @@ -901,18 +846,13 @@ mod tests { fn panic_execution_gives_error() { let mut t = TestExternalities::::new_with_code(BLOATY_CODE, map![ blake2_256(&>::key_for(alice())).to_vec() => { - vec![69u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0_u128.encode() }, twox_128(>::key()).to_vec() => { - vec![69u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0_u128.encode() }, - twox_128(>::key()).to_vec() => vec![0u8; 16], - twox_128(>::key()).to_vec() => vec![0u8; 16], - twox_128(>::key()).to_vec() => vec![0u8; 16], twox_128(>::key()).to_vec() => vec![0u8; 16], - blake2_256(&>::key_for(0)).to_vec() => vec![0u8; 32], - twox_128(>::key()).to_vec() => vec![70u8; 16], - twox_128(>::key()).to_vec() => vec![0u8; 16] + blake2_256(&>::key_for(0)).to_vec() => vec![0u8; 32] ]); let r = WasmExecutor::new() @@ -928,18 +868,13 @@ mod tests { fn successful_execution_gives_ok() { let mut t = TestExternalities::::new_with_code(COMPACT_CODE, map![ blake2_256(&>::key_for(alice())).to_vec() => { - vec![111u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + (111 * DOLLARS).encode() }, twox_128(>::key()).to_vec() => { - vec![111u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + (111 * DOLLARS).encode() }, - twox_128(>::key()).to_vec() => vec![0u8; 16], - twox_128(>::key()).to_vec() => vec![0u8; 16], - twox_128(>::key()).to_vec() => vec![0u8; 16], twox_128(>::key()).to_vec() => vec![0u8; 16], - blake2_256(&>::key_for(0)).to_vec() => vec![0u8; 32], - twox_128(>::key()).to_vec() => vec![0u8; 16], - twox_128(>::key()).to_vec() => vec![0u8; 16] + blake2_256(&>::key_for(0)).to_vec() => vec![0u8; 32] ]); let r = WasmExecutor::new() @@ -951,8 +886,8 @@ mod tests { assert_eq!(r, Ok(ApplyOutcome::Success)); runtime_io::with_externalities(&mut t, || { - assert_eq!(Balances::total_balance(&alice()), 42); - assert_eq!(Balances::total_balance(&bob()), 69); + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - 1 * transfer_fee(&xt()) - creation_fee()); + assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); }); } @@ -979,8 +914,7 @@ mod tests { let block1 = changes_trie_block(); let mut t = new_test_ext(COMPACT_CODE, true); - WasmExecutor::new() - .call(&mut t, 8, COMPACT_CODE, "Core_execute_block", &block1.0).unwrap(); + WasmExecutor::new().call(&mut t, 8, COMPACT_CODE, "Core_execute_block", &block1.0).unwrap(); assert!(t.storage_changes_root(GENESIS_HASH.into()).unwrap().is_some()); } @@ -1000,6 +934,91 @@ mod tests { client.import(BlockOrigin::Own, block).unwrap(); } + + #[test] + #[ignore] + fn weight_multiplier_increases_and_decreases_on_big_weight() { + let mut t = new_test_ext(COMPACT_CODE, false); + + let mut prev_multiplier = WeightMultiplier::default(); + + runtime_io::with_externalities(&mut t, || { + assert_eq!(System::next_weight_multiplier(), prev_multiplier); + }); + + let mut tt = new_test_ext(COMPACT_CODE, false); + // NOTE: This assumes that system::remark has the default. + let num_to_exhaust = multiplier_ideal() * 2 / SimpleDispatchInfo::default().weigh_data(()); + println!("++ Generating {} transactions to fill {} weight units", num_to_exhaust, multiplier_ideal() * 2); + + let mut xts = (0..num_to_exhaust).map(|i| CheckedExtrinsic { + signed: Some((charlie(), signed_extra(i.into(), 0))), + function: Call::System(system::Call::remark(vec![0; 1])), + }).collect::>(); + xts.insert(0, CheckedExtrinsic { + signed: None, + function: Call::Timestamp(timestamp::Call::set(42)), + }); + + // big one in terms of weight. + let block1 = construct_block( + &mut tt, + 1, + GENESIS_HASH.into(), + xts + ); + + // small one in terms of weight. + let block2 = construct_block( + &mut tt, + 2, + block1.1.clone(), + vec![ + CheckedExtrinsic { + signed: None, + function: Call::Timestamp(timestamp::Call::set(52)), + }, + CheckedExtrinsic { + signed: Some((charlie(), signed_extra(num_to_exhaust.into(), 0))), + function: Call::System(system::Call::remark(vec![0; 1])), + } + ] + ); + + // execute a big block. + executor().call::<_, NeverNativeValue, fn() -> _>( + &mut t, + "Core_execute_block", + &block1.0, + true, + None, + ).0.unwrap(); + + // weight multiplier is increased for next block. + runtime_io::with_externalities(&mut t, || { + let fm = System::next_weight_multiplier(); + println!("After a big block: {:?} -> {:?}", prev_multiplier, fm); + assert!(fm > prev_multiplier); + prev_multiplier = fm; + }); + + // execute a big block. + executor().call::<_, NeverNativeValue, fn() -> _>( + &mut t, + "Core_execute_block", + &block2.0, + true, + None, + ).0.unwrap(); + + // weight multiplier is increased for next block. + runtime_io::with_externalities(&mut t, || { + let fm = System::next_weight_multiplier(); + println!("After a small block: {:?} -> {:?}", prev_multiplier, fm); + assert!(fm < prev_multiplier); + }); + } + #[cfg(feature = "benchmarks")] mod benches { use super::*; diff --git a/node/primitives/Cargo.toml b/node/primitives/Cargo.toml index db40d91346ec10b344d7083800598f99bf6742a0..c4be1ef6f22e780ebd507b4cbecdb15af345844a 100644 --- a/node/primitives/Cargo.toml +++ b/node/primitives/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" [dependencies] serde = { version = "1.0", optional = true, features = ["derive"] } -parity-codec = { version = "3.3", default-features = false, features = ["derive"] } +parity-codec = { version = "4.1.1", 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 } runtime_primitives = { package = "sr-primitives", path = "../../core/sr-primitives", default-features = false } diff --git a/node/primitives/src/lib.rs b/node/primitives/src/lib.rs index 8b7b2521e4000fc4c912023026dcc86d7f9b49e3..351bb4fa12ed5607c75c22018fb7076bc88cc991 100644 --- a/node/primitives/src/lib.rs +++ b/node/primitives/src/lib.rs @@ -41,12 +41,19 @@ pub type AccountIndex = u32; /// Balance of an account. pub type Balance = u128; -/// Alias to the signature scheme used for Aura authority signatures. -pub type AuraSignature = primitives::ed25519::Signature; +/// Type used for expressing timestamp. +pub type Moment = u64; + +/// The aura crypto scheme defined via the keypair type. +#[cfg(feature = "std")] +pub type AuraPair = primitives::ed25519::Pair; -/// The Ed25519 pub key of an session that belongs to an Aura authority of the chain. +/// Identity of an Aura authority. pub type AuraId = primitives::ed25519::Public; +/// Signature for an Aura authority. +pub type AuraSignature = primitives::ed25519::Signature; + /// Index of a transaction in the chain. pub type Index = u64; diff --git a/node/runtime/Cargo.toml b/node/runtime/Cargo.toml index f1981a585221eee50bc1f8e03e9551deade49546..17c666dbdc31254bb030ee2411ef121f59310603 100644 --- a/node/runtime/Cargo.toml +++ b/node/runtime/Cargo.toml @@ -3,11 +3,12 @@ name = "node-runtime" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +build = "build.rs" [dependencies] integer-sqrt = { version = "0.1.2" } safe-mix = { version = "1.0", default-features = false } -parity-codec = { version = "3.5.1", default-features = false, features = ["derive"] } +parity-codec = { version = "4.1.1", default-features = false, features = ["derive"] } substrate-primitives = { path = "../../core/primitives", default-features = false } client = { package = "substrate-client", path = "../../core/client", default-features = false } rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false } @@ -16,29 +17,35 @@ offchain-primitives = { package = "substrate-offchain-primitives", path = "../.. version = { package = "sr-version", path = "../../core/sr-version", default-features = false } support = { package = "srml-support", path = "../../srml/support", default-features = false } aura = { package = "srml-aura", path = "../../srml/aura", default-features = false } +authorship = { package = "srml-authorship", path = "../../srml/authorship", default-features = false } balances = { package = "srml-balances", path = "../../srml/balances", default-features = false } contracts = { package = "srml-contracts", path = "../../srml/contracts", default-features = false } -council = { package = "srml-council", path = "../../srml/council", default-features = false } +collective = { package = "srml-collective", path = "../../srml/collective", default-features = false } democracy = { package = "srml-democracy", path = "../../srml/democracy", default-features = false } +elections = { package = "srml-elections", path = "../../srml/elections", 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 } indices = { package = "srml-indices", path = "../../srml/indices", default-features = false } -session = { package = "srml-session", path = "../../srml/session", 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 } system = { package = "srml-system", path = "../../srml/system", default-features = false } timestamp = { package = "srml-timestamp", path = "../../srml/timestamp", default-features = false } treasury = { package = "srml-treasury", path = "../../srml/treasury", default-features = false } sudo = { package = "srml-sudo", path = "../../srml/sudo", default-features = false } +im-online = { package = "srml-im-online", path = "../../srml/im-online", default-features = false } node-primitives = { path = "../primitives", default-features = false } consensus_aura = { package = "substrate-consensus-aura-primitives", path = "../../core/consensus/aura/primitives", default-features = false } rustc-hex = { version = "2.0", optional = true } serde = { version = "1.0", optional = true } substrate-keyring = { path = "../../core/keyring", optional = true } +[build-dependencies] +wasm-builder-runner = { package = "substrate-wasm-builder-runner", version = "1.0.2", path = "../../core/utils/wasm-builder-runner" } + [features] default = ["std"] -core = [ +no_std = [ "contracts/core", ] std = [ @@ -48,10 +55,12 @@ std = [ "runtime_primitives/std", "support/std", "aura/std", + "authorship/std", "balances/std", "contracts/std", - "council/std", + "collective/std", "democracy/std", + "elections/std", "executive/std", "finality-tracker/std", "grandpa/std", @@ -71,4 +80,5 @@ std = [ "rustc-hex", "substrate-keyring", "offchain-primitives/std", + "im-online/std", ] diff --git a/node/runtime/build.rs b/node/runtime/build.rs new file mode 100644 index 0000000000000000000000000000000000000000..39aecacb20e0fcbe4d5c6ee639e531a6734b1eeb --- /dev/null +++ b/node/runtime/build.rs @@ -0,0 +1,27 @@ +// 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 wasm_builder_runner::{build_current_project, WasmBuilderSource}; + +fn main() { + build_current_project( + "wasm_binary.rs", + WasmBuilderSource::CratesOrPath { + path: "../../core/utils/wasm-builder", + version: "1.0.4", + }, + ); +} diff --git a/node/runtime/src/impls.rs b/node/runtime/src/impls.rs new file mode 100644 index 0000000000000000000000000000000000000000..85365b6a7e73e1b8d8ff2f97b972006114d3696a --- /dev/null +++ b/node/runtime/src/impls.rs @@ -0,0 +1,228 @@ +// 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 configurable implementations as associated type for the substrate runtime. + +use node_primitives::Balance; +use runtime_primitives::weights::{Weight, WeightMultiplier}; +use runtime_primitives::traits::{Convert, Saturating}; +use runtime_primitives::Fixed64; +use support::traits::Get; +use crate::{Balances, MaximumBlockWeight}; + +/// Struct that handles the conversion of Balance -> `u64`. This is used for staking's election +/// calculation. +pub struct CurrencyToVoteHandler; + +impl CurrencyToVoteHandler { + fn factor() -> Balance { (Balances::total_issuance() / u64::max_value() as Balance).max(1) } +} + +impl Convert for CurrencyToVoteHandler { + fn convert(x: Balance) -> u64 { (x / Self::factor()) as u64 } +} + +impl Convert for CurrencyToVoteHandler { + fn convert(x: u128) -> Balance { x * Self::factor() } +} + +/// 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. +/// +/// Formula: +/// diff = (ideal_weight - current_block_weight) +/// v = 0.00004 +/// next_weight = weight * (1 + (v . diff) + (v . diff)^2 / 2) +/// +/// https://research.web3.foundation/en/latest/polkadot/Token%20Economics/#relay-chain-transaction-fees +pub struct WeightMultiplierUpdateHandler; +impl Convert<(Weight, WeightMultiplier), WeightMultiplier> for WeightMultiplierUpdateHandler { + fn convert(previous_state: (Weight, WeightMultiplier)) -> WeightMultiplier { + let (block_weight, multiplier) = previous_state; + // CRITICAL NOTE: what the system module interprets as maximum block weight, and a portion + // of it (1/4 usually) as ideal weight demonstrate the gap in block weights for operational + // transactions. What this weight multiplier interprets as the maximum, is actually the + // maximum that is available to normal transactions. Hence, + let max_weight = >::get() / 4; + let ideal_weight = (max_weight / 4) as u128; + let block_weight = block_weight as u128; + + // determines if the first_term is positive + let positive = block_weight >= ideal_weight; + let diff_abs = block_weight.max(ideal_weight) - block_weight.min(ideal_weight); + // diff is within u32, safe. + let diff = Fixed64::from_rational(diff_abs as i64, max_weight as u64); + let diff_squared = diff.saturating_mul(diff); + + // 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. + let v_squared_2 = Fixed64::from_rational(1, 1_000_000_000); + + let first_term = v.saturating_mul(diff); + // It is very unlikely that this will exist (in our poor perbill estimate) but we are giving + // it a shot. + let second_term = v_squared_2.saturating_mul(diff_squared); + + if positive { + let excess = first_term.saturating_add(second_term); + multiplier.saturating_add(WeightMultiplier::from_fixed(excess)) + } else { + // first_term > second_term + let negative = first_term - second_term; + multiplier.saturating_sub(WeightMultiplier::from_fixed(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 are practically free. We stop here and only increase if the network + // became more busy. + .max(WeightMultiplier::from_rational(-1, 1)) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use runtime_primitives::weights::Weight; + use runtime_primitives::Perbill; + + fn max() -> Weight { + >::get() + } + + fn ideal() -> Weight { + max() / 4 / 4 + } + + // poc reference implementation. + #[allow(dead_code)] + fn weight_multiplier_update(block_weight: Weight) -> Perbill { + let block_weight = block_weight as f32; + let v: f32 = 0.00004; + + // maximum tx weight + let m = max() as f32; + // Ideal saturation in terms of weight + let ss = ideal() as f32; + // 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) + } + + fn fm(parts: i64) -> WeightMultiplier { + WeightMultiplier::from_parts(parts) + } + + #[test] + fn stateless_weight_mul() { + // Light block. Fee is reduced a little. + assert_eq!( + WeightMultiplierUpdateHandler::convert((ideal() / 4, WeightMultiplier::default())), + fm(-7500) + ); + // a bit more. Fee is decreased less, meaning that the fee increases as the block grows. + assert_eq!( + WeightMultiplierUpdateHandler::convert((ideal() / 2, WeightMultiplier::default())), + fm(-5000) + ); + // ideal. Original fee. No changes. + assert_eq!( + WeightMultiplierUpdateHandler::convert((ideal() as u32, WeightMultiplier::default())), + fm(0) + ); + // // More than ideal. Fee is increased. + assert_eq!( + WeightMultiplierUpdateHandler::convert(((ideal() * 2) as u32, WeightMultiplier::default())), + fm(10000) + ); + } + + #[test] + fn stateful_weight_mul_grow_to_infinity() { + assert_eq!( + WeightMultiplierUpdateHandler::convert((ideal() * 2, WeightMultiplier::default())), + fm(10000) + ); + assert_eq!( + WeightMultiplierUpdateHandler::convert((ideal() * 2, fm(10000))), + fm(20000) + ); + assert_eq!( + WeightMultiplierUpdateHandler::convert((ideal() * 2, fm(20000))), + fm(30000) + ); + // ... + assert_eq!( + WeightMultiplierUpdateHandler::convert((ideal() * 2, fm(1_000_000_000))), + fm(1_000_000_000 + 10000) + ); + } + + #[test] + fn stateful_weight_mil_collapse_to_minus_one() { + assert_eq!( + WeightMultiplierUpdateHandler::convert((0, WeightMultiplier::default())), + fm(-10000) + ); + assert_eq!( + WeightMultiplierUpdateHandler::convert((0, fm(-10000))), + fm(-20000) + ); + assert_eq!( + WeightMultiplierUpdateHandler::convert((0, fm(-20000))), + fm(-30000) + ); + // ... + assert_eq!( + WeightMultiplierUpdateHandler::convert((0, fm(1_000_000_000 * -1))), + fm(-1_000_000_000) + ); + } + + #[test] + fn weight_to_fee_should_not_overflow_on_large_weights() { + // defensive-only test. at the moment we are not allowing any weight more than + // 4 * 1024 * 1024 in a block. + let kb = 1024_u32; + 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())); + }); + + vec![10 * mb, Weight::max_value() / 2, Weight::max_value()] + .into_iter() + .for_each(|i| { + let fm = WeightMultiplierUpdateHandler::convert(( + i, + 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 cff2e39b4cd7eb9c1272ba9f8f1d862bbb4dafeb..2b8750d2de81a119422f5511eba62af29eb15ad6 100644 --- a/node/runtime/src/lib.rs +++ b/node/runtime/src/lib.rs @@ -21,45 +21,62 @@ #![recursion_limit="256"] use rstd::prelude::*; -use support::{construct_runtime, parameter_types}; +use support::{ + construct_runtime, parameter_types, traits::{SplitTwoWays, Currency, OnUnbalanced} +}; use substrate_primitives::u32_trait::{_1, _2, _3, _4}; use node_primitives::{ - AccountId, AccountIndex, Balance, BlockNumber, Hash, Index, Signature, AuraId + AccountId, AccountIndex, AuraId, Balance, BlockNumber, Hash, Index, + Moment, Signature, }; use grandpa::fg_primitives::{self, ScheduledChange}; use client::{ block_builder::api::{self as block_builder_api, InherentData, CheckInherentsResult}, runtime_api as client_api, impl_runtime_apis }; -use runtime_primitives::{ApplyResult, generic, create_runtime_str}; +use runtime_primitives::{ApplyResult, impl_opaque_keys, generic, create_runtime_str, key_types}; use runtime_primitives::transaction_validity::TransactionValidity; +use runtime_primitives::weights::Weight; use runtime_primitives::traits::{ - BlakeTwo256, Block as BlockT, DigestFor, NumberFor, StaticLookup, Convert, + BlakeTwo256, Block as BlockT, DigestFor, NumberFor, StaticLookup, }; use version::RuntimeVersion; -use council::{motions as council_motions}; -#[cfg(feature = "std")] -use council::seats as council_seats; +use elections::VoteIndex; #[cfg(any(feature = "std", test))] use version::NativeVersion; use substrate_primitives::OpaqueMetadata; use grandpa::{AuthorityId as GrandpaId, AuthorityWeight as GrandpaWeight}; +use finality_tracker::{DEFAULT_REPORT_LATENCY, DEFAULT_WINDOW_SIZE}; #[cfg(any(feature = "std", test))] pub use runtime_primitives::BuildStorage; pub use timestamp::Call as TimestampCall; pub use balances::Call as BalancesCall; -pub use runtime_primitives::{Permill, Perbill, impl_opaque_keys}; +pub use contracts::Gas; +pub use runtime_primitives::{Permill, Perbill}; pub use support::StorageValue; pub use staking::StakerStatus; +/// Implementations for `Convert` and other helper structs passed into runtime modules as associated +/// types. +pub mod impls; +use impls::{CurrencyToVoteHandler, WeightMultiplierUpdateHandler}; + +// Make the WASM binary available. +#[cfg(feature = "std")] +include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); + /// Runtime version. pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("node"), impl_name: create_runtime_str!("substrate-node"), authoring_version: 10, - spec_version: 97, - impl_version: 99, + // Per convention: if the runtime behavior changes, increment spec_version + // 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: 118, + impl_version: 118, apis: RUNTIME_API_VERSIONS, }; @@ -72,18 +89,36 @@ pub fn native_version() -> NativeVersion { } } -pub struct CurrencyToVoteHandler; +pub const MILLICENTS: Balance = 1_000_000_000; +pub const CENTS: Balance = 1_000 * MILLICENTS; // assume this is worth about a cent. +pub const DOLLARS: Balance = 100 * CENTS; -impl CurrencyToVoteHandler { - fn factor() -> u128 { (Balances::total_issuance() / u64::max_value() as u128).max(1) } -} +type NegativeImbalance = >::NegativeImbalance; + +pub struct Author; -impl Convert for CurrencyToVoteHandler { - fn convert(x: u128) -> u64 { (x / Self::factor()) as u64 } +impl OnUnbalanced for Author { + fn on_unbalanced(amount: NegativeImbalance) { + Balances::resolve_creating(&Authorship::author(), amount); + } } -impl Convert for CurrencyToVoteHandler { - fn convert(x: u128) -> u128 { x * Self::factor() } +pub type DealWithFees = SplitTwoWays< + Balance, + NegativeImbalance, + _4, Treasury, // 4 parts (80%) goes to the treasury. + _1, Author, // 1 part (20%) goes to the block author. +>; + +pub const SECS_PER_BLOCK: Moment = 6; +pub const MINUTES: Moment = 60 / SECS_PER_BLOCK; +pub const HOURS: Moment = MINUTES * 60; +pub const DAYS: Moment = HOURS * 24; + +parameter_types! { + pub const BlockHashCount: BlockNumber = 250; + pub const MaximumBlockWeight: Weight = 4 * 1024 * 1024; + pub const MaximumBlockLength: u32 = 4 * 1024 * 1024; } impl system::Trait for Runtime { @@ -95,7 +130,11 @@ 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; + type MaximumBlockLength = MaximumBlockLength; } impl aura::Trait for Runtime { @@ -110,19 +149,48 @@ impl indices::Trait for Runtime { type Event = Event; } +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 { type Balance = Balance; type OnFreeBalanceZero = ((Staking, Contracts), Session); type OnNewAccount = Indices; type Event = Event; - type TransactionPayment = (); + type TransactionPayment = DealWithFees; type DustRemoval = (); type TransferPayment = (); + type ExistentialDeposit = ExistentialDeposit; + type TransferFee = TransferFee; + type CreationFee = CreationFee; + type TransactionBaseFee = TransactionBaseFee; + type TransactionByteFee = TransactionByteFee; } +parameter_types! { + pub const MinimumPeriod: u64 = SECS_PER_BLOCK / 2; +} impl timestamp::Trait for Runtime { - type Moment = u64; + type Moment = Moment; type OnTimestampSet = Aura; + type MinimumPeriod = MinimumPeriod; +} + +parameter_types! { + pub const UncleGenerations: u64 = 0; +} + +// TODO: #2986 implement this properly +impl authorship::Trait for Runtime { + type FindAuthor = (); + type UncleGenerations = UncleGenerations; + type FilterUncle = (); + type EventHandler = (); } parameter_types! { @@ -130,9 +198,13 @@ parameter_types! { pub const Offset: BlockNumber = 0; } -type SessionHandlers = (Grandpa, Aura); +type SessionHandlers = (Grandpa, Aura, ImOnline); + impl_opaque_keys! { - pub struct SessionKeys(grandpa::AuthorityId, AuraId); + pub struct SessionKeys { + #[id(key_types::ED25519)] + pub ed25519: GrandpaId, + } } // NOTE: `SessionHandler` and `SessionKeys` are co-dependent: One key will be used for each handler. @@ -147,6 +219,14 @@ impl session::Trait for Runtime { type ShouldEndSession = session::PeriodicSessions; type Event = Event; type Keys = SessionKeys; + type ValidatorId = AccountId; + type ValidatorIdOf = staking::StashOf; + type SelectInitialValidators = Staking; +} + +impl session::historical::Trait for Runtime { + type FullIdentification = staking::Exposure; + type FullIdentificationOf = staking::ExposureOf; } parameter_types! { @@ -163,19 +243,18 @@ impl staking::Trait for Runtime { type Reward = (); type SessionsPerEra = SessionsPerEra; type BondingDuration = BondingDuration; + type SessionInterface = Self; } -const MINUTES: BlockNumber = 6; -const BUCKS: Balance = 1_000_000_000_000; - parameter_types! { pub const LaunchPeriod: BlockNumber = 28 * 24 * 60 * MINUTES; pub const VotingPeriod: BlockNumber = 28 * 24 * 60 * MINUTES; pub const EmergencyVotingPeriod: BlockNumber = 3 * 24 * 60 * MINUTES; - pub const MinimumDeposit: Balance = 100 * BUCKS; + pub const MinimumDeposit: Balance = 100 * DOLLARS; pub const EnactmentPeriod: BlockNumber = 30 * 24 * 60 * MINUTES; pub const CooloffPeriod: BlockNumber = 30 * 24 * 60 * MINUTES; } + impl democracy::Trait for Runtime { type Proposal = Call; type Event = Event; @@ -185,47 +264,111 @@ impl democracy::Trait for Runtime { type VotingPeriod = VotingPeriod; type EmergencyVotingPeriod = EmergencyVotingPeriod; type MinimumDeposit = MinimumDeposit; - type ExternalOrigin = council_motions::EnsureProportionAtLeast<_1, _2, AccountId>; - type ExternalMajorityOrigin = council_motions::EnsureProportionAtLeast<_2, _3, AccountId>; - type EmergencyOrigin = council_motions::EnsureProportionAtLeast<_1, _1, AccountId>; - type CancellationOrigin = council_motions::EnsureProportionAtLeast<_2, _3, AccountId>; - type VetoOrigin = council_motions::EnsureMember; + type ExternalOrigin = collective::EnsureProportionAtLeast<_1, _2, AccountId, CouncilInstance>; + type ExternalMajorityOrigin = collective::EnsureProportionAtLeast<_2, _3, AccountId, CouncilInstance>; + type ExternalPushOrigin = collective::EnsureProportionAtLeast<_2, _3, AccountId, TechnicalInstance>; + type EmergencyOrigin = collective::EnsureProportionAtLeast<_1, _1, AccountId, CouncilInstance>; + type CancellationOrigin = collective::EnsureProportionAtLeast<_2, _3, AccountId, CouncilInstance>; + type VetoOrigin = collective::EnsureMember; type CooloffPeriod = CooloffPeriod; } -impl council::Trait for Runtime { +type CouncilInstance = collective::Instance1; +impl collective::Trait for Runtime { + type Origin = Origin; + type Proposal = Call; + type Event = Event; +} + +parameter_types! { + pub const CandidacyBond: Balance = 10 * DOLLARS; + pub const VotingBond: Balance = 1 * DOLLARS; + pub const VotingFee: Balance = 2 * 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; +} + +impl elections::Trait for Runtime { type Event = Event; + type Currency = Balances; type BadPresentation = (); type BadReaper = (); type BadVoterIndex = (); type LoserCandidate = (); - type OnMembersChanged = CouncilMotions; + type ChangeMembers = Council; + type CandidacyBond = CandidacyBond; + type VotingBond = VotingBond; + type VotingFee = VotingFee; + type PresentSlashPerVoter = PresentSlashPerVoter; + type CarryCount = CarryCount; + type InactiveGracePeriod = InactiveGracePeriod; + type VotingPeriod = ElectionsVotingPeriod; + type DecayRatio = DecayRatio; } -impl council::motions::Trait for Runtime { +type TechnicalInstance = collective::Instance2; +impl collective::Trait for Runtime { type Origin = Origin; type Proposal = Call; type Event = Event; } +parameter_types! { + pub const ProposalBond: Permill = Permill::from_percent(5); + pub const ProposalBondMinimum: Balance = 1 * DOLLARS; + pub const SpendPeriod: BlockNumber = 1 * DAYS; + pub const Burn: Permill = Permill::from_percent(50); +} + impl treasury::Trait for Runtime { type Currency = Balances; - type ApproveOrigin = council_motions::EnsureMembers<_4, AccountId>; - type RejectOrigin = council_motions::EnsureMembers<_2, AccountId>; + type ApproveOrigin = collective::EnsureMembers<_4, AccountId, CouncilInstance>; + type RejectOrigin = collective::EnsureMembers<_2, AccountId, CouncilInstance>; type Event = Event; type MintedForSpending = (); type ProposalRejection = (); + type ProposalBond = ProposalBond; + type ProposalBondMinimum = ProposalBondMinimum; + type SpendPeriod = SpendPeriod; + type Burn = Burn; +} + +parameter_types! { + pub const ContractTransferFee: Balance = 1 * CENTS; + pub const ContractCreationFee: Balance = 1 * CENTS; + pub const ContractTransactionBaseFee: Balance = 1 * CENTS; + pub const ContractTransactionByteFee: Balance = 10 * MILLICENTS; + pub const ContractFee: Balance = 1 * CENTS; } impl contracts::Trait for Runtime { type Currency = Balances; type Call = Call; type Event = Event; - type Gas = u64; type DetermineContractAddress = contracts::SimpleAddressDeterminator; type ComputeDispatchFee = contracts::DefaultDispatchFeeComputor; type TrieIdGenerator = contracts::TrieIdFromParentCounter; type GasPayment = (); + type SignedClaimHandicap = contracts::DefaultSignedClaimHandicap; + type TombstoneDeposit = contracts::DefaultTombstoneDeposit; + type StorageSizeOffset = contracts::DefaultStorageSizeOffset; + type RentByteFee = contracts::DefaultRentByteFee; + type RentDepositOffset = contracts::DefaultRentDepositOffset; + type SurchargeReward = contracts::DefaultSurchargeReward; + type TransferFee = ContractTransferFee; + type CreationFee = ContractCreationFee; + type TransactionBaseFee = ContractTransactionBaseFee; + type TransactionByteFee = ContractTransactionByteFee; + type ContractFee = ContractFee; + type CallBaseFee = contracts::DefaultCallBaseFee; + type CreateBaseFee = contracts::DefaultCreateBaseFee; + type MaxDepth = contracts::DefaultMaxDepth; + type MaxValueSize = contracts::DefaultMaxValueSize; + type BlockGasLimit = contracts::DefaultBlockGasLimit; } impl sudo::Trait for Runtime { @@ -233,12 +376,28 @@ impl sudo::Trait for Runtime { type Proposal = Call; } +impl im_online::Trait for Runtime { + type AuthorityId = AuraId; + type Call = Call; + type Event = Event; + type SessionsPerEra = SessionsPerEra; + type UncheckedExtrinsic = UncheckedExtrinsic; + type IsValidAuthorityId = Aura; +} + impl grandpa::Trait for Runtime { type Event = Event; } +parameter_types! { + pub const WindowSize: BlockNumber = DEFAULT_WINDOW_SIZE.into(); + pub const ReportLatency: BlockNumber = DEFAULT_REPORT_LATENCY.into(); +} + impl finality_tracker::Trait for Runtime { type OnFinalizationStalled = Grandpa; + type WindowSize = WindowSize; + type ReportLatency = ReportLatency; } construct_runtime!( @@ -247,22 +406,24 @@ construct_runtime!( NodeBlock = node_primitives::Block, UncheckedExtrinsic = UncheckedExtrinsic { - System: system, - Aura: aura::{Module, Config, Inherent(Timestamp)}, - Timestamp: timestamp::{Module, Call, Storage, Config, Inherent}, + System: system::{Module, Call, Storage, Config, Event}, + Aura: aura::{Module, Call, Storage, Config, Inherent(Timestamp)}, + Timestamp: timestamp::{Module, Call, Storage, Inherent}, + Authorship: authorship::{Module, Call, Storage}, Indices: indices, Balances: balances, - Session: session::{Module, Call, Storage, Event, Config}, Staking: staking::{default, OfflineWorker}, - Democracy: democracy, - Council: council::{Module, Call, Storage, Event}, - CouncilMotions: council_motions::{Module, Call, Storage, Event, Origin}, - CouncilSeats: council_seats::{Config}, + 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}, FinalityTracker: finality_tracker::{Module, Call, Inherent}, - Grandpa: grandpa::{Module, Call, Storage, Config, Event}, - Treasury: treasury, + Grandpa: grandpa::{Module, Call, Storage, Config, Event}, + Treasury: treasury::{Module, Call, Storage, Event}, Contracts: contracts, Sudo: sudo, + ImOnline: im_online::{default, ValidateUnsigned}, } ); @@ -276,12 +437,19 @@ pub type Block = generic::Block; pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; +/// The SignedExtension to the basic transaction logic. +pub type SignedExtra = ( + system::CheckEra, + system::CheckNonce, + system::CheckWeight, + balances::TakeFees +); /// Unchecked extrinsic type as expected by this runtime. -pub type UncheckedExtrinsic = generic::UncheckedMortalCompactExtrinsic; +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; /// Extrinsic type that has already been checked. -pub type CheckedExtrinsic = generic::CheckedExtrinsic; +pub type CheckedExtrinsic = generic::CheckedExtrinsic; /// Executive: handles dispatch to the various modules. -pub type Executive = executive::Executive, Balances, Runtime, AllModules>; +pub type Executive = executive::Executive, Runtime, AllModules>; impl_runtime_apis! { impl client_api::Core for Runtime { diff --git a/node/runtime/wasm/Cargo.lock b/node/runtime/wasm/Cargo.lock deleted file mode 100644 index 0d94d65476a5de491096e11ae71e96c52e997ab4..0000000000000000000000000000000000000000 --- a/node/runtime/wasm/Cargo.lock +++ /dev/null @@ -1,3964 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "adler32" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "aes-ctr" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ctr 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "stream-cipher 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "aes-soft" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "aesni" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "stream-cipher 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "aho-corasick" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "aio-limited" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "arrayref" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "arrayvec" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "asn1_der" -version = "0.6.1" -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)", -] - -[[package]] -name = "asn1_der_derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "atty" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "autocfg" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "backtrace" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "backtrace-sys" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "base58" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "base64" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bigint" -version = "4.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bitflags" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bitmask" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "blake2" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "blake2-rfc" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "block-buffer" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "block-buffer" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "block-cipher-trait" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "block-padding" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bs58" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "build_const" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bumpalo" -version = "2.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "byte-tools" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "byte-tools" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "byteorder" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "byteorder" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bytes" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "c_linked_list" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "cc" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "cfg-if" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "chrono" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "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)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "clear_on_drop" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "cloudabi" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "constant_time_eq" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "crc" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crc32fast" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-channel" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-deque" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-deque" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-deque" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-queue" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-utils" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-utils" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crunchy" -version = "0.1.6" -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 = "crypto-mac" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crypto-mac" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ctr" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "stream-cipher 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "cuckoofilter" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "curve25519-dalek" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "data-encoding" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "derive_more" -version = "0.14.1" -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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "digest" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "digest" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "dns-parser" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ed25519-dalek" -version = "1.0.0-pre.1" -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.1.4 (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)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "either" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "elastic-array" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "env_logger" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "environmental" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "erased-serde" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "failure" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "backtrace 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "failure_derive" -version = "0.1.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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "fake-simd" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "fixed-hash" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.6 (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 = "flate2" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "miniz-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "miniz_oxide_c_api 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "fnv" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "fuchsia-zircon" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "fuchsia-zircon-sys" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "futures" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "futures-cpupool" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "gcc" -version = "0.3.55" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -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)", - "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "generic-array" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "get_if_addrs" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "c_linked_list 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "get_if_addrs-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "get_if_addrs-sys" -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.55 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hash-db" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "hash256-std-hasher" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hashbrown" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hashmap_core" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "heapsize" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "heck" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hex" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "hex-literal" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "hex-literal-impl 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hex-literal-impl" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro-hack 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hmac" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crypto-mac 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hmac" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hmac-drbg" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", - "hmac 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "http" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "httparse" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "humantime" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "idna" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "impl-codec" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "impl-serde" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "impl-serde" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "serde 1.0.91 (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 = "iovec" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.55 (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" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "itoa" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "js-sys" -version = "0.3.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "wasm-bindgen 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "keccak" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "kernel32-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "kvdb" -version = "0.1.0" -source = "git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7987b8475878fc4d2e1f81d#b0317f649ab2c665b7987b8475878fc4d2e1f81d" -dependencies = [ - "elastic-array 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7987b8475878fc4d2e1f81d)", -] - -[[package]] -name = "lazy_static" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "libc" -version = "0.2.55" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "libp2p" -version = "0.9.1" -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.27 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core-derive 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-deflate 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-dns 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-floodsub 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-identify 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-kad 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-mdns 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-mplex 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-noise 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-ping 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-plaintext 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-ratelimit 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-secio 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-tcp 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-uds 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-wasm-ext 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-websocket 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-yamux 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multihash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (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.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-core" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "asn1_der 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bs58 0.2.2 (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)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libsecp256k1 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "multistream-select 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multihash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rw-stream-sink 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (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)", - "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.1 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-core-derive" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-deflate" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "flate2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-dns" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-dns-unofficial 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-floodsub" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bs58 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "cuckoofilter 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (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)", -] - -[[package]] -name = "libp2p-identify" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (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)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-timer 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-kad" -version = "0.9.0" -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.10 (registry+https://github.com/rust-lang/crates.io-index)", - "bigint 4.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bs58 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multihash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.6.1 (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)", - "smallvec 0.6.9 (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)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-timer 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-mdns" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "data-encoding 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "dns-parser 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-udp 0.1.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.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-mplex" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "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)", -] - -[[package]] -name = "libp2p-noise" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "curve25519-dalek 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", - "snow 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "x25519-dalek 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-ping" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-timer 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-plaintext" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-ratelimit" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aio-limited 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-secio" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aes-ctr 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "asn1_der 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "ctr 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "hmac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-send-wrapper 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rw-stream-sink 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "twofish 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-futures 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", - "web-sys 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-tcp" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (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)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tk-listen 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-uds" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-wasm-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-send-wrapper 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-futures 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-websocket" -version = "0.9.1" -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.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rw-stream-sink 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "soketto 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-rustls 0.10.0-alpha.3 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki-roots 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libp2p-yamux" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "yamux 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libsecp256k1" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hmac-drbg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "lock_api" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "lock_api" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "log" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "matches" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "memchr" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "memoffset" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "memory-db" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hashmap_core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "memory_units" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "merlin" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "miniz-sys" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "miniz_oxide" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "miniz_oxide_c_api" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", - "crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "miniz_oxide 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "mio" -version = "0.6.19" -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)", - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -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)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "miow" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "multistream-select" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (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)", -] - -[[package]] -name = "net2" -version = "0.2.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "node-primitives" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-primitives 2.0.0", - "sr-std 2.0.0", - "substrate-primitives 2.0.0", -] - -[[package]] -name = "node-runtime" -version = "2.0.0" -dependencies = [ - "integer-sqrt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "node-primitives 2.0.0", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "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.91 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-primitives 2.0.0", - "sr-std 2.0.0", - "sr-version 2.0.0", - "srml-aura 2.0.0", - "srml-balances 2.0.0", - "srml-contracts 2.0.0", - "srml-council 2.0.0", - "srml-democracy 2.0.0", - "srml-executive 2.0.0", - "srml-finality-tracker 2.0.0", - "srml-grandpa 2.0.0", - "srml-indices 2.0.0", - "srml-session 2.0.0", - "srml-staking 2.0.0", - "srml-sudo 2.0.0", - "srml-support 2.0.0", - "srml-system 2.0.0", - "srml-timestamp 2.0.0", - "srml-treasury 2.0.0", - "substrate-client 2.0.0", - "substrate-consensus-aura-primitives 2.0.0", - "substrate-keyring 2.0.0", - "substrate-offchain-primitives 2.0.0", - "substrate-primitives 2.0.0", -] - -[[package]] -name = "node-runtime-wasm" -version = "2.0.0" -dependencies = [ - "node-runtime 2.0.0", -] - -[[package]] -name = "nodrop" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "nohash-hasher" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "nom" -version = "4.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "version_check 0.1.5 (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.4 (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.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num_cpus" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "numtoa" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "once_cell" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "opaque-debug" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "owning_ref" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "owning_ref" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parity-bytes" -version = "0.1.0" -source = "git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7987b8475878fc4d2e1f81d#b0317f649ab2c665b7987b8475878fc4d2e1f81d" - -[[package]] -name = "parity-codec" -version = "3.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec-derive 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parity-codec-derive" -version = "3.3.0" -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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parity-multiaddr" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "bs58 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "data-encoding 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multihash 0.1.1 (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.91 (registry+https://github.com/rust-lang/crates.io-index)", - "unsigned-varint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parity-multihash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "blake2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "unsigned-varint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parity-send-wrapper" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "parity-wasm" -version = "0.31.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parking_lot" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parking_lot" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parking_lot" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parking_lot" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lock_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.5.0 (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 = "parking_lot_core" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parking_lot_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parking_lot_core" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parking_lot_core" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "paste" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "paste-impl 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "paste-impl" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro-hack 0.5.7 (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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "pbkdf2" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "percent-encoding" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "primitive-types" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "fixed-hash 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "impl-codec 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "impl-serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "uint 0.7.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.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "proc-macro-hack" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro-hack-impl 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "proc-macro-hack" -version = "0.5.7" -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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "proc-macro-hack-impl" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "proc-macro2" -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "protobuf" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "pwasm-utils" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-wasm 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "quick-error" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "quick-error" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "quote" -version = "0.6.12" -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 = "rand" -version = "0.3.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_chacha" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_core" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "rand_hc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_isaac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_jitter" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_os" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_pcg" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_xorshift" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rayon" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rayon-core" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "redox_syscall" -version = "0.1.54" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "redox_termios" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "regex" -version = "1.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aho-corasick 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "regex-syntax" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ring" -version = "0.14.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.15" -source = "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 = "rustls" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", - "sct 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rw-stream-sink" -version = "0.1.2" -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.27 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ryu" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "safe-mix" -version = "1.0.0" -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)", -] - -[[package]] -name = "schnorrkel" -version = "0.1.1" -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.1.4 (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)", - "merlin 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.1.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)", - "subtle 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "scopeguard" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "scopeguard" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "sct" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.6.2 (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 = "send_wrapper" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "serde" -version = "1.0.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "serde_derive 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "serde_derive" -version = "1.0.91" -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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "serde_json" -version = "1.0.39" -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 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "sha-1" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "sha1" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "sha2" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-buffer 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "sha2" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "sha3" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "slab" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "slog" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "erased-serde 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "slog-json" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "chrono 0.4.6 (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.91 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", - "slog 2.4.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "slog-scope" -version = "4.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slog 2.4.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "smallvec" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "snow" -version = "0.5.2" -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.1 (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)", - "rand_core 0.4.0 (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)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", - "static_slice 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "soketto" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (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)", -] - -[[package]] -name = "sourcefile" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "spin" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "sr-api-macros" -version = "2.0.0" -dependencies = [ - "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "sr-io" -version = "2.0.0" -dependencies = [ - "environmental 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libsecp256k1 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (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-primitives 2.0.0", - "substrate-state-machine 2.0.0", - "substrate-trie 2.0.0", - "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "sr-primitives" -version = "2.0.0" -dependencies = [ - "integer-sqrt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-io 2.0.0", - "sr-std 2.0.0", - "substrate-primitives 2.0.0", -] - -[[package]] -name = "sr-sandbox" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.1 (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-primitives 2.0.0", - "wasmi 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[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 = "sr-version" -version = "2.0.0" -dependencies = [ - "impl-serde 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-primitives 2.0.0", - "sr-std 2.0.0", -] - -[[package]] -name = "srml-aura" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-primitives 2.0.0", - "sr-std 2.0.0", - "srml-session 2.0.0", - "srml-staking 2.0.0", - "srml-support 2.0.0", - "srml-system 2.0.0", - "srml-timestamp 2.0.0", - "substrate-consensus-aura-primitives 2.0.0", - "substrate-inherents 2.0.0", - "substrate-primitives 2.0.0", -] - -[[package]] -name = "srml-balances" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.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.91 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-primitives 2.0.0", - "sr-std 2.0.0", - "srml-support 2.0.0", - "srml-system 2.0.0", - "substrate-keyring 2.0.0", -] - -[[package]] -name = "srml-contracts" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-wasm 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)", - "pwasm-utils 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-io 2.0.0", - "sr-primitives 2.0.0", - "sr-sandbox 2.0.0", - "sr-std 2.0.0", - "srml-support 2.0.0", - "srml-system 2.0.0", - "srml-timestamp 2.0.0", - "substrate-primitives 2.0.0", - "wasmi-validation 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "srml-council" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.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.91 (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-democracy 2.0.0", - "srml-support 2.0.0", - "srml-system 2.0.0", - "substrate-primitives 2.0.0", -] - -[[package]] -name = "srml-democracy" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.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.91 (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", -] - -[[package]] -name = "srml-executive" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (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", -] - -[[package]] -name = "srml-finality-tracker" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-primitives 2.0.0", - "sr-std 2.0.0", - "srml-support 2.0.0", - "srml-system 2.0.0", - "substrate-inherents 2.0.0", -] - -[[package]] -name = "srml-grandpa" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-primitives 2.0.0", - "sr-std 2.0.0", - "srml-finality-tracker 2.0.0", - "srml-session 2.0.0", - "srml-support 2.0.0", - "srml-system 2.0.0", - "substrate-finality-grandpa-primitives 2.0.0", - "substrate-primitives 2.0.0", -] - -[[package]] -name = "srml-indices" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.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.91 (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-keyring 2.0.0", - "substrate-primitives 2.0.0", -] - -[[package]] -name = "srml-metadata" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-std 2.0.0", - "substrate-primitives 2.0.0", -] - -[[package]] -name = "srml-session" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.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.91 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-primitives 2.0.0", - "sr-std 2.0.0", - "srml-support 2.0.0", - "srml-system 2.0.0", - "srml-timestamp 2.0.0", -] - -[[package]] -name = "srml-staking" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.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.91 (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-session 2.0.0", - "srml-support 2.0.0", - "srml-system 2.0.0", - "substrate-keyring 2.0.0", -] - -[[package]] -name = "srml-sudo" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (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-support-procedural 2.0.0", - "srml-system 2.0.0", -] - -[[package]] -name = "srml-support" -version = "2.0.0" -dependencies = [ - "bitmask 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "paste 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (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-metadata 2.0.0", - "srml-support-procedural 2.0.0", - "substrate-inherents 2.0.0", -] - -[[package]] -name = "srml-support-procedural" -version = "2.0.0" -dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-api-macros 2.0.0", - "srml-support-procedural-tools 2.0.0", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "srml-support-procedural-tools" -version = "2.0.0" -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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "srml-support-procedural-tools-derive 2.0.0", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "srml-support-procedural-tools-derive" -version = "2.0.0" -dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "srml-system" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.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.91 (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", - "substrate-primitives 2.0.0", -] - -[[package]] -name = "srml-timestamp" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-primitives 2.0.0", - "sr-std 2.0.0", - "srml-support 2.0.0", - "srml-system 2.0.0", - "substrate-inherents 2.0.0", -] - -[[package]] -name = "srml-treasury" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "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", -] - -[[package]] -name = "stable_deref_trait" -version = "1.1.1" -source = "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 = "static_slice" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "stream-cipher" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "strum" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "strum_macros" -version = "0.14.0" -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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-bip39" -version = "0.2.1" -source = "git+https://github.com/paritytech/substrate-bip39#44307fda4ea17fe97aeb93af317fbc8f6ed34193" -dependencies = [ - "hmac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "schnorrkel 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-client" -version = "2.0.0" -dependencies = [ - "derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hex-literal 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb 0.1.0 (git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7987b8475878fc4d2e1f81d)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-api-macros 2.0.0", - "sr-primitives 2.0.0", - "sr-std 2.0.0", - "sr-version 2.0.0", - "substrate-consensus-common 2.0.0", - "substrate-executor 2.0.0", - "substrate-inherents 2.0.0", - "substrate-keyring 2.0.0", - "substrate-primitives 2.0.0", - "substrate-state-machine 2.0.0", - "substrate-telemetry 2.0.0", - "substrate-trie 2.0.0", -] - -[[package]] -name = "substrate-consensus-aura-primitives" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.1 (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", -] - -[[package]] -name = "substrate-consensus-common" -version = "2.0.0" -dependencies = [ - "derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-primitives 2.0.0", - "sr-std 2.0.0", - "sr-version 2.0.0", - "substrate-inherents 2.0.0", - "substrate-primitives 2.0.0", - "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-executor" -version = "2.0.0" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libsecp256k1 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-io 2.0.0", - "sr-version 2.0.0", - "substrate-panic-handler 2.0.0", - "substrate-primitives 2.0.0", - "substrate-serializer 2.0.0", - "substrate-state-machine 2.0.0", - "substrate-trie 2.0.0", - "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmi 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-finality-grandpa-primitives" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (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", -] - -[[package]] -name = "substrate-inherents" -version = "2.0.0" -dependencies = [ - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-primitives 2.0.0", - "sr-std 2.0.0", -] - -[[package]] -name = "substrate-keyring" -version = "2.0.0" -dependencies = [ - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-primitives 2.0.0", - "strum 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "strum_macros 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-primitives 2.0.0", -] - -[[package]] -name = "substrate-offchain-primitives" -version = "2.0.0" -dependencies = [ - "sr-primitives 2.0.0", - "substrate-client 2.0.0", -] - -[[package]] -name = "substrate-panic-handler" -version = "2.0.0" -dependencies = [ - "backtrace 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-primitives" -version = "2.0.0" -dependencies = [ - "base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.1 (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)", - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hash256-std-hasher 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "impl-serde 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "primitive-types 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "schnorrkel 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-std 2.0.0", - "substrate-bip39 0.2.1 (git+https://github.com/paritytech/substrate-bip39)", - "tiny-bip39 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "twox-hash 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmi 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-serializer" -version = "2.0.0" -dependencies = [ - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-state-machine" -version = "2.0.0" -dependencies = [ - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-panic-handler 2.0.0", - "substrate-primitives 2.0.0", - "substrate-trie 2.0.0", - "trie-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-root 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-telemetry" -version = "2.0.0" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "slog 2.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "slog-json 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slog-scope 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-trie" -version = "2.0.0" -dependencies = [ - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "memory-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-std 2.0.0", - "substrate-primitives 2.0.0", - "trie-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-root 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "subtle" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "subtle" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "syn" -version = "0.15.34" -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.12 (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 = "synstructure" -version = "0.10.2" -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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (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 = "termcolor" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "termion" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "thread_local" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "time" -version = "0.1.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -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)", - "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "hmac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tiny-keccak" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tk-listen" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio" -version = "0.1.20" -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.27 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.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-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-sync 0.1.5 (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.14 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-trace-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-codec" -version = "0.1.1" -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.27 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-current-thread" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-dns-unofficial" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-executor" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-fs" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-io" -version = "0.1.12" -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.27 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-reactor" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (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.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-rustls" -version = "0.10.0-alpha.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)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (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)", -] - -[[package]] -name = "tokio-sync" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-tcp" -version = "0.1.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)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (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.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-threadpool" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-timer" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-trace-core" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-udp" -version = "0.1.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)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-uds" -version = "0.2.5" -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.27 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "toml" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "trie-db" -version = "0.12.2" -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.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hashmap_core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.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 = "trie-root" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "twofish" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "twox-hash" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "typenum" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "ucd-util" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "uint" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.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-bidi" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "unicode-normalization" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "unicode-segmentation" -version = "1.3.0" -source = "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 = "unsigned-varint" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "untrusted" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "url" -version = "1.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "utf8-ranges" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "version_check" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "wasm-bindgen" -version = "0.2.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "wasm-bindgen-macro 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bumpalo 2.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-shared 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.3.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-macro-support 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.45" -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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-backend 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-shared 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.45" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "wasm-bindgen-webidl" -version = "0.2.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "failure 0.1.5 (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.6 (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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-backend 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "weedle 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "wasm-timer" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "send_wrapper 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "web-sys 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "wasmi" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-wasm 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmi-validation 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "wasmi-validation" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-wasm 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "web-sys" -version = "0.3.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.22 (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.45 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-webidl 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "webpki" -version = "0.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "webpki-roots" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "weedle" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "winapi" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi" -version = "0.3.7" -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-build" -version = "0.1.1" -source = "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-util" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.3.7 (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" - -[[package]] -name = "wincolor" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ws2_32-sys" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "x25519-dalek" -version = "0.5.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.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "yamux" -version = "0.2.1" -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.27 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "nohash-hasher 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "quick-error 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "zeroize" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "zeroize" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "zeroize_derive 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "zeroize_derive" -version = "0.8.0" -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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[metadata] -"checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" -"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 aho-corasick 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e6f484ae0c99fec2e858eb6134949117399f222608d84cadb3f58c1f97c2364c" -"checksum aio-limited 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7f10b352bc3fc08ae24dc5d2d3ddcac153678533986122dc283d747b12071000" -"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" -"checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" -"checksum asn1_der 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9893d63fc3b1c44231e667da6836a33f27d8b6b3bdc82f83da5dfd579d1b6528" -"checksum asn1_der_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e7f92edafad155aff997fa5b727c6429b91e996b5a5d62a2b0adbae1306b5fe" -"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" -"checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf" -"checksum backtrace 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)" = "1a13fc43f04daf08ab4f71e3d27e1fc27fc437d3e95ac0063a796d92fb40f39b" -"checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" -"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 bigint 4.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ebecac13b3c745150d7b6c3ea7572d372f09d627c2077e893bf26c5c7f70d282" -"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" -"checksum bitmask 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5da9b3d9f6f585199287a473f4f8dfab6566cf827d15c00c219f53c645687ead" -"checksum blake2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "91721a6330935673395a0607df4d49a9cb90ae12d259f1b3e0a3f6e1d486872e" -"checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" -"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" -"checksum block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4dc3af3ee2e12f3e5d224e5e1e3d73668abbeb69e566d361f7d5563a4fdf09" -"checksum bs58 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0de79cfb98e7aa9988188784d8664b4b5dad6eaaa0863b91d9a4ed871d4f7a42" -"checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" -"checksum bumpalo 2.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "84dca3afd8e01b9526818b7963e5b4916063b3cdf9f10cf6b73ef0bd0ec37aa5" -"checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" -"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" -"checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" -"checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" -"checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" -"checksum c_linked_list 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4964518bd3b4a8190e832886cdc0da9794f12e8e6c1613a9e90ff331c4c8724b" -"checksum cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "39f75544d7bbaf57560d2168f28fd649ff9c76153874db88bdbdfd839b1a7e7d" -"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" -"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878" -"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 constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e" -"checksum crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" -"checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" -"checksum crossbeam 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad4c7ea749d9fb09e23c5cb17e3b70650860553a0e2744e38446b1803bf7db94" -"checksum crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f0ed1a4de2235cabda8558ff5840bffb97fcb64c97827f354a451307df5f72b" -"checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" -"checksum crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "05e44b8cf3e1a625844d1750e1f7820da46044ff6d28f4d43e455ba3e5bb2c13" -"checksum crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" -"checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150" -"checksum crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "04c9e3102cc2d69cd681412141b390abd55a362afc1540965dad0ad4d34280b4" -"checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" -"checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" -"checksum crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f8306fcef4a7b563b76b7dd949ca48f52bc1141aa067d2ea09565f3e2652aa5c" -"checksum crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" -"checksum crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" -"checksum crypto-mac 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "779015233ac67d65098614aec748ac1c756ab6677fa2e14cf8b37c08dfed1198" -"checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" -"checksum ctr 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "022cd691704491df67d25d006fe8eca083098253c4d43516c2206479c58c6736" -"checksum cuckoofilter 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8dd43f7cfaffe0a386636a10baea2ee05cc50df3b77bea4a456c9572a939bf1f" -"checksum curve25519-dalek 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "750226d75fc2f5a8daec6e7477624e258674023eb73d8d647f63b943ca182a4a" -"checksum data-encoding 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4f47ca1860a761136924ddd2422ba77b2ea54fe8cc75b9040804a0d9d32ad97" -"checksum derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6d944ac6003ed268757ef1ee686753b57efc5fcf0ebe7b64c9fc81e7e32ff839" -"checksum digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e5b29bf156f3f4b3c4f610a25ff69370616ae6e0657d416de22645483e72af0a" -"checksum digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c" -"checksum dns-parser 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c4d33be9473d06f75f58220f71f7a9317aca647dc061dbd3c361b0bef505fbea" -"checksum ed25519-dalek 1.0.0-pre.1 (registry+https://github.com/rust-lang/crates.io-index)" = "81956bcf7ef761fb4e1d88de3fa181358a0d26cbcb9755b587a08f9119824b86" -"checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b" -"checksum elastic-array 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "073be79b6538296faf81c631872676600616073817dd9a440c477ad09b408983" -"checksum env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b61fa891024a945da30a9581546e8cfaf5602c7b3f4c137a2805cf388f92075a" -"checksum environmental 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c7464757b80de8930c91c9afe77ddce501826bf9d134a87db2c67d9dc177e2c" -"checksum erased-serde 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3beee4bc16478a1b26f2e80ad819a52d24745e292f521a63c16eea5f74b7eb60" -"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 fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" -"checksum fixed-hash 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d1a683d1234507e4f3bf2736eeddf0de1dc65996dc0164d57eba0a74bcf29489" -"checksum flate2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f87e68aa82b2de08a6e037f1385455759df6e445a8df5e005b4297191dbf18aa" -"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" -"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" -"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" -"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" -"checksum futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)" = "a2037ec1c6c1c4f79557762eab1f7eae1f64f6cb418ace90fae88f0942b60139" -"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" -"checksum gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)" = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" -"checksum generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c0f28c2f5bfb5960175af447a2da7c18900693738343dc896ffbcabd9839592" -"checksum generic-array 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fceb69994e330afed50c93524be68c42fa898c2d9fd4ee8da03bd7363acd26f2" -"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 hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ba7fb417e5c470acdd61068c79767d0e65962e70836cf6c9dfd2409f06345ce0" -"checksum hash256-std-hasher 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f8b2027c19ec91eb304999abae7307d225cf93be42af53b0039f76e98ed5af86" -"checksum hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3bae29b6653b3412c2e71e9d486db9f9df5d701941d86683005efb9f2d28e3da" -"checksum hashmap_core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8e04cb7a5051270ef3fa79f8c7604d581ecfa73d520e74f554e45541c4b5881a" -"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" -"checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" -"checksum hex-literal 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ddc2928beef125e519d69ae1baa8c37ea2e0d3848545217f6db0179c5eb1d639" -"checksum hex-literal-impl 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "520870c3213943eb8d7803e80180d12a6c7ceb4ae74602544529d1643dc4ddda" -"checksum hmac 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7a13f4163aa0c5ca1be584aace0e2212b2e41be5478218d4f657f5f778b2ae2a" -"checksum hmac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f127a908633569f208325f86f71255d3363c79721d7f9fe31cd5569908819771" -"checksum hmac-drbg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4fe727d41d2eec0a6574d887914347e5ff96a3b87177817e2a9820c5c87fecc2" -"checksum http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "eed324f0f0daf6ec10c474f150505af2c143f251722bf9dbd1261bd1f2ee2c1a" -"checksum httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e8734b0cfd3bc3e101ec59100e101c2eecd19282202e87808b3037b442777a83" -"checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" -"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" -"checksum impl-codec 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d2050d823639fbeae26b2b5ba09aca8907793117324858070ade0673c49f793b" -"checksum impl-serde 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5158079de9d4158e0ce1de3ae0bd7be03904efc40b3d7dd8b8c301cbf6b52b56" -"checksum impl-serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d26be4b97d738552ea423f76c4f681012ff06c3fa36fa968656b3679f60b4a1" -"checksum integer-sqrt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ea155abb3ba6f382a75f1418988c05fe82959ed9ce727de427f9cfd425b0c903" -"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 itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" -"checksum js-sys 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "9987e7c13a91d9cf0efe59cca48a3a7a70e2b11695d5a4640f85ae71e28f5e73" -"checksum keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" -"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -"checksum kvdb 0.1.0 (git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7987b8475878fc4d2e1f81d)" = "" -"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" -"checksum libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)" = "42914d39aad277d9e176efbdad68acb1d5443ab65afe0e0e4f0d49352a950880" -"checksum libp2p 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6abde4e6fc777dc06ae2a15202ddedb1a38d7c71ed16bc10fa704b03f73aec37" -"checksum libp2p-core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b4ceb4791289534d4c1ad8e4bd3c6f06d3670efa55ce71482951a287df93ddd1" -"checksum libp2p-core-derive 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "851a59dcaab66c96777ae0cace96de88a700243c3b8360ab51c7e093f3727066" -"checksum libp2p-deflate 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "902b44e92e1f8b7e697b3a186d15c841e0e38037f14286513207a5407650a635" -"checksum libp2p-dns 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71a6630a84552b39e5f752e1f6a951d31f3211079465d2e7af73491b6f48fc3f" -"checksum libp2p-floodsub 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9fced4da0c31e0dc8a759472c65fab41db40c01de2d93bc45e1431c13f0564f0" -"checksum libp2p-identify 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1ba5e882d72c71cdf77f45ab68dd715451d3b78a23085f8d385c7a31ec1b4272" -"checksum libp2p-kad 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d71966dbbb4cedcfcdb1d4c87d5dbb6f3f07b465d1ca74f2624256669997d1f2" -"checksum libp2p-mdns 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cdbdaea6f0049cc09ba5db00308f5b93105a8a33b65ba2e36bd35da707850ea2" -"checksum libp2p-mplex 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b351bfd67e97154e7b60f62402237671486c8a89f83eabdb6838f37d4d5f006" -"checksum libp2p-noise 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44324032b2f9260d2b862c741d79d250dc02298dbba56354a992528a826ee2d5" -"checksum libp2p-ping 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1ac43ffd01de4210cf1b969bbb55a008c77f9ec22b74df26a6590bb6bd4c93f" -"checksum libp2p-plaintext 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0506e10770bcbcb59f2a6154ce93c8fd5cb9730b6ceb5aa1463164af1fd0b9c6" -"checksum libp2p-ratelimit 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3886b79a35c0348497bab763517a9a2b4965173f4b4c7438d59f1e4dcf5122ff" -"checksum libp2p-secio 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b811272e5cd86d39bd71fb94687025d9802b13daf0998ebe0d3f2885c636c51a" -"checksum libp2p-tcp 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b2c54cb75f17557de6ce0149aa03e729455e2d240f84d854272bc4b11012a324" -"checksum libp2p-uds 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fbedf4a1e72a5f67523915414e9e12d71d128731873f0f24d8b878398fb47aa4" -"checksum libp2p-wasm-ext 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c1f615b56aa2a6f4ec07bf9667be9fff8877b9c5bd5335601af47490eda341" -"checksum libp2p-websocket 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a0d1bfe60577558f48a9fdf9f35c0ee2dc5baa01f685ff847d3b5cf4f12ee135" -"checksum libp2p-yamux 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bf4bfc7ff127cd622502dbe56f10513dd6776b970e33d8ebb6e367f0752324f6" -"checksum libsecp256k1 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "688e8d65e495567c2c35ea0001b26b9debf0b4ea11f8cccc954233b75fc3428a" -"checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" -"checksum lock_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed946d4529956a20f2d63ebe1b69996d5a2137c91913fe3ebbeff957f5bca7ff" -"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" -"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" -"checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39" -"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" -"checksum memory-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7623b01a4f1b7acb7cf8e3f678f05e15e6ae26cb0b738dfeb5cc186fd6b82ef4" -"checksum memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" -"checksum merlin 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8c39467de91b004f5b9c06fac5bbc8e7d28309a205ee66905166b70804a71fea" -"checksum miniz-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9e3ae51cea1576ceba0dde3d484d30e6e5b86dee0b2d412fe3a16a15c98202" -"checksum miniz_oxide 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c468f2369f07d651a5d0bb2c9079f8488a66d5466efe42d0c5c6466edcb7f71e" -"checksum miniz_oxide_c_api 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b7fe927a42e3807ef71defb191dc87d4e24479b221e67015fe38ae2b7b447bab" -"checksum mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "83f51996a3ed004ef184e16818edc51fadffe8e7ca68be67f9dee67d84d0ff23" -"checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" -"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" -"checksum multistream-select 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f989d40aab0ed0d83c1cdb4856b5790e980b96548d1a921f280e985eb049f38d" -"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" -"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" -"checksum nohash-hasher 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0d138afcce92d219ccb6eb53d9b1e8a96ac0d633cfd3c53cd9856d96d1741bb8" -"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" -"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 num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a23f0ed30a54abaa0c7e83b1d2d87ada7c3c23078d1d87815af3e3b6385fbba" -"checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" -"checksum once_cell 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "532c29a261168a45ce28948f9537ddd7a5dd272cc513b3017b1e82a88f962c37" -"checksum opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "93f5bb2e8e8dec81642920ccff6b61f1eb94fa3020c5a325c9851ff604152409" -"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" -"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)" = "" -"checksum parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dcb43c05fb71c03b4ea7327bf15694da1e0f23f19d5b1e95bab6c6d74097e336" -"checksum parity-codec-derive 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "00a486fd383382ddcb2de928364b1f82571c1e48274fc43b7667a4738ee4056c" -"checksum parity-multiaddr 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "045b3c7af871285146300da35b1932bb6e4639b66c7c98e85d06a32cbc4e8fa7" -"checksum parity-multihash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05d6a68e07ab34a9e87bd8dd4936f6bb5be21e4f6dbcdbaf04d8e854eba0af01" -"checksum parity-send-wrapper 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aa9777aa91b8ad9dd5aaa04a9b6bcb02c7f1deb952fca5a66034d5e63afc5c6f" -"checksum parity-wasm 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)" = "511379a8194230c2395d2f5fa627a5a7e108a9f976656ce723ae68fca4097bfc" -"checksum parking_lot 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d4d05f1349491390b1730afba60bb20d55761bef489a954546b58b4b34e1e2ac" -"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" -"checksum parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa7767817701cce701d5585b9c4db3cdd02086398322c1d7e8bf5094a96a2ce7" -"checksum parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4db1a8ccf734a7bce794cc19b3df06ed87ab2f3907036b693c68f56b4d4537fa" -"checksum parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad7f7e6ebdc79edff6fdcb87a55b620174f7a989e3eb31b65231f4af57f00b8c" -"checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" -"checksum parking_lot_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb88cb1cb3790baa6776844f968fea3be44956cf184fa1be5a03341f5491278c" -"checksum paste 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1f4a4a1c555c6505821f9d58b8779d0f630a6b7e4e1be24ba718610acf01fa79" -"checksum paste-impl 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "26e796e623b8b257215f27e6c80a5478856cae305f5b59810ff9acdaa34570e6" -"checksum pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" -"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" -"checksum primitive-types 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6e8612a8dc70f26276fed6131c153ca277cf275ee0a5e2a50cd8a69c697beb8f" -"checksum proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e10d4b51f154c8a7fb96fd6dad097cb74b863943ec010ac94b9fd1be8861fe1e" -"checksum proc-macro-hack 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2c725b36c99df7af7bf9324e9c999b9e37d92c8f8caf106d82e1d7953218d2d8" -"checksum proc-macro-hack 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0c1dd4172a1e1f96f709341418f49b11ea6c2d95d53dca08c0f74cbd332d9cf3" -"checksum proc-macro-hack-impl 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2b753ad9ed99dd8efeaa7d2fb8453c8f6bc3e54b97966d35f1bc77ca6865254a" -"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -"checksum protobuf 2.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a151c11a92df0059d6ab446fafa3b21a1210aad4bc2293e1c946e8132b10db01" -"checksum pwasm-utils 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "efb0dcbddbb600f47a7098d33762a00552c671992171637f5bb310b37fe1f0e4" -"checksum quick-error 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5fb6ccf8db7bbcb9c2eae558db5ab4f3da1c2a87e4e597ed394726bc8ea6ca1d" -"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" -"checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db" -"checksum rand 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)" = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" -"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" -"checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" -"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" -"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" -"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -"checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0" -"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" -"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_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 rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "373814f27745b2686b350dd261bfd24576a6fb0e2c5919b3a2b6005f820b0473" -"checksum rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b055d1e92aba6877574d8fe604a63c8b5df60f60e5982bf7ccbb1338ea527356" -"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -"checksum redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)" = "12229c14a0f65c4f1cb046a3b52047cdd9da1f4b30f8a39c5063c8bae515e252" -"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" -"checksum regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8f0a0bcab2fd7d1d7c54fa9eae6f43eddeb9ce2e7352f8518a814a4f65d60c58" -"checksum regex-syntax 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "dcfd8681eebe297b81d98498869d4aae052137651ad7b96822f09ceb690d0a96" -"checksum ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)" = "426bc186e3e95cac1e4a4be125a4aca7e84c2d616ffc02244eef36e2a60a093c" -"checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af" -"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 rw-stream-sink 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9cbe61c20455d3015b2bb7be39e1872310283b8e5a52f5b242b0ac7581fe78" -"checksum ryu 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "b96a9549dc8d48f2c283938303c4b5a77aa29bfbc5b54b084fb1630408899a8f" -"checksum safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7f7bf422d23a88c16d5090d455f182bc99c60af4df6a345c63428acf5129e347" -"checksum schnorrkel 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b5eff518f9bed3d803a0d002af0ab96339b0ebbedde3bec98a684986134b7a39" -"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 sct 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f5adf8fbd58e1b1b52699dc8bed2630faecb6d8c7bee77d009d6bbe4af569b9" -"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum send_wrapper 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a0eddf2e8f50ced781f288c19f18621fa72a3779e3cb58dbf23b07469b0abeb4" -"checksum serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)" = "a72e9b96fa45ce22a4bc23da3858dfccfd60acd28a25bcd328a98fdd6bea43fd" -"checksum serde_derive 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)" = "101b495b109a3e3ca8c4cbe44cf62391527cdfb6ba15821c5ce80bcd5ea23f9f" -"checksum serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "5a23aa71d4a4d43fdbfaac00eff68ba8a06a51759a89ac3304323e800c4dd40d" -"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" -"checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d" -"checksum sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd26bc0e7a2e3a7c959bc494caf58b72ee0c71d67704e9520f736ca7e4853ecf" -"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" -"checksum slog 2.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1e1a2eec401952cd7b12a84ea120e2d57281329940c3f93c2bf04f462539508e" -"checksum slog-json 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ddc0d2aff1f8f325ef660d9a0eb6e6dcd20b30b3f581a5897f58bf42d061c37a" -"checksum slog-scope 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "60c04b4726fa04595ccf2c2dad7bcd15474242c4c5e109a8a376e8a2c9b1539a" -"checksum smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c4488ae950c49d403731982257768f48fada354a5203fe81f9bb6f43ca9002be" -"checksum snow 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5a64f02fd208ef15bd2d1a65861df4707e416151e1272d02c8faafad1c138100" -"checksum soketto 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8cf3ae22c0bce5437c7dce6a2b00e492c19da1feb21ad64a7b6fd7058438c3f2" -"checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3" -"checksum spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44363f6f51401c34e7be73db0db371c04705d35efbe9f7d6082e03a921a32c55" -"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" -"checksum static_assertions 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c19be23126415861cb3a23e501d34a708f7f9b2183c5252d690941c2e69199d5" -"checksum static_slice 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "92a7e0c5e3dfb52e8fbe0e63a1b947bbb17b4036408b151353c4491374931362" -"checksum stream-cipher 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8861bc80f649f5b4c9bd38b696ae9af74499d479dbfb327f0607de6b326a36bc" -"checksum strum 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1810e25f576e7ffce1ff5243b37066da5ded0310b3274c20baaeccb1145b2806" -"checksum strum_macros 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "572a2f4e53dd4c3483fd79e5cc10ddd773a3acb1169bbfe8762365e107110579" -"checksum substrate-bip39 0.2.1 (git+https://github.com/paritytech/substrate-bip39)" = "" -"checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" -"checksum subtle 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "01dca13cf6c3b179864ab3292bd794e757618d35a7766b7c46050c614ba00829" -"checksum syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)" = "a1393e4a97a19c01e900df2aec855a29f71cf02c402e2f443b8d2747c25c5dbe" -"checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" -"checksum termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4096add70612622289f2fdcdbd5086dc81c1e2675e6ae58d6c4f62a16c6d7f2f" -"checksum termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dde0593aeb8d47accea5392b39350015b5eccb12c0d98044d856983d89548dea" -"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" -"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.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e9175261fbdb60781fcd388a4d6cc7e14764a2b629a7ad94abb439aed223a44f" -"checksum tk-listen 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5462b0f968c0457efe38fcd2df7e487096b992419e4f5337b06775a614bbda4b" -"checksum tokio 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "94a1f9396aec29d31bb16c24d155cfa144d1af91c40740125db3131bdaf76da8" -"checksum tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c501eceaf96f0e1793cf26beb63da3d11c738c4a943fdf3746d81d64684c39f" -"checksum tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d16217cad7f1b840c5a97dfb3c43b0c871fef423a6e8d2118c604e843662a443" -"checksum tokio-dns-unofficial 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "82c65483db54eb91b4ef3a9389a3364558590faf30ce473141707c0e16fda975" -"checksum tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "83ea44c6c0773cc034771693711c35c677b4b5a4b21b9e7071704c54de7d555e" -"checksum tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe6dc22b08d6993916647d108a1a7d15b9cd29c4f4496c62b92c45b5041b7af" -"checksum tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5090db468dad16e1a7a54c8c67280c5e4b544f3d3e018f0b913b400261f85926" -"checksum tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6af16bfac7e112bea8b0442542161bfc41cbfa4466b580bdda7d18cb88b911ce" -"checksum tokio-rustls 0.10.0-alpha.3 (registry+https://github.com/rust-lang/crates.io-index)" = "316fdbc899efec48b3b492bd0f339e6d81c4ee96a409257572147ec341943452" -"checksum tokio-sync 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "5b2f843ffdf8d6e1f90bddd48da43f99ab071660cd92b7ec560ef3cdfd7a409a" -"checksum tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1d14b10654be682ac43efee27401d792507e30fd8d26389e1da3b185de2e4119" -"checksum tokio-threadpool 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72558af20be886ea124595ea0f806dd5703b8958e4705429dd58b3d8231f72f2" -"checksum tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "f2106812d500ed25a4f38235b9cae8f78a09edf43203e16e59c3b769a342a60e" -"checksum tokio-trace-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "350c9edade9830dc185ae48ba45667a445ab59f6167ef6d0254ec9d2430d9dd3" -"checksum tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "66268575b80f4a4a710ef83d087fdfeeabdce9b74c797535fbac18a2cb906e92" -"checksum tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "037ffc3ba0e12a0ab4aca92e5234e0dedeb48fddf6ccd260f1f150a36a9f2445" -"checksum toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b8c96d7873fa7ef8bdeb3a9cda3ac48389b4154f32b9803b4bc26220b677b039" -"checksum trie-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1ba73747fd3a64ab531274c04cb588dfa9d30d972d62990831e63fbce2cfec59" -"checksum trie-root 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cfa2e20c4f1418ac2e71ddc418e35e1b56e34022e2146209ffdbf1b2de8b1bd9" -"checksum twofish 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712d261e83e727c8e2dbb75dacac67c36e35db36a958ee504f2164fc052434e1" -"checksum twox-hash 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6c7bcecad121018bdcd6b709fa2325b004878fcb3d3067934ce90749f0faff9a" -"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" -"checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" -"checksum uint 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2143cded94692b156c356508d92888acc824db5bffc0b4089732264c6fcf86d4" -"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" -"checksum unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "141339a08b982d942be2ca06ff8b076563cbe223d1befd5450716790d44e2426" -"checksum unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1967f4cdfc355b37fd76d2a954fb2ed3871034eb4f26d60537d88795cfc332a9" -"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" -"checksum unsigned-varint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2c64cdf40b4a9645534a943668681bcb219faf51874d4b65d2e0abda1b10a2ab" -"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 utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737" -"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" -"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum wasm-bindgen 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "b7ccc7b93cfd13e26700a9e2e41e6305f1951b87e166599069f77d10358100e6" -"checksum wasm-bindgen-backend 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "1953f91b1608eb1522513623c7739f047bb0fed4128ce51a93f08e12cc314645" -"checksum wasm-bindgen-futures 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "fa1af11c73eca3dc8c51c76ea475a4416e912da6402064a49fc6c0214701866d" -"checksum wasm-bindgen-macro 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "0f69da5696545d7ca6607a2e4b1a0edf5a6b36b2c49dbb0f1df6ad1d92884047" -"checksum wasm-bindgen-macro-support 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d4246f3bc73223bbb846f4f2430a60725826a96c9389adf715ed1d5af46dec6" -"checksum wasm-bindgen-shared 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "c08381e07e7a79e5e229ad7c60d15833d19033542cc5dd91d085df59d235f4a6" -"checksum wasm-bindgen-webidl 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "1f42ff7adb8102bf5ad8adbc45b1635c520c8175f9fdf6eb2c54479d485d435a" -"checksum wasm-timer 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad9ac33c834103916e373d648adf65f58c83fb3d8a0f3e6b9a64bca7253a4dca" -"checksum wasmi 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "aebbaef470840d157a5c47c8c49f024da7b1b80e90ff729ca982b2b80447e78b" -"checksum wasmi-validation 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab380192444b3e8522ae79c0a1976e42a82920916ccdfbce3def89f456ea33f3" -"checksum web-sys 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "540b8259eb242ff3a566fa0140bda03a4ece4e5c226e1284b5c95dddcd4341f6" -"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" -"checksum weedle 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc44aa200daee8b1f3a004beaf16554369746f1b4486f0cf93b0caf8a3c2d1e" -"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" -"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" -"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -"checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba" -"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" -"checksum x25519-dalek 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7ee1585dc1484373cbc1cee7aafda26634665cf449436fd6e24bfd1fad230538" -"checksum yamux 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "01bd67889938c48f0049fc60a77341039e6c3eaf16cb7693e6ead7c0ba701295" -"checksum zeroize 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8ddfeb6eee2fb3b262ef6e0898a52b7563bb8e0d5955a313b3cf2f808246ea14" -"checksum zeroize 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b60a6c572b91d8ecb0a460950d84fe5b40699edd07d65f73789b31237afc8f66" -"checksum zeroize_derive 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9dac4b660d969bff9c3fe1847a891cacaa8b21dd5f2aae6e0a3e0975aea96431" diff --git a/node/runtime/wasm/Cargo.toml b/node/runtime/wasm/Cargo.toml deleted file mode 100644 index e98d4cb918a916577c63a3de5298c0510bdc08c8..0000000000000000000000000000000000000000 --- a/node/runtime/wasm/Cargo.toml +++ /dev/null @@ -1,28 +0,0 @@ -[package] -name = "node-runtime-wasm" -version = "2.0.0" -authors = ["Parity Technologies "] -edition = "2018" - -[lib] -name = "node_runtime" -crate-type = ["cdylib"] - -[dependencies] -node-runtime = { path = "..", default-features = false } - -[features] -default = ["core"] -core = [ - "node-runtime/core", -] -std = [ - "node-runtime/std", -] - -[profile.release] -panic = "abort" -lto = true - -[workspace] -members = [] diff --git a/node/runtime/wasm/build.sh b/node/runtime/wasm/build.sh deleted file mode 100755 index 4a81e47f9ee843943de032c4177c8b693f6e92d5..0000000000000000000000000000000000000000 --- a/node/runtime/wasm/build.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash -set -e - -if cargo --version | grep -q "nightly"; then - CARGO_CMD="cargo" -else - CARGO_CMD="cargo +nightly" -fi -CARGO_INCREMENTAL=0 RUSTFLAGS="-C link-arg=--export-table" $CARGO_CMD build --target=wasm32-unknown-unknown --release "$@" -for i in node_runtime -do - wasm-gc target/wasm32-unknown-unknown/release/$i.wasm target/wasm32-unknown-unknown/release/$i.compact.wasm -done diff --git a/scripts/build.sh b/scripts/build.sh deleted file mode 100755 index d79ebe52301e5e0d764eff3203a0feda2b09c771..0000000000000000000000000000000000000000 --- a/scripts/build.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bash - -# This script assumes that all pre-requisites are installed. - -set -e - -PROJECT_ROOT=`git rev-parse --show-toplevel` -source "`dirname \"$0\"`/common.sh" - -export CARGO_INCREMENTAL=0 - -# Save current directory. -pushd . - -cd -- "$ROOT" - -for SRC in "${SRCS[@]}" -do - echo "*** Building wasm binaries in $SRC" - cd "$PROJECT_ROOT/$SRC" - - ./build.sh "$@" - - cd - >> /dev/null -done - -# Restore initial directory. -popd diff --git a/scripts/gitlab/check_runtime.sh b/scripts/gitlab/check_runtime.sh index 03c46601360d16d49dff9c95d198f29b0757d40d..725c5077c56494838ba283a59bddf991585bbdef 100755 --- a/scripts/gitlab/check_runtime.sh +++ b/scripts/gitlab/check_runtime.sh @@ -112,10 +112,7 @@ else # drop through into pushing `gotissues` and exit 1... fi -# dropped through. there's something wrong; mark `gotissues` and exit 1. - -github_label "A4-gotissues" - +# dropped through. there's something wrong; exit 1. exit 1 diff --git a/scripts/node-template-release/src/main.rs b/scripts/node-template-release/src/main.rs index 470d34ef0d8837f335e5842e8168b49a1511ab51..4036104f6030a243f44deaf25664597ad13ed69b 100644 --- a/scripts/node-template-release/src/main.rs +++ b/scripts/node-template-release/src/main.rs @@ -136,9 +136,6 @@ fn write_cargo_toml(path: &Path, cargo_toml: CargoToml) { /// Build and test the generated node-template fn build_and_test(path: &Path, cargo_tomls: &[PathBuf]) { - // Build wasm - assert!(Command::new(path.join("./scripts/build.sh")).current_dir(path).status().expect("Compiles wasm").success()); - // Build node assert!(Command::new("cargo").args(&["build", "--all"]).current_dir(path).status().expect("Compiles node").success()); diff --git a/scripts/sentry-node/docker-compose.yml b/scripts/sentry-node/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..78f8ba36b82eed93e7c72a0b40ea43d9a85364fd --- /dev/null +++ b/scripts/sentry-node/docker-compose.yml @@ -0,0 +1,140 @@ +# Docker compose file to simulate a sentry node setup. +# +# +# Setup: +# +# Validator A is not supposed to be connected to the public internet. Instead it +# connects to a sentry node (sentry-a) which connects to the public internet. +# Validator B can reach validator A via sentry node A and vice versa. +# +# +# Usage: +# +# 1. Build `target/release/substrate` binary: `cargo build --release` +# +# 2. Start networks and containers: `sudo docker-compose -f scripts/sentry-node/docker-compose.yml up` +# +# 3. Reach: +# - polkadot/apps on localhost:3000 +# - validator-a: localhost:9944 +# - validator-b: localhost:9945 +# - sentry-a: localhost:9946 + +version: "3.7" +services: + + validator-a: + ports: + - "9944:9944" + volumes: + - ../../target/release/substrate:/usr/local/bin/substrate + image: parity/substrate + networks: + - network-a + command: + # Local node id: QmRpheLN4JWdAnY7HGJfWFNbfkQCb6tFf4vvA6hgjMZKrR + - "--node-key" + - "0000000000000000000000000000000000000000000000000000000000000001" + - "--base-path" + - "/tmp/alice" + - "--chain=local" + - "--key" + - "//Alice" + - "--port" + - "30333" + - "--validator" + - "--name" + - "AlicesNode" + - "--reserved-nodes" + - "/dns4/sentry-a/tcp/30333/p2p/QmV7EhW6J6KgmNdr558RH1mPx2xGGznW7At4BhXzntRFsi" + # Not only bind to localhost. + - "--ws-external" + - "--rpc-external" + # - "--log" + # - "sub-libp2p=trace" + # - "--log" + # - "afg=trace" + - "--no-telemetry" + - "--rpc-cors" + - "all" + + sentry-a: + image: parity/substrate + ports: + - "9946:9944" + volumes: + - ../../target/release/substrate:/usr/local/bin/substrate + networks: + - network-a + - internet + command: + # Local node id: QmV7EhW6J6KgmNdr558RH1mPx2xGGznW7At4BhXzntRFsi + - "--node-key" + - "0000000000000000000000000000000000000000000000000000000000000003" + - "--base-path" + - "/tmp/sentry" + - "--chain=local" + # Don't configure a key, as sentry-a is not a validator. + # - "--key" + # - "//Charlie" + - "--port" + - "30333" + # sentry-a is not a validator. + # - "--validator" + - "--name" + - "CharliesNode" + - "--bootnodes" + - "/dns4/validator-a/tcp/30333/p2p/QmRpheLN4JWdAnY7HGJfWFNbfkQCb6tFf4vvA6hgjMZKrR" + - "--bootnodes" + - "/dns4/validator-b/tcp/30333/p2p/QmSVnNf9HwVMT1Y4cK1P6aoJcEZjmoTXpjKBmAABLMnZEk" + - "--no-telemetry" + - "--rpc-cors" + - "all" + # Not only bind to localhost. + - "--ws-external" + - "--rpc-external" + # Make sure sentry-a still participates as a grandpa voter to forward + # grandpa finality gossip messages. + - "--grandpa-voter" + + validator-b: + image: parity/substrate + ports: + - "9945:9944" + volumes: + - ../../target/release/substrate:/usr/local/bin/substrate + networks: + - internet + command: + # Local node id: QmSVnNf9HwVMT1Y4cK1P6aoJcEZjmoTXpjKBmAABLMnZEk + - "--node-key" + - "0000000000000000000000000000000000000000000000000000000000000002" + - "--base-path" + - "/tmp/bob" + - "--chain=local" + - "--key" + - "//Bob" + - "--port" + - "30333" + - "--validator" + - "--name" + - "BobsNode" + - "--bootnodes" + - "/dns4/validator-a/tcp/30333/p2p/QmRpheLN4JWdAnY7HGJfWFNbfkQCb6tFf4vvA6hgjMZKrR" + - "--bootnodes" + - "/dns4/sentry-a/tcp/30333/p2p/QmV7EhW6J6KgmNdr558RH1mPx2xGGznW7At4BhXzntRFsi" + - "--no-telemetry" + - "--rpc-cors" + - "all" + # Not only bind to localhost. + - "--ws-external" + - "--rpc-external" + + ui: + image: polkadot-js/apps + ports: + - "3000:80" + +networks: + network-a: + internet: diff --git a/srml/README.adoc b/srml/README.adoc index 81b4f216e67e433466ec68292a31cff4ddd734ac..05da2de0a03572da454c95cae41fa20868eac22d 100644 --- a/srml/README.adoc +++ b/srml/README.adoc @@ -1,4 +1,26 @@ -= Runtime += SRML -Set of libs for the substrate runtime. +The Substrate Runtime Module Library (SRML) is a collection of runtime modules. + +== What are runtime modules? + +A Substrate runtime can be composed of several smaller components for separation of concerns. These components are called runtime _modules_. Each runtime module packages together a set of functions (dispatchable extrinsic calls, public or private, mutable or immutable), storage items, and events. + +There are four primary components that support runtime modules: + +=== system module + +https://github.com/paritytech/substrate/tree/master/srml/system[`system`] provides low-level APIs and utilities for other modules. https://github.com/paritytech/substrate/tree/master/srml/system[`system`] also defines all core types and extrinsic events for the Substrate runtime. *All modules depend on the system module.* + +=== executive module + +https://github.com/paritytech/substrate/tree/master/srml/executive[`executive`] dispatches incoming extrinsic calls to the respective modules in the runtime. + +=== support macros + +https://github.com/paritytech/substrate/tree/master/srml/support[`support` macros] are a collection of Rust macros to facilitate the implementation of common module components. https://github.com/paritytech/substrate/tree/master/srml/support[`support` macros] expand at runtime to generate types (e.g. `Module`, `Call`, `Store`, `Event`) which are thereafter used by the runtime to communicate with the modules. Common support macros include https://crates.parity.io/srml_support/macro.decl_module.html[`decl_module`], https://crates.parity.io/srml_support_procedural/macro.decl_storage.html[`decl_storage`], https://crates.parity.io/srml_support/macro.decl_event.html[`decl_event`], and https://crates.parity.io/srml_support/macro.ensure.html[`ensure`]. + +=== runtime + +The runtime expands the support macros to get type and trait implementations for each module before calling https://github.com/paritytech/substrate/tree/master/srml/executive[`executive`] to dispatch calls to the individual modules. To see an example of how this might look, see https://github.com/paritytech/substrate/blob/master/node/runtime/src/lib.rs[`../node/runtime`]. \ No newline at end of file diff --git a/srml/assets/Cargo.toml b/srml/assets/Cargo.toml index 88a443db94cdb80c3b48e8f1407193b3e16ad8d2..977248a7a7ca144010f16bd0cfd011f6f15ead05 100644 --- a/srml/assets/Cargo.toml +++ b/srml/assets/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" [dependencies] serde = { version = "1.0", optional = true } -parity-codec = { version = "3.3", default-features = false } +parity-codec = { version = "4.1.1", default-features = false } # Needed for various traits. In our case, `OnFinalize`. primitives = { package = "sr-primitives", path = "../../core/sr-primitives", default-features = false } # Needed for type-safe access to storage DB. diff --git a/srml/assets/src/lib.rs b/srml/assets/src/lib.rs index d563db3b14146eb01bd22a937e10867a03c17ee2..940f7cdf3f1c4434977b610c898966befec49080 100644 --- a/srml/assets/src/lib.rs +++ b/srml/assets/src/lib.rs @@ -240,15 +240,11 @@ mod tests { use super::*; use runtime_io::with_externalities; - use srml_support::{impl_outer_origin, assert_ok, assert_noop}; + use srml_support::{impl_outer_origin, assert_ok, assert_noop, parameter_types}; use substrate_primitives::{H256, Blake2Hasher}; // 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 primitives::{ - BuildStorage, - traits::{BlakeTwo256, IdentityLookup}, - testing::Header - }; + use primitives::{traits::{BlakeTwo256, IdentityLookup}, testing::Header}; impl_outer_origin! { pub enum Origin for Test {} @@ -259,6 +255,11 @@ mod tests { // 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; + } impl system::Trait for Test { type Origin = Origin; type Index = u64; @@ -268,7 +269,11 @@ mod tests { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; + type WeightMultiplierUpdate = (); type Event = (); + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; } impl Trait for Test { type Event = (); @@ -280,7 +285,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 { - system::GenesisConfig::::default().build_storage().unwrap().0.into() + system::GenesisConfig::default().build_storage::().unwrap().0.into() } #[test] diff --git a/srml/aura/Cargo.toml b/srml/aura/Cargo.toml index 5624df7be6c54f6b90effeb4b2f7ea10b375c90f..c1bf922a581af181a4a10fa9f132426e24c56fab 100644 --- a/srml/aura/Cargo.toml +++ b/srml/aura/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -parity-codec = { version = "3.3", default-features = false, features = ["derive"] } +parity-codec = { version = "4.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0", optional = true } inherents = { package = "substrate-inherents", path = "../../core/inherents", default-features = false } rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false } diff --git a/srml/aura/src/lib.rs b/srml/aura/src/lib.rs index 43f2da88af8000571631d501b6e3a41a52e928ab..a0f7ad6242429f96eb513beba333590a6d28ff0c 100644 --- a/srml/aura/src/lib.rs +++ b/srml/aura/src/lib.rs @@ -52,8 +52,11 @@ pub use timestamp; use rstd::{result, prelude::*}; use parity_codec::Encode; -use srml_support::{decl_storage, decl_module, Parameter, storage::StorageValue}; -use primitives::{traits::{SaturatedConversion, Saturating, Zero, One, Member}, generic::DigestItem}; +use srml_support::{decl_storage, decl_module, Parameter, storage::StorageValue, traits::Get}; +use primitives::{ + traits::{SaturatedConversion, Saturating, Zero, One, Member, IsMember, TypedKey}, + generic::DigestItem, +}; use timestamp::OnTimestampSet; #[cfg(feature = "std")] use timestamp::TimestampInherentData; @@ -153,7 +156,7 @@ pub trait Trait: timestamp::Trait { type HandleReport: HandleReport; /// The identifier type for an authority. - type AuthorityId: Member + Parameter + Default; + type AuthorityId: Member + Parameter + TypedKey + Default; } decl_storage! { @@ -184,7 +187,8 @@ impl Module { impl session::OneSessionHandler for Module { type Key = T::AuthorityId; - fn on_new_session<'a, I: 'a>(changed: bool, validators: I) + + fn on_new_session<'a, I: 'a>(changed: bool, validators: I, _queued_validators: I) where I: Iterator { // instant changes @@ -196,8 +200,21 @@ impl session::OneSessionHandler for Module { } } } - fn on_disabled(_i: usize) { - // ignore? + fn on_disabled(i: usize) { + let log: DigestItem = DigestItem::Consensus( + AURA_ENGINE_ID, + ConsensusLog::::OnDisabled(i as u64).encode(), + ); + + >::deposit_log(log.into()); + } +} + +impl IsMember for Module { + fn is_member(authority_id: &T::AuthorityId) -> bool { + Self::authorities() + .iter() + .any(|id| id == authority_id) } } @@ -234,7 +251,7 @@ impl Module { pub fn slot_duration() -> T::Moment { // we double the minimum block-period so each author can always propose within // the majority of its slot. - >::minimum_period().saturating_mul(2.into()) + ::MinimumPeriod::get().saturating_mul(2.into()) } fn on_timestamp_set(now: T::Moment, slot_duration: T::Moment) { @@ -274,7 +291,8 @@ pub struct StakingSlasher(::rstd::marker::PhantomData); impl HandleReport for StakingSlasher { fn handle_report(report: AuraReport) { - let validators = session::Module::::validators(); + use staking::SessionInterface; + let validators = T::SessionInterface::validators(); report.punish( validators.len(), diff --git a/srml/aura/src/mock.rs b/srml/aura/src/mock.rs index e9c43850f6e01648c99809be89dc18a701abaa32..4348f6af2da4eaa65fd20bd795dded59cc9cc93f 100644 --- a/srml/aura/src/mock.rs +++ b/srml/aura/src/mock.rs @@ -18,8 +18,11 @@ #![cfg(test)] -use primitives::{BuildStorage, traits::IdentityLookup, testing::{Header, UintAuthorityId}}; -use srml_support::impl_outer_origin; +use primitives::{ + traits::IdentityLookup, + testing::{Header, UintAuthorityId}, +}; +use srml_support::{impl_outer_origin, parameter_types}; use runtime_io; use substrate_primitives::{H256, Blake2Hasher}; use crate::{Trait, Module, GenesisConfig}; @@ -32,6 +35,13 @@ impl_outer_origin!{ #[derive(Clone, PartialEq, Eq, Debug)] pub struct Test; +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const MinimumPeriod: u64 = 1; +} + impl system::Trait for Test { type Origin = Origin; type Index = u64; @@ -41,12 +51,17 @@ impl system::Trait for Test { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; + type WeightMultiplierUpdate = (); type Event = (); + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; } impl timestamp::Trait for Test { type Moment = u64; type OnTimestampSet = Aura; + type MinimumPeriod = MinimumPeriod; } impl Trait for Test { @@ -55,10 +70,7 @@ impl Trait for Test { } pub fn new_test_ext(authorities: Vec) -> runtime_io::TestExternalities { - let mut t = system::GenesisConfig::::default().build_storage().unwrap().0; - t.extend(timestamp::GenesisConfig::{ - minimum_period: 1, - }.build_storage().unwrap().0); + let mut t = system::GenesisConfig::default().build_storage::().unwrap().0; t.extend(GenesisConfig::{ authorities: authorities.into_iter().map(|a| UintAuthorityId(a)).collect(), }.build_storage().unwrap().0); diff --git a/srml/authorship/Cargo.toml b/srml/authorship/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..0cf2f1e256f0cbd839f76e1aeafff02251208a2a --- /dev/null +++ b/srml/authorship/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "srml-authorship" +version = "0.1.0" +description = "Block and Uncle Author tracking for the SRML" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +substrate-primitives = { path = "../../core/primitives", default-features = false } +parity-codec = { version = "4.1.1", default-features = false, features = ["derive"] } +rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false } +primitives = { package = "sr-primitives", path = "../../core/sr-primitives", default-features = false } +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 } + +[features] +default = ["std"] +std = [ + "parity-codec/std", + "substrate-primitives/std", + "rstd/std", + "srml-support/std", + "primitives/std", + "system/std", + "runtime_io/std", +] diff --git a/srml/authorship/src/lib.rs b/srml/authorship/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..50b27f83f669435d87fae0c68058a722e0479759 --- /dev/null +++ b/srml/authorship/src/lib.rs @@ -0,0 +1,637 @@ +// 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 . + +//! Authorship tracking for SRML runtimes. +//! +//! This tracks the current author of the block and recent uncles. + +#![cfg_attr(not(feature = "std"), no_std)] + +use rstd::prelude::*; +use rstd::collections::btree_set::BTreeSet; +use srml_support::{decl_module, decl_storage, for_each_tuple, StorageValue}; +use srml_support::traits::{FindAuthor, VerifySeal, Get}; +use srml_support::dispatch::Result as DispatchResult; +use parity_codec::{Encode, Decode}; +use system::ensure_none; +use primitives::traits::{SimpleArithmetic, Header as HeaderT, One, Zero}; + +pub trait Trait: system::Trait { + /// Find the author of a block. + type FindAuthor: FindAuthor; + /// The number of blocks back we should accept uncles. + /// This means that we will deal with uncle-parents that are + /// `UncleGenerations + 1` before `now`. + type UncleGenerations: Get; + /// A filter for uncles within a block. This is for implementing + /// further constraints on what uncles can be included, other than their ancestry. + /// + /// For PoW, as long as the seals are checked, there is no need to use anything + /// but the `VerifySeal` implementation as the filter. This is because the cost of making many equivocating + /// uncles is high. + /// + /// For PoS, there is no such limitation, so a further constraint must be imposed + /// beyond a seal check in order to prevent an arbitrary number of + /// equivocating uncles from being included. + /// + /// The `OnePerAuthorPerHeight` filter is good for many slot-based PoS + /// engines. + type FilterUncle: FilterUncle; + /// An event handler for authored blocks. + type EventHandler: EventHandler; +} + +/// An event handler for the authorship module. There is a dummy implementation +/// for `()`, which does nothing. +pub trait EventHandler { + /// Note that the given account ID is the author of the current block. + fn note_author(author: Author); + + /// Note that the given account ID authored the given uncle, and how many + /// blocks older than the current block it is (age >= 0, so siblings are allowed) + fn note_uncle(author: Author, age: BlockNumber); +} + +macro_rules! impl_event_handler { + () => ( + impl EventHandler for () { + fn note_author(_author: A) { } + fn note_uncle(_author: A, _age: B) { } + } + ); + + ( $($t:ident)* ) => { + impl),*> + EventHandler for ($($t,)*) + { + fn note_author(author: Author) { + $($t::note_author(author.clone());)* + } + fn note_uncle(author: Author, age: BlockNumber) { + $($t::note_uncle(author.clone(), age.clone());)* + } + } + } +} + +for_each_tuple!(impl_event_handler); + +/// Additional filtering on uncles that pass preliminary ancestry checks. +/// +/// This should do work such as checking seals +pub trait FilterUncle { + /// An accumulator of data about uncles included. + /// + /// In practice, this is used to validate uncles against others in the same block. + type Accumulator: Default; + + /// Do additional filtering on a seal-checked uncle block, with the accumulated + /// filter. + fn filter_uncle(header: &Header, acc: Self::Accumulator) + -> Result<(Option, Self::Accumulator), &'static str>; +} + +impl FilterUncle for () { + type Accumulator = (); + fn filter_uncle(_: &H, acc: Self::Accumulator) + -> Result<(Option, Self::Accumulator), &'static str> + { + Ok((None, acc)) + } +} + +/// A filter on uncles which verifies seals and does no additional checks. +/// This is well-suited to consensus modes such as PoW where the cost of +/// equivocating is high. +pub struct SealVerify(rstd::marker::PhantomData); + +impl> FilterUncle + for SealVerify +{ + type Accumulator = (); + + fn filter_uncle(header: &Header, _acc: ()) + -> Result<(Option, ()), &'static str> + { + T::verify_seal(header).map(|author| (author, ())) + } +} + +/// A filter on uncles which verifies seals and ensures that there is only +/// one uncle included per author per height. +/// +/// This does O(n log n) work in the number of uncles included. +pub struct OnePerAuthorPerHeight(rstd::marker::PhantomData<(T, N)>); + +impl FilterUncle + for OnePerAuthorPerHeight +where + Header: HeaderT + PartialEq, + Header::Number: Ord, + Author: Clone + PartialEq + Ord, + T: VerifySeal, +{ + type Accumulator = BTreeSet<(Header::Number, Author)>; + + fn filter_uncle(header: &Header, mut acc: Self::Accumulator) + -> Result<(Option, Self::Accumulator), &'static str> + { + let author = T::verify_seal(header)?; + let number = header.number(); + + if let Some(ref author) = author { + if !acc.insert((number.clone(), author.clone())) { + return Err("more than one uncle per number per author included"); + } + } + + Ok((author, acc)) + } +} + +#[derive(Encode, Decode)] +#[cfg_attr(any(feature = "std", test), derive(PartialEq, Debug))] +enum UncleEntryItem { + InclusionHeight(BlockNumber), + Uncle(Hash, Option), +} + +decl_storage! { + trait Store for Module as Authorship { + /// Uncles + Uncles: Vec>; + /// Author of current block. + Author: Option; + /// Whether uncles were already set in this block. + DidSetUncles: bool; + } +} + +fn prune_old_uncles( + minimum_height: BlockNumber, + uncles: &mut Vec> +) where BlockNumber: SimpleArithmetic { + let prune_entries = uncles.iter().take_while(|item| match item { + UncleEntryItem::Uncle(_, _) => true, + UncleEntryItem::InclusionHeight(height) => height < &minimum_height, + }); + let prune_index = prune_entries.count(); + + let _ = uncles.drain(..prune_index); +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + fn on_initialize(now: T::BlockNumber) { + let uncle_generations = T::UncleGenerations::get(); + let mut uncles = ::Uncles::get(); + + // prune uncles that are older than the allowed number of generations. + if uncle_generations <= now { + let minimum_height = now - uncle_generations; + prune_old_uncles(minimum_height, &mut uncles) + } + + ::DidSetUncles::put(false); + + T::EventHandler::note_author(Self::author()); + } + + fn on_finalize() { + // ensure we never go to trie with these values. + ::Author::kill(); + ::DidSetUncles::kill(); + } + + /// Provide a set of uncles. + fn set_uncles(origin, new_uncles: Vec) -> DispatchResult { + ensure_none(origin)?; + + if ::DidSetUncles::get() { + return Err("Uncles already set in block."); + } + ::DidSetUncles::put(true); + + Self::verify_and_import_uncles(new_uncles) + } + } +} + +impl Module { + /// Fetch the author of the block. + /// + /// This is safe to invoke in `on_initialize` implementations, as well + /// as afterwards. + pub fn author() -> T::AccountId { + // Check the memoized storage value. + if let Some(author) = ::Author::get() { + return author; + } + + let digest = >::digest(); + let pre_runtime_digests = digest.logs.iter().filter_map(|d| d.as_pre_runtime()); + if let Some(author) = T::FindAuthor::find_author(pre_runtime_digests) { + ::Author::put(&author); + author + } else { + Default::default() + } + } + + fn verify_and_import_uncles(new_uncles: Vec) -> DispatchResult { + let now = >::block_number(); + + let (minimum_height, maximum_height) = { + let uncle_generations = T::UncleGenerations::get(); + let min = if now >= uncle_generations { + now - uncle_generations + } else { + Zero::zero() + }; + + (min, now) + }; + + let mut uncles = ::Uncles::get(); + uncles.push(UncleEntryItem::InclusionHeight(now)); + + let mut acc: >::Accumulator = Default::default(); + + for uncle in new_uncles { + let hash = uncle.hash(); + + if uncle.number() < &One::one() { + return Err("uncle is genesis"); + } + + if uncle.number() > &maximum_height { + return Err("uncles too high in chain"); + } + + { + let parent_number = uncle.number().clone() - One::one(); + let parent_hash = >::block_hash(&parent_number); + if &parent_hash != uncle.parent_hash() { + return Err("uncle parent not in chain"); + } + } + + if uncle.number() < &minimum_height { + return Err("uncle not recent enough to be included"); + } + + let duplicate = uncles.iter().find(|entry| match entry { + UncleEntryItem::InclusionHeight(_) => false, + UncleEntryItem::Uncle(h, _) => h == &hash, + }).is_some(); + + let in_chain = >::block_hash(uncle.number()) == hash; + + if duplicate || in_chain { return Err("uncle already included") } + + // check uncle validity. + let (author, temp_acc) = T::FilterUncle::filter_uncle(&uncle, acc)?; + acc = temp_acc; + + T::EventHandler::note_uncle( + author.clone().unwrap_or_default(), + now - uncle.number().clone(), + ); + uncles.push(UncleEntryItem::Uncle(hash, author)); + } + + ::Uncles::put(&uncles); + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use runtime_io::with_externalities; + use substrate_primitives::{H256, Blake2Hasher}; + use primitives::traits::{BlakeTwo256, IdentityLookup}; + use primitives::testing::Header; + use primitives::generic::DigestItem; + use srml_support::{parameter_types, impl_outer_origin, ConsensusEngineId}; + + impl_outer_origin!{ + pub enum Origin for Test {} + } + + #[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; + } + + impl system::Trait for Test { + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type WeightMultiplierUpdate = (); + type Event = (); + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; + } + + impl Trait for Test { + type FindAuthor = AuthorGiven; + type UncleGenerations = UncleGenerations; + type FilterUncle = SealVerify; + type EventHandler = (); + } + + type System = system::Module; + type Authorship = Module; + + const TEST_ID: ConsensusEngineId = [1, 2, 3, 4]; + + pub struct AuthorGiven; + + impl FindAuthor for AuthorGiven { + fn find_author<'a, I>(digests: I) -> Option + where I: 'a + IntoIterator + { + for (id, data) in digests { + if id == TEST_ID { + return u64::decode(&mut &data[..]); + } + } + + None + } + } + + parameter_types! { + pub const UncleGenerations: u64 = 5; + } + + pub struct VerifyBlock; + + impl VerifySeal for VerifyBlock { + fn verify_seal(header: &Header) -> Result, &'static str> { + let pre_runtime_digests = header.digest.logs.iter().filter_map(|d| d.as_pre_runtime()); + let seals = header.digest.logs.iter().filter_map(|d| d.as_seal()); + + let author = match AuthorGiven::find_author(pre_runtime_digests) { + None => return Err("no author"), + Some(author) => author, + }; + + for (id, seal) in seals { + if id == TEST_ID { + match u64::decode(&mut &seal[..]) { + None => return Err("wrong seal"), + Some(a) => { + if a != author { + return Err("wrong author in seal"); + } + break + } + } + } + } + + Ok(Some(author)) + } + } + + fn seal_header(mut header: Header, author: u64) -> Header { + { + let digest = header.digest_mut(); + digest.logs.push(DigestItem::PreRuntime(TEST_ID, author.encode())); + digest.logs.push(DigestItem::Seal(TEST_ID, author.encode())); + } + + header + } + + + fn create_header(number: u64, parent_hash: H256, state_root: H256) -> Header { + Header::new( + number, + Default::default(), + state_root, + parent_hash, + Default::default(), + ) + } + + fn new_test_ext() -> runtime_io::TestExternalities { + let t = system::GenesisConfig::default().build_storage::().unwrap().0; + t.into() + } + + #[test] + fn prune_old_uncles_works() { + use UncleEntryItem::*; + let mut uncles = vec![ + InclusionHeight(1u32), Uncle((), Some(())), Uncle((), None), Uncle((), None), + InclusionHeight(2u32), Uncle((), None), + InclusionHeight(3u32), Uncle((), None), + ]; + + prune_old_uncles(3, &mut uncles); + + assert_eq!(uncles, vec![InclusionHeight(3), Uncle((), None)]); + } + + #[test] + fn rejects_bad_uncles() { + with_externalities(&mut new_test_ext(), || { + let author_a = 69; + + struct CanonChain { + inner: Vec

, + } + + impl CanonChain { + fn best_hash(&self) -> H256 { + self.inner.last().unwrap().hash() + } + + fn canon_hash(&self, index: usize) -> H256 { + self.inner[index].hash() + } + + fn header(&self, index: usize) -> &Header { + &self.inner[index] + } + + fn push(&mut self, header: Header) { + self.inner.push(header) + } + } + + let mut canon_chain = CanonChain { + inner: vec![seal_header(create_header(0, Default::default(), Default::default()), 999)], + }; + + for number in 1..8 { + System::initialize(&number, &canon_chain.best_hash(), &Default::default(), &Default::default()); + let header = seal_header(System::finalize(), author_a); + canon_chain.push(header); + } + + // initialize so system context is set up correctly. + System::initialize(&8, &canon_chain.best_hash(), &Default::default(), &Default::default()); + + // 2 of the same uncle at once + { + let uncle_a = seal_header( + create_header(3, canon_chain.canon_hash(2), [1; 32].into()), + author_a, + ); + assert_eq!( + Authorship::verify_and_import_uncles(vec![uncle_a.clone(), uncle_a.clone()]), + Err("uncle already included"), + ); + } + + // 2 of the same uncle at different times. + { + let uncle_a = seal_header( + create_header(3, canon_chain.canon_hash(2), [1; 32].into()), + author_a, + ); + + assert!(Authorship::verify_and_import_uncles(vec![uncle_a.clone()]).is_ok()); + + assert_eq!( + Authorship::verify_and_import_uncles(vec![uncle_a.clone()]), + Err("uncle already included"), + ); + } + + // same uncle as ancestor. + { + let uncle_clone = canon_chain.header(5).clone(); + + assert_eq!( + Authorship::verify_and_import_uncles(vec![uncle_clone]), + Err("uncle already included"), + ); + } + + // uncle without valid seal. + { + let unsealed = create_header(3, canon_chain.canon_hash(2), [2; 32].into()); + assert_eq!( + Authorship::verify_and_import_uncles(vec![unsealed]), + Err("no author"), + ); + } + + // old uncles can't get in. + { + assert_eq!(System::block_number(), 8); + + let gen_2 = seal_header( + create_header(2, canon_chain.canon_hash(1), [3; 32].into()), + author_a, + ); + + assert_eq!( + Authorship::verify_and_import_uncles(vec![gen_2]), + Err("uncle not recent enough to be included"), + ); + } + + // siblings are also allowed + { + let other_8 = seal_header( + create_header(8, canon_chain.canon_hash(7), [1; 32].into()), + author_a, + ); + + assert!(Authorship::verify_and_import_uncles(vec![other_8]).is_ok()); + } + }); + } + + #[test] + fn sets_author_lazily() { + with_externalities(&mut new_test_ext(), || { + let author = 42; + let mut header = seal_header( + create_header(1, Default::default(), [1; 32].into()), + author, + ); + + header.digest_mut().pop(); // pop the seal off. + System::initialize(&1, &Default::default(), &Default::default(), header.digest()); + + assert_eq!(Authorship::author(), author); + }); + } + + #[test] + fn one_uncle_per_author_per_number() { + type Filter = OnePerAuthorPerHeight; + + let author_a = 42; + let author_b = 43; + + let mut acc: Option<>::Accumulator> = Some(Default::default()); + let header_a1 = seal_header( + create_header(1, Default::default(), [1; 32].into()), + author_a, + ); + let header_b1 = seal_header( + create_header(1, Default::default(), [1; 32].into()), + author_b, + ); + + let header_a2_1 = seal_header( + create_header(2, Default::default(), [1; 32].into()), + author_a, + ); + let header_a2_2 = seal_header( + create_header(2, Default::default(), [2; 32].into()), + author_a, + ); + + let mut check_filter = move |uncle| { + match Filter::filter_uncle(uncle, acc.take().unwrap()) { + Ok((author, a)) => { + acc = Some(a); + Ok(author) + } + Err(e) => Err(e), + } + }; + + // same height, different author is OK. + assert_eq!(check_filter(&header_a1), Ok(Some(author_a))); + assert_eq!(check_filter(&header_b1), Ok(Some(author_b))); + + // same author, different height. + assert_eq!(check_filter(&header_a2_1), Ok(Some(author_a))); + + // same author, same height (author a, height 2) + assert!(check_filter(&header_a2_2).is_err()); + } +} diff --git a/srml/babe/Cargo.toml b/srml/babe/Cargo.toml index 4b82a6c6bc7b8e9c867eca4101b0c4510f744af4..0a95c272a119555e422fcd0cd6fe0804a97275aa 100644 --- a/srml/babe/Cargo.toml +++ b/srml/babe/Cargo.toml @@ -5,23 +5,24 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -hex-literal = "0.1.4" -parity-codec = { version = "3.5.1", default-features = false, features = ["derive"] } -serde = { version = "1.0.90", optional = true } +hex-literal = "0.2" +parity-codec = { version = "4.1.1", default-features = false, features = ["derive"] } +serde = { version = "1.0", optional = true } inherents = { package = "substrate-inherents", path = "../../core/inherents", default-features = false } rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false } primitives = { package = "sr-primitives", path = "../../core/sr-primitives", default-features = false } +staking = { package = "srml-staking", path = "../staking", default-features = false } srml-support = { path = "../support", default-features = false } system = { package = "srml-system", path = "../system", default-features = false } timestamp = { package = "srml-timestamp", path = "../timestamp", default-features = false } session = { package = "srml-session", path = "../session", default-features = false } babe-primitives = { package = "substrate-consensus-babe-primitives", path = "../../core/consensus/babe/primitives", default-features = false } +runtime_io = { package = "sr-io", path = "../../core/sr-io", default-features = false } [dev-dependencies] lazy_static = "1.3.0" parking_lot = "0.8.0" substrate-primitives = { path = "../../core/primitives" } -runtime_io = { package = "sr-io", path = "../../core/sr-io" } [features] default = ["std"] @@ -36,4 +37,6 @@ std = [ "inherents/std", "babe-primitives/std", "session/std", + "runtime_io/std", + "staking/std", ] diff --git a/srml/babe/src/lib.rs b/srml/babe/src/lib.rs index 0cfc0fb8d059dee5bb077715f425cab1f2293cec..577bda8c0142b5faef6fdaac44f99f7f15dc69ce 100644 --- a/srml/babe/src/lib.rs +++ b/srml/babe/src/lib.rs @@ -17,29 +17,28 @@ //! Consensus extension module for BABE consensus. #![cfg_attr(not(feature = "std"), no_std)] -#![forbid(unsafe_code)] +#![forbid(unused_must_use, unsafe_code, unused_variables, dead_code)] pub use timestamp; use rstd::{result, prelude::*}; -use srml_support::{decl_storage, decl_module, StorageValue}; +use srml_support::{decl_storage, decl_module, StorageValue, traits::FindAuthor, traits::Get}; use timestamp::{OnTimestampSet, Trait}; -use primitives::{generic::DigestItem, traits::{SaturatedConversion, Saturating}}; +use primitives::{generic::DigestItem, ConsensusEngineId}; +use primitives::traits::{IsMember, SaturatedConversion, Saturating, RandomnessBeacon, Convert}; #[cfg(feature = "std")] use timestamp::TimestampInherentData; use parity_codec::{Encode, Decode}; use inherents::{RuntimeString, InherentIdentifier, InherentData, ProvideInherent, MakeFatalError}; #[cfg(feature = "std")] use inherents::{InherentDataProviders, ProvideInherentData}; -use babe_primitives::BABE_ENGINE_ID; - -pub use babe_primitives::AuthorityId; +use babe_primitives::{BABE_ENGINE_ID, ConsensusLog, Weight, Epoch, RawBabePreDigest}; +pub use babe_primitives::{AuthorityId, VRF_OUTPUT_LENGTH, PUBLIC_KEY_LENGTH}; /// The BABE inherent identifier. pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"babeslot"; /// The type of the BABE inherent. pub type InherentType = u64; - /// Auxiliary trait to extract BABE inherent data. pub trait BabeInherentData { /// Get BABE inherent data. @@ -67,6 +66,7 @@ pub struct InherentDataProvider { #[cfg(feature = "std")] impl InherentDataProvider { + /// Constructs `Self` pub fn new(slot_duration: u64) -> Self { Self { slot_duration @@ -97,8 +97,8 @@ impl ProvideInherentData for InherentDataProvider { inherent_data: &mut InherentData, ) -> result::Result<(), RuntimeString> { let timestamp = inherent_data.timestamp_inherent_data()?; - let slot_num = timestamp / self.slot_duration; - inherent_data.put_data(INHERENT_IDENTIFIER, &slot_num) + let slot_number = timestamp / self.slot_duration; + inherent_data.put_data(INHERENT_IDENTIFIER, &slot_number) } fn error_to_string(&self, error: &[u8]) -> Option { @@ -106,18 +106,86 @@ impl ProvideInherentData for InherentDataProvider { } } +/// The length of the BABE randomness +pub const RANDOMNESS_LENGTH: usize = 32; + decl_storage! { trait Store for Module as Babe { - /// The last timestamp. - LastTimestamp get(last): T::Moment; + NextRandomness: [u8; RANDOMNESS_LENGTH]; + + /// Randomness under construction + UnderConstruction: [u8; VRF_OUTPUT_LENGTH]; - /// The current authorities set. - Authorities get(authorities): Vec; + /// Current epoch + pub Authorities get(authorities) config(): Vec<(AuthorityId, Weight)>; + + /// The epoch randomness for the *current* epoch. + /// + /// # Security + /// + /// This MUST NOT be used for gambling, as it can be influenced by a + /// malicious validator in the short term. It MAY be used in many + /// cryptographic protocols, however, so long as one remembers that this + /// (like everything else on-chain) it is public. For example, it can be + /// used where a number is needed that cannot have been chosen by an + /// adversary, for purposes such as public-coin zero-knowledge proofs. + pub Randomness get(randomness): [u8; RANDOMNESS_LENGTH]; + + /// Current epoch index + EpochIndex: u64; } } decl_module! { - pub struct Module for enum Call where origin: T::Origin { } + /// The BABE SRML module + pub struct Module for enum Call where origin: T::Origin { + /// Initialization + fn on_initialize() { + for i in Self::get_inherent_digests() + .logs + .iter() + .filter_map(|s| s.as_pre_runtime()) + .filter_map(|(id, mut data)| if id == BABE_ENGINE_ID { + RawBabePreDigest::decode(&mut data) + } else { + None + }) { + + Self::deposit_vrf_output(&i.vrf_output); + return; + } + } + } +} + +impl RandomnessBeacon for Module { + fn random() -> [u8; VRF_OUTPUT_LENGTH] { + Self::randomness() + } +} + +/// A BABE public key +pub type BabeKey = [u8; PUBLIC_KEY_LENGTH]; + +impl FindAuthor for Module { + fn find_author<'a, I>(digests: I) -> Option where + I: 'a + IntoIterator + { + for (id, mut data) in digests.into_iter() { + if id == BABE_ENGINE_ID { + return Some(RawBabePreDigest::decode(&mut data)?.authority_index); + } + } + return None; + } +} + +impl IsMember for Module { + fn is_member(authority_id: &AuthorityId) -> bool { + >::authorities() + .iter() + .any(|id| &id.0 == authority_id) + } } impl Module { @@ -125,42 +193,100 @@ impl Module { pub fn slot_duration() -> T::Moment { // we double the minimum block-period so each author can always propose within // the majority of their slot. - >::minimum_period().saturating_mul(2.into()) + ::MinimumPeriod::get().saturating_mul(2.into()) } + + fn deposit_consensus(new: U) { + let log: DigestItem = DigestItem::Consensus(BABE_ENGINE_ID, new.encode()); + >::deposit_log(log.into()) + } + + fn get_inherent_digests() -> system::DigestOf { + >::digest() + } + + fn change_epoch(new: Epoch) { + Authorities::put(&new.authorities); + Randomness::put(&new.randomness); + Self::deposit_consensus(ConsensusLog::NextEpochData(new)) + } + + fn deposit_vrf_output(vrf_output: &[u8; VRF_OUTPUT_LENGTH]) { + UnderConstruction::mutate(|z| z.iter_mut().zip(vrf_output).for_each(|(x, y)| *x^=y)) + } + + /// Call this function exactly once when an epoch changes, to update the + /// randomness. Returns the new randomness. + fn randomness_change_epoch(epoch_index: u64) -> [u8; RANDOMNESS_LENGTH] { + let this_randomness = NextRandomness::get(); + let next_randomness = compute_randomness( + this_randomness, + epoch_index, + UnderConstruction::get(), + ); + UnderConstruction::put(&[0; RANDOMNESS_LENGTH]); + NextRandomness::put(&next_randomness); + this_randomness + } + } impl OnTimestampSet for Module { fn on_timestamp_set(_moment: T::Moment) { } } -impl Module { - fn change_authorities(new: Vec) { - >::put(&new); - - let log: DigestItem = DigestItem::Consensus(BABE_ENGINE_ID, new.encode()); - >::deposit_log(log.into()); - } +pub trait Duration { + fn babe_epoch_duration() -> u64; } -impl session::OneSessionHandler for Module { +impl session::OneSessionHandler for Module { type Key = AuthorityId; - fn on_new_session<'a, I: 'a>(changed: bool, validators: I) - where I: Iterator + fn on_new_session<'a, I: 'a>(_changed: bool, _validators: I, queued_validators: I) + where I: Iterator { - // instant changes - if changed { - let next_authorities = validators.map(|(_, k)| k).collect::>(); - let last_authorities = >::authorities(); - if next_authorities != last_authorities { - Self::change_authorities(next_authorities); - } - } + use staking::BalanceOf; + let to_votes = |b: BalanceOf| { + , u64>>::convert(b) + }; + + 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); + + // *Next* epoch's authorities. + let authorities = queued_validators.map(|(account, k)| { + (k, to_votes(staking::Module::::stakers(account).total)) + }).collect::>(); + + // What was the next epoch is now the current epoch + let randomness = Self::randomness_change_epoch(epoch_index); + Self::change_epoch(Epoch { + randomness, + authorities, + epoch_index, + duration: T::babe_epoch_duration(), + }) } - fn on_disabled(_i: usize) { - // ignore? + + fn on_disabled(i: usize) { + Self::deposit_consensus(ConsensusLog::OnDisabled(i as u64)) } } +fn compute_randomness( + last_epoch_randomness: [u8; RANDOMNESS_LENGTH], + epoch_index: u64, + rho: [u8; VRF_OUTPUT_LENGTH], +) -> [u8; RANDOMNESS_LENGTH] { + let mut s = [0; 40 + VRF_OUTPUT_LENGTH]; + s[..32].copy_from_slice(&last_epoch_randomness); + s[32..40].copy_from_slice(&epoch_index.to_le_bytes()); + s[40..].copy_from_slice(&rho); + runtime_io::blake2_256(&s) +} + impl ProvideInherent for Module { type Call = timestamp::Call; type Error = MakeFatalError; @@ -181,7 +307,7 @@ impl ProvideInherent for Module { if timestamp_based_slot == seal_slot { Ok(()) } else { - Err(RuntimeString::from("timestamp set in block doesn’t match slot in seal").into()) + Err(RuntimeString::from("timestamp set in block doesn't match slot in seal").into()) } } } diff --git a/srml/balances/Cargo.toml b/srml/balances/Cargo.toml index 6c000294ba12e057c864d2f0f3add72b7a93c565..cc8f442e04d2d4e70334b7dba9f4f0b46b8a200b 100644 --- a/srml/balances/Cargo.toml +++ b/srml/balances/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" [dependencies] serde = { version = "1.0", optional = true } safe-mix = { version = "1.0", default-features = false} -parity-codec = { version = "3.3", default-features = false, features = ["derive"] } +parity-codec = { version = "4.1.1", default-features = false, features = ["derive"] } substrate-keyring = { path = "../../core/keyring", optional = true } rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false } primitives = { package = "sr-primitives", path = "../../core/sr-primitives", default-features = false } @@ -15,7 +15,7 @@ srml-support = { path = "../support", default-features = false } system = { package = "srml-system", path = "../system", default-features = false } [dev-dependencies] -runtime_io = { package = "sr-io", path = "../../core/sr-io" } +runtime_io = { package = "sr-io", path = "../../core/sr-io", default-features = false } substrate-primitives = { path = "../../core/primitives" } [features] diff --git a/srml/balances/src/lib.rs b/srml/balances/src/lib.rs index 72ec997206550bc0c6b48933c188fa7d246778d5..48960f1e544ebb3869be39eab33eab113d712439 100644 --- a/srml/balances/src/lib.rs +++ b/srml/balances/src/lib.rs @@ -154,22 +154,30 @@ use rstd::{cmp, result, mem}; use parity_codec::{Codec, Encode, Decode}; use srml_support::{StorageValue, StorageMap, Parameter, decl_event, decl_storage, decl_module}; use srml_support::traits::{ - UpdateBalanceOutcome, Currency, OnFreeBalanceZero, MakePayment, OnUnbalanced, + UpdateBalanceOutcome, Currency, OnFreeBalanceZero, OnUnbalanced, WithdrawReason, WithdrawReasons, LockIdentifier, LockableCurrency, ExistenceRequirement, - Imbalance, SignedImbalance, ReservableCurrency + Imbalance, SignedImbalance, ReservableCurrency, Get, }; use srml_support::dispatch::Result; use primitives::traits::{ - Zero, SimpleArithmetic, StaticLookup, Member, CheckedAdd, CheckedSub, - MaybeSerializeDebug, Saturating + Zero, SimpleArithmetic, StaticLookup, Member, CheckedAdd, CheckedSub, MaybeSerializeDebug, + Saturating, Bounded, SignedExtension, SaturatedConversion, DispatchError }; -use system::{IsDeadAccount, OnNewAccount, ensure_signed}; +use primitives::transaction_validity::{TransactionPriority, ValidTransaction}; +use primitives::weights::DispatchInfo; +use system::{IsDeadAccount, OnNewAccount, ensure_signed, ensure_root}; mod mock; mod tests; pub use self::imbalances::{PositiveImbalance, NegativeImbalance}; +pub const DEFAULT_EXISTENTIAL_DEPOSIT: u32 = 0; +pub const DEFAULT_TRANSFER_FEE: u32 = 0; +pub const DEFAULT_CREATION_FEE: u32 = 0; +pub const DEFAULT_TRANSACTION_BASE_FEE: u32 = 0; +pub const DEFAULT_TRANSACTION_BYTE_FEE: u32 = 0; + pub trait Subtrait: system::Trait { /// The balance of an account. type Balance: Parameter + Member + SimpleArithmetic + Codec + Default + Copy + @@ -183,6 +191,21 @@ pub trait Subtrait: system::Trait { /// Handler for when a new account is created. type OnNewAccount: OnNewAccount; + + /// The minimum amount required to keep an account open. + type ExistentialDeposit: Get; + + /// The fee required to make a transfer. + type TransferFee: Get; + + /// The fee required to create an account. + type CreationFee: Get; + + /// The 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; } pub trait Trait: system::Trait { @@ -211,12 +234,32 @@ pub trait Trait: system::Trait { /// The overarching event type. type Event: From> + Into<::Event>; + + /// The minimum amount required to keep an account open. + type ExistentialDeposit: Get; + + /// The fee required to make a transfer. + type TransferFee: Get; + + /// The fee required to create an account. + type CreationFee: Get; + + /// The 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; } impl, I: Instance> Subtrait for T { type Balance = T::Balance; type OnFreeBalanceZero = T::OnFreeBalanceZero; type OnNewAccount = T::OnNewAccount; + type ExistentialDeposit = T::ExistentialDeposit; + type TransferFee = T::TransferFee; + type CreationFee = T::CreationFee; + type TransactionBaseFee = T::TransactionBaseFee; + type TransactionByteFee = T::TransactionByteFee; } decl_event!( @@ -236,20 +279,26 @@ 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))] -pub struct VestingSchedule { +pub struct VestingSchedule { /// Locked amount at genesis. - pub offset: Balance, - /// Amount that gets unlocked every block from genesis. + pub locked: Balance, + /// Amount that gets unlocked every block after `starting_block`. pub per_block: Balance, + /// Starting block for unlocking(vesting). + pub starting_block: BlockNumber, } -impl VestingSchedule { +impl VestingSchedule { /// Amount locked at block `n`. - pub fn locked_at(&self, n: BlockNumber) -> Balance + pub fn locked_at(&self, n: BlockNumber) -> Balance where Balance: From { - if let Some(x) = Balance::from(n).checked_mul(&self.per_block) { - self.offset.max(x) - x + // Number of blocks that count toward vesting + // Saturating to 0 when n < starting_block + let vested_block_count = n.saturating_sub(self.starting_block); + // Return amount that is still locked in vesting + if let Some(x) = Balance::from(vested_block_count).checked_mul(&self.per_block) { + self.locked.max(x) - x } else { Zero::zero() } @@ -271,36 +320,33 @@ decl_storage! { pub TotalIssuance get(total_issuance) build(|config: &GenesisConfig| { config.balances.iter().fold(Zero::zero(), |acc: T::Balance, &(_, n)| acc + n) }): T::Balance; - /// The minimum amount required to keep an account open. - pub ExistentialDeposit get(existential_deposit) config(): T::Balance; - /// The fee required to make a transfer. - pub TransferFee get(transfer_fee) config(): T::Balance; - /// The fee required to create an account. - pub CreationFee get(creation_fee) config(): T::Balance; - /// The fee to be paid for making a transaction; the base. - pub TransactionBaseFee get(transaction_base_fee) config(): T::Balance; - /// The fee to be paid for making a transaction; the per-byte portion. - pub TransactionByteFee get(transaction_byte_fee) config(): T::Balance; /// Information regarding the vesting of a given account. pub Vesting get(vesting) build(|config: &GenesisConfig| { - config.vesting.iter().filter_map(|&(ref who, begin, length)| { - let begin = >::from(begin); + // Generate initial vesting configuration + // * who - Account which we are generating vesting configuration for + // * begin - Block when the account will start to vest + // * length - Number of blocks from `begin` until fully vested + // * liquid - Number of units which can be spent before vesting begins + config.vesting.iter().filter_map(|&(ref who, begin, length, liquid)| { let length = >::from(length); config.balances.iter() .find(|&&(ref w, _)| w == who) .map(|&(_, balance)| { - // <= begin it should be >= balance - // >= begin+length it should be <= 0 - - let per_block = balance / length.max(primitives::traits::One::one()); - let offset = begin * per_block + balance; - - (who.clone(), VestingSchedule { offset, per_block }) + // Total genesis `balance` minus `liquid` equals funds locked for vesting + let locked = balance.saturating_sub(liquid); + // Number of units unlocked per block after `begin` + let per_block = locked / length.max(primitives::traits::One::one()); + + (who.clone(), VestingSchedule { + locked: locked, + per_block: per_block, + starting_block: begin + }) }) }).collect::>() - }): map T::AccountId => Option>; + }): map T::AccountId => Option>; /// The 'free' balance of a given account. /// @@ -313,7 +359,9 @@ 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) build(|config: &GenesisConfig| config.balances.clone()): map T::AccountId => T::Balance; + pub FreeBalance get(free_balance) + build(|config: &GenesisConfig| config.balances.clone()): + map T::AccountId => T::Balance; /// The amount of the balance of a given account that is externally reserved; this can still get /// slashed, but gets slashed last of all. @@ -333,13 +381,28 @@ decl_storage! { } add_extra_genesis { config(balances): Vec<(T::AccountId, T::Balance)>; - config(vesting): Vec<(T::AccountId, T::BlockNumber, T::BlockNumber)>; // begin, length + config(vesting): Vec<(T::AccountId, T::BlockNumber, T::BlockNumber, T::Balance)>; + // ^^ begin, length, amount liquid at genesis } - extra_genesis_skip_phantom_data_field; } decl_module! { pub struct Module, I: Instance = DefaultInstance> for enum Call where origin: T::Origin { + /// The minimum amount required to keep an account open. + const ExistentialDeposit: T::Balance = T::ExistentialDeposit::get(); + + /// The fee required to make a transfer. + const TransferFee: T::Balance = T::TransferFee::get(); + + /// The fee required to create an account. + const CreationFee: T::Balance = T::CreationFee::get(); + + /// 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. @@ -389,10 +452,12 @@ decl_module! { /// - Contains a limited number of reads and writes. /// # fn set_balance( + origin, who: ::Source, #[compact] new_free: T::Balance, #[compact] new_reserved: T::Balance ) { + ensure_root(origin)?; let who = T::Lookup::lookup(who)?; let current_free = >::get(&who); @@ -422,7 +487,7 @@ impl, I: Instance> Module { pub fn vesting_balance(who: &T::AccountId) -> T::Balance { if let Some(v) = Self::vesting(who) { Self::free_balance(who) - .min(v.locked_at::(>::block_number())) + .min(v.locked_at(>::block_number())) } else { Zero::zero() } @@ -439,7 +504,7 @@ impl, I: Instance> Module { /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that /// the caller will do this. fn set_reserved_balance(who: &T::AccountId, balance: T::Balance) -> UpdateBalanceOutcome { - if balance < Self::existential_deposit() { + if balance < T::ExistentialDeposit::get() { >::insert(who, balance); Self::on_reserved_too_low(who); UpdateBalanceOutcome::AccountKilled @@ -460,8 +525,8 @@ impl, I: Instance> Module { fn set_free_balance(who: &T::AccountId, balance: T::Balance) -> UpdateBalanceOutcome { // Commented out for now - but consider it instructive. // assert!(!Self::total_balance(who).is_zero()); - // assert!(Self::free_balance(who) > Self::existential_deposit()); - if balance < Self::existential_deposit() { + // assert!(Self::free_balance(who) > T::ExistentialDeposit::get()); + if balance < T::ExistentialDeposit::get() { >::insert(who, balance); Self::on_free_too_low(who); UpdateBalanceOutcome::AccountKilled @@ -696,7 +761,11 @@ 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; + type MaximumBlockLength = T::MaximumBlockLength; } impl, I: Instance> Trait for ElevatedTrait { type Balance = T::Balance; @@ -706,6 +775,11 @@ impl, I: Instance> Trait for ElevatedTrait { 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; } impl, I: Instance> Currency for Module @@ -729,13 +803,33 @@ where } fn minimum_balance() -> Self::Balance { - Self::existential_deposit() + T::ExistentialDeposit::get() } fn free_balance(who: &T::AccountId) -> Self::Balance { >::get(who) } + fn burn(mut amount: Self::Balance) -> Self::PositiveImbalance { + >::mutate(|issued| + issued.checked_sub(&amount).unwrap_or_else(|| { + amount = *issued; + Zero::zero() + }) + ); + PositiveImbalance::new(amount) + } + + fn issue(mut amount: Self::Balance) -> Self::NegativeImbalance { + >::mutate(|issued| + *issued = issued.checked_add(&amount).unwrap_or_else(|| { + amount = Self::Balance::max_value() - *issued; + Self::Balance::max_value() + }) + ); + NegativeImbalance::new(amount) + } + // # // Despite iterating over a list of locks, they are limited by the number of // lock IDs, which means the number of runtime modules that intend to use and create locks. @@ -774,7 +868,7 @@ where let from_balance = Self::free_balance(transactor); let to_balance = Self::free_balance(dest); let would_create = to_balance.is_zero(); - let fee = if would_create { Self::creation_fee() } else { Self::transfer_fee() }; + let fee = if would_create { T::CreationFee::get() } else { T::TransferFee::get() }; let liability = match value.checked_add(&fee) { Some(l) => l, None => return Err("got overflow after adding a fee to value"), @@ -784,7 +878,7 @@ where None => return Err("balance too low to send value"), Some(b) => b, }; - if would_create && value < Self::existential_deposit() { + 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)?; @@ -816,7 +910,7 @@ where liveness: ExistenceRequirement, ) -> result::Result { if let Some(new_balance) = Self::free_balance(who).checked_sub(&value) { - if liveness == ExistenceRequirement::KeepAlive && new_balance < Self::existential_deposit() { + if liveness == ExistenceRequirement::KeepAlive && new_balance < T::ExistentialDeposit::get() { return Err("payment would kill account") } Self::ensure_can_withdraw(who, value, reason, new_balance)?; @@ -873,12 +967,12 @@ where } } - fn make_free_balance_be(who: &T::AccountId, balance: T::Balance) -> ( + fn make_free_balance_be(who: &T::AccountId, balance: Self::Balance) -> ( SignedImbalance, UpdateBalanceOutcome ) { let original = Self::free_balance(who); - if balance < Self::existential_deposit() && original.is_zero() { + if balance < T::ExistentialDeposit::get() && original.is_zero() { // If we're attempting to set an existing account to less than ED, then // bypass the entire operation. It's a no-op if you follow it through, but // since this is an instance where we might account for a negative imbalance @@ -904,7 +998,7 @@ where // Free balance can never be less than ED. If that happens, it gets reduced to zero // and the account information relevant to this subsystem is deleted (i.e. the // account is reaped). - let outcome = if balance < >::existential_deposit() { + let outcome = if balance < T::ExistentialDeposit::get() { Self::set_free_balance(who, balance); UpdateBalanceOutcome::AccountKilled } else { @@ -1055,18 +1149,79 @@ where } } -impl, I: Instance> MakePayment for Module { - fn make_payment(transactor: &T::AccountId, encoded_len: usize) -> Result { - let encoded_len = T::Balance::from(encoded_len as u32); - let transaction_fee = Self::transaction_base_fee() + Self::transaction_byte_fee() * encoded_len; - let imbalance = Self::withdraw( - transactor, - transaction_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 TakeFees, I: Instance = DefaultInstance>(#[codec(compact)] T::Balance); + +impl, I: Instance> TakeFees { + /// utility constructor. Used only in client/factory code. + #[cfg(feature = "std")] + 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 { + // length fee + let len_fee = if info.pay_length_fee() { + let len = T::Balance::from(len as u32); + let base = T::TransactionBaseFee::get(); + let byte = T::TransactionByteFee::get(); + base.saturating_add(byte.saturating_mul(len)) + } else { + Zero::zero() + }; + + // weight fee + let weight = info.weight; + let weight_fee: T::Balance = >::next_weight_multiplier().apply_to(weight).into(); + + 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 AdditionalSigned = (); + fn additional_signed(&self) -> rstd::result::Result<(), &'static str> { Ok(()) } + + fn validate( + &self, + who: &Self::AccountId, + info: DispatchInfo, + len: usize, + ) -> rstd::result::Result { + // pay any fees. + let fee = Self::compute_fee(len, info, self.0); + let imbalance = >::withdraw( + who, + fee, WithdrawReason::TransactionPayment, - ExistenceRequirement::KeepAlive - )?; + ExistenceRequirement::KeepAlive, + ).map_err(|_| DispatchError::Payment)?; T::TransactionPayment::on_unbalanced(imbalance); - Ok(()) + + 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) } } diff --git a/srml/balances/src/mock.rs b/srml/balances/src/mock.rs index ac5208ab90c2a3c1e7367f706eecfa708118508a..fecec82180d891fa9eb5380c8b6b9629abe8ddd8 100644 --- a/srml/balances/src/mock.rs +++ b/srml/balances/src/mock.rs @@ -18,20 +18,59 @@ #![cfg(test)] -use primitives::BuildStorage; -use primitives::{traits::{IdentityLookup}, testing::Header}; +use primitives::{traits::IdentityLookup, testing::Header, weights::{DispatchInfo, Weight}}; use substrate_primitives::{H256, Blake2Hasher}; use runtime_io; -use srml_support::impl_outer_origin; +use srml_support::{impl_outer_origin, parameter_types}; +use srml_support::traits::Get; +use std::cell::RefCell; use crate::{GenesisConfig, Module, Trait}; impl_outer_origin!{ pub enum Origin for Runtime {} } +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); +} + +pub struct ExistentialDeposit; +impl Get for ExistentialDeposit { + fn get() -> u64 { EXISTENTIAL_DEPOSIT.with(|v| *v.borrow()) } +} + +pub struct TransferFee; +impl Get for TransferFee { + fn get() -> u64 { TRANSFER_FEE.with(|v| *v.borrow()) } +} + +pub struct CreationFee; +impl Get for CreationFee { + fn get() -> u64 { CREATION_FEE.with(|v| *v.borrow()) } +} + +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()) } +} + // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. #[derive(Clone, PartialEq, Eq, Debug)] pub struct Runtime; +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; +} impl system::Trait for Runtime { type Origin = Origin; type Index = u64; @@ -41,7 +80,11 @@ impl system::Trait for Runtime { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; + type WeightMultiplierUpdate = (); type Event = (); + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; } impl Trait for Runtime { type Balance = u64; @@ -51,6 +94,11 @@ impl Trait for Runtime { type TransactionPayment = (); type DustRemoval = (); type TransferPayment = (); + type ExistentialDeposit = ExistentialDeposit; + type TransferFee = TransferFee; + type CreationFee = CreationFee; + type TransactionBaseFee = TransactionBaseFee; + type TransactionByteFee = TransactionByteFee; } pub struct ExtBuilder { @@ -76,6 +124,11 @@ impl Default for ExtBuilder { } } impl ExtBuilder { + pub fn transaction_fees(mut self, base_fee: u64, byte_fee: u64) -> Self { + self.transaction_base_fee = base_fee; + self.transaction_byte_fee = byte_fee; + self + } pub fn existential_deposit(mut self, existential_deposit: u64) -> Self { self.existential_deposit = existential_deposit; self @@ -89,11 +142,6 @@ impl ExtBuilder { self.creation_fee = creation_fee; self } - pub fn transaction_fees(mut self, base_fee: u64, byte_fee: u64) -> Self { - self.transaction_base_fee = base_fee; - self.transaction_byte_fee = byte_fee; - self - } pub fn monied(mut self, monied: bool) -> Self { self.monied = monied; if self.existential_deposit == 0 { @@ -105,21 +153,34 @@ impl ExtBuilder { self.vesting = vesting; self } + pub fn set_associated_consts(&self) { + EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); + TRANSFER_FEE.with(|v| *v.borrow_mut() = self.transfer_fee); + CREATION_FEE.with(|v| *v.borrow_mut() = self.creation_fee); + TRANSACTION_BASE_FEE.with(|v| *v.borrow_mut() = self.transaction_base_fee); + TRANSACTION_BYTE_FEE.with(|v| *v.borrow_mut() = self.transaction_byte_fee); + } pub fn build(self) -> runtime_io::TestExternalities { - let mut t = system::GenesisConfig::::default().build_storage().unwrap().0; + self.set_associated_consts(); + let mut t = system::GenesisConfig::default().build_storage::().unwrap().0; t.extend(GenesisConfig:: { - transaction_base_fee: self.transaction_base_fee, - transaction_byte_fee: self.transaction_byte_fee, balances: if self.monied { - vec![(1, 10 * self.existential_deposit), (2, 20 * self.existential_deposit), (3, 30 * self.existential_deposit), (4, 40 * self.existential_deposit)] + vec![ + (1, 10 * self.existential_deposit), + (2, 20 * self.existential_deposit), + (3, 30 * self.existential_deposit), + (4, 40 * self.existential_deposit), + (12, 10 * self.existential_deposit) + ] } else { vec![] }, - existential_deposit: self.existential_deposit, - transfer_fee: self.transfer_fee, - creation_fee: self.creation_fee, vesting: if self.vesting && self.monied { - vec![(1, 0, 10), (2, 10, 20)] + vec![ + (1, 0, 10, 5 * self.existential_deposit), + (2, 10, 20, 0), + (12, 10, 20, 5 * self.existential_deposit) + ] } else { vec![] }, @@ -130,3 +191,8 @@ impl ExtBuilder { pub type System = system::Module; pub type Balances = Module; + +/// 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() } +} diff --git a/srml/balances/src/tests.rs b/srml/balances/src/tests.rs index 0a5a4b5bb70a69478ae19beddb3940e211b58495..2828d40e63ff80cf5b54ad0e3c67d5d116b9c732 100644 --- a/srml/balances/src/tests.rs +++ b/srml/balances/src/tests.rs @@ -19,12 +19,12 @@ #![cfg(test)] use super::*; -use mock::{Balances, ExtBuilder, Runtime, System}; +use mock::{Balances, ExtBuilder, Runtime, System, info_from_weight}; use runtime_io::with_externalities; use srml_support::{ assert_noop, assert_ok, assert_err, traits::{LockableCurrency, LockIdentifier, WithdrawReason, WithdrawReasons, - Currency, MakePayment, ReservableCurrency} + Currency, ReservableCurrency} }; const ID_1: LockIdentifier = *b"1 "; @@ -111,31 +111,50 @@ 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).build(), || { - Balances::set_lock(ID_1, &1, 10, u64::max_value(), WithdrawReason::Transfer.into()); - assert_noop!( - >::transfer(&1, &2, 1), - "account liquidity restrictions prevent withdrawal" - ); - assert_ok!(>::reserve(&1, 1)); - assert_ok!(>::make_payment(&1, 1)); - - Balances::set_lock(ID_1, &1, 10, u64::max_value(), WithdrawReason::Reserve.into()); - assert_ok!(>::transfer(&1, &2, 1)); - assert_noop!( - >::reserve(&1, 1), - "account liquidity restrictions prevent withdrawal" - ); - assert_ok!(>::make_payment(&1, 1)); - - 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_noop!( - >::make_payment(&1, 1), - "account liquidity restrictions prevent withdrawal" - ); - }); + with_externalities( + &mut ExtBuilder::default() + .existential_deposit(1) + .monied(true).transaction_fees(0, 1) + .build(), + || { + Balances::set_lock(ID_1, &1, 10, u64::max_value(), WithdrawReason::Transfer.into()); + assert_noop!( + >::transfer(&1, &2, 1), + "account liquidity restrictions prevent withdrawal" + ); + assert_ok!(>::reserve(&1, 1)); + // NOTE: this causes a fee payment. + assert!( as SignedExtension>::pre_dispatch( + TakeFees::from(1), + &1, + info_from_weight(1), + 0, + ).is_ok()); + + Balances::set_lock(ID_1, &1, 10, u64::max_value(), WithdrawReason::Reserve.into()); + assert_ok!(>::transfer(&1, &2, 1)); + assert_noop!( + >::reserve(&1, 1), + "account liquidity restrictions prevent withdrawal" + ); + assert!( as SignedExtension>::pre_dispatch( + TakeFees::from(1), + &1, + info_from_weight(1), + 0, + ).is_ok()); + + Balances::set_lock(ID_1, &1, 10, u64::max_value(), WithdrawReason::TransactionPayment.into()); + assert_ok!(>::transfer(&1, &2, 1)); + assert_ok!(>::reserve(&1, 1)); + assert!( as SignedExtension>::pre_dispatch( + TakeFees::from(1), + &1, + info_from_weight(1), + 0, + ).is_err()); + } + ); } #[test] @@ -204,8 +223,9 @@ fn default_indexing_on_new_accounts_should_not_work2() { .monied(true) .build(), || { + assert_eq!(Balances::is_dead_account(&5), true); // account 5 should not exist - // account 1 has 256 * 10 = 2560, account 5 is not exist, ext_deposit is 10, value is 9, not satisfies for ext_deposit + // ext_deposit is 10, value is 9, not satisfies for ext_deposit assert_noop!( Balances::transfer(Some(1).into(), 5, 9), "value too low to create account" @@ -235,16 +255,19 @@ fn reserved_balance_should_prevent_reclaim_count() { assert_eq!(Balances::is_dead_account(&2), false); assert_eq!(System::account_nonce(&2), 1); - assert_ok!(Balances::transfer(Some(4).into(), 5, 256 * 1 + 0x69)); // account 4 tries to take index 1 for account 5. + // account 4 tries to take index 1 for account 5. + assert_ok!(Balances::transfer(Some(4).into(), 5, 256 * 1 + 0x69)); assert_eq!(Balances::total_balance(&5), 256 * 1 + 0x69); assert_eq!(Balances::is_dead_account(&5), false); assert!(Balances::slash(&2, 256 * 18 + 2).1.is_zero()); // account 2 gets slashed - assert_eq!(Balances::total_balance(&2), 0); // "reserve" account reduced to 255 (below ED) so account deleted + // "reserve" account reduced to 255 (below ED) so account deleted + assert_eq!(Balances::total_balance(&2), 0); assert_eq!(System::account_nonce(&2), 0); // nonce zero assert_eq!(Balances::is_dead_account(&2), true); - assert_ok!(Balances::transfer(Some(4).into(), 6, 256 * 1 + 0x69)); // account 4 tries to take index 1 again for account 6. + // account 4 tries to take index 1 again for account 6. + assert_ok!(Balances::transfer(Some(4).into(), 6, 256 * 1 + 0x69)); assert_eq!(Balances::total_balance(&6), 256 * 1 + 0x69); assert_eq!(Balances::is_dead_account(&6), false); }, @@ -258,7 +281,7 @@ fn reward_should_work() { assert_eq!(Balances::total_balance(&1), 10); assert_ok!(Balances::deposit_into_existing(&1, 10).map(drop)); assert_eq!(Balances::total_balance(&1), 20); - assert_eq!(>::get(), 110); + assert_eq!(>::get(), 120); }); } @@ -294,7 +317,8 @@ fn dust_account_removal_should_work2() { System::inc_account_nonce(&2); assert_eq!(System::account_nonce(&2), 1); assert_eq!(Balances::total_balance(&2), 2000); - assert_ok!(Balances::transfer(Some(2).into(), 5, 1851)); // index 1 (account 2) becomes zombie for 256*10 + 50(fee) < 256 * 10 (ext_deposit) + // index 1 (account 2) becomes zombie for 256*10 + 50(fee) < 256 * 10 (ext_deposit) + assert_ok!(Balances::transfer(Some(2).into(), 5, 1851)); assert_eq!(Balances::total_balance(&2), 0); assert_eq!(Balances::total_balance(&5), 1851); assert_eq!(System::account_nonce(&2), 0); @@ -571,32 +595,52 @@ fn check_vesting_status() { assert_eq!(System::block_number(), 1); let user1_free_balance = Balances::free_balance(&1); let user2_free_balance = Balances::free_balance(&2); + let user12_free_balance = Balances::free_balance(&12); assert_eq!(user1_free_balance, 256 * 10); // Account 1 has free balance assert_eq!(user2_free_balance, 256 * 20); // Account 2 has free balance + assert_eq!(user12_free_balance, 256 * 10); // Account 12 has free balance let user1_vesting_schedule = VestingSchedule { - offset: 256 * 10, - per_block: 256, + locked: 256 * 5, + per_block: 128, // Vesting over 10 blocks + starting_block: 0, }; let user2_vesting_schedule = VestingSchedule { - offset: 256 * 30, - per_block: 256, + locked: 256 * 20, + per_block: 256, // Vesting over 20 blocks + starting_block: 10, + }; + let user12_vesting_schedule = VestingSchedule { + locked: 256 * 5, + per_block: 64, // Vesting over 20 blocks + starting_block: 10, }; assert_eq!(Balances::vesting(&1), Some(user1_vesting_schedule)); // Account 1 has a vesting schedule assert_eq!(Balances::vesting(&2), Some(user2_vesting_schedule)); // Account 2 has a vesting schedule + assert_eq!(Balances::vesting(&12), Some(user12_vesting_schedule)); // Account 12 has a vesting schedule - assert_eq!(Balances::vesting_balance(&1), user1_free_balance - 256); // Account 1 has only 256 units vested at block 1 + // Account 1 has only 128 units vested from their illiquid 256 * 5 units at block 1 + assert_eq!(Balances::vesting_balance(&1), 128 * 9); + // Account 2 has their full balance locked + assert_eq!(Balances::vesting_balance(&2), user2_free_balance); + // Account 12 has only their illiquid funds locked + assert_eq!(Balances::vesting_balance(&12), user12_free_balance - 256 * 5); System::set_block_number(10); assert_eq!(System::block_number(), 10); - assert_eq!(Balances::vesting_balance(&1), 0); // Account 1 has fully vested by block 10 - assert_eq!(Balances::vesting_balance(&2), user2_free_balance); // Account 2 has started vesting by block 10 + // Account 1 has fully vested by block 10 + assert_eq!(Balances::vesting_balance(&1), 0); + // Account 2 has started vesting by block 10 + assert_eq!(Balances::vesting_balance(&2), user2_free_balance); + // Account 12 has started vesting by block 10 + assert_eq!(Balances::vesting_balance(&12), user12_free_balance - 256 * 5); System::set_block_number(30); assert_eq!(System::block_number(), 30); assert_eq!(Balances::vesting_balance(&1), 0); // Account 1 is still fully vested, and not negative assert_eq!(Balances::vesting_balance(&2), 0); // Account 2 has fully vested by block 30 + assert_eq!(Balances::vesting_balance(&12), 0); // Account 2 has fully vested by block 30 } ); @@ -614,9 +658,10 @@ fn unvested_balance_should_not_transfer() { 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 - assert_eq!(Balances::vesting_balance(&1), 90); // Account 1 has only 10 units vested at block 1 + // Account 1 has only 5 units vested at block 1 (plus 50 unvested) + assert_eq!(Balances::vesting_balance(&1), 45); assert_noop!( - Balances::transfer(Some(1).into(), 2, 11), + Balances::transfer(Some(1).into(), 2, 56), "vesting balance too high to send value" ); // Account 1 cannot send more than vested amount } @@ -635,8 +680,9 @@ fn vested_balance_should_transfer() { 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 - assert_eq!(Balances::vesting_balance(&1), 90); // Account 1 has only 10 units vested at block 1 - assert_ok!(Balances::transfer(Some(1).into(), 2, 10)); + // 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)); } ); } @@ -652,11 +698,69 @@ fn extra_balance_should_transfer() { || { assert_eq!(System::block_number(), 1); assert_ok!(Balances::transfer(Some(3).into(), 1, 100)); + assert_ok!(Balances::transfer(Some(3).into(), 2, 100)); + let user1_free_balance = Balances::free_balance(&1); assert_eq!(user1_free_balance, 200); // Account 1 has 100 more free balance than normal - assert_eq!(Balances::vesting_balance(&1), 90); // Account 1 has 90 units vested at block 1 - assert_ok!(Balances::transfer(Some(1).into(), 2, 105)); // Account 1 can send extra units gained + let user2_free_balance = Balances::free_balance(&2); + assert_eq!(user2_free_balance, 300); // Account 2 has 100 more free balance than normal + + // Account 1 has only 5 units vested at block 1 (plus 150 unvested) + assert_eq!(Balances::vesting_balance(&1), 45); + assert_ok!(Balances::transfer(Some(1).into(), 3, 155)); // Account 1 can send extra units gained + + // Account 2 has no units vested at block 1, but gained 100 + assert_eq!(Balances::vesting_balance(&2), 200); + assert_ok!(Balances::transfer(Some(2).into(), 3, 100)); // Account 2 can send extra units gained + } + ); +} + +#[test] +fn liquid_funds_should_transfer_with_delayed_vesting() { + with_externalities( + &mut ExtBuilder::default() + .existential_deposit(256) + .monied(true) + .vesting(true) + .build(), + || { + assert_eq!(System::block_number(), 1); + let user12_free_balance = Balances::free_balance(&12); + + assert_eq!(user12_free_balance, 2560); // Account 12 has free balance + // Account 12 has liquid funds + assert_eq!(Balances::vesting_balance(&12), user12_free_balance - 256 * 5); + + // Account 12 has delayed vesting + let user12_vesting_schedule = VestingSchedule { + locked: 256 * 5, + per_block: 64, // Vesting over 20 blocks + starting_block: 10, + }; + assert_eq!(Balances::vesting(&12), Some(user12_vesting_schedule)); + + // Account 12 can still send liquid funds + assert_ok!(Balances::transfer(Some(12).into(), 3, 256 * 5)); + } + ); +} + +#[test] +fn signed_extension_take_fees_work() { + with_externalities( + &mut ExtBuilder::default() + .existential_deposit(10) + .transaction_fees(10, 1) + .monied(true) + .build(), + || { + let len = 10; + assert!(TakeFees::::from(0).pre_dispatch(&1, info_from_weight(0), len).is_ok()); + assert_eq!(Balances::free_balance(&1), 100 - 20); + assert!(TakeFees::::from(5 /* tipped */).pre_dispatch(&1, info_from_weight(0), len).is_ok()); + assert_eq!(Balances::free_balance(&1), 100 - 20 - 25); } ); } diff --git a/srml/council/Cargo.toml b/srml/collective/Cargo.toml similarity index 82% rename from srml/council/Cargo.toml rename to srml/collective/Cargo.toml index c180846b8e969dfde3e126ae5840ef12b7c59db2..cd0bb17871a4add8b4c88f38dd98495de4f87e01 100644 --- a/srml/council/Cargo.toml +++ b/srml/collective/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "srml-council" +name = "srml-collective" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" @@ -7,13 +7,12 @@ edition = "2018" [dependencies] serde = { version = "1.0", optional = true } safe-mix = { version = "1.0", default-features = false} -parity-codec = { version = "3.3", default-features = false, features = ["derive"] } +parity-codec = { version = "4.1.1", default-features = false, features = ["derive"] } substrate-primitives = { path = "../../core/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 } primitives = { package = "sr-primitives", path = "../../core/sr-primitives", default-features = false } srml-support = { path = "../support", default-features = false } -democracy = { package = "srml-democracy", path = "../democracy", default-features = false } system = { package = "srml-system", path = "../system", default-features = false } [dev-dependencies] @@ -31,6 +30,5 @@ std = [ "runtime_io/std", "srml-support/std", "primitives/std", - "democracy/std", "system/std", ] diff --git a/srml/collective/src/lib.rs b/srml/collective/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..d729b8a300f7a5fd8c7e79bc798812381c26337c --- /dev/null +++ b/srml/collective/src/lib.rs @@ -0,0 +1,737 @@ +// 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 . + +//! Collective system: Members of a set of account IDs can make their collective feelings known +//! through dispatched calls from one of two specialised origins. + +#![cfg_attr(not(feature = "std"), no_std)] +#![recursion_limit="128"] + +use rstd::{prelude::*, result}; +use substrate_primitives::u32_trait::Value as U32; +use primitives::traits::{Hash, EnsureOrigin}; +use srml_support::{ + dispatch::{Dispatchable, Parameter}, codec::{Encode, Decode}, traits::ChangeMembers, + StorageValue, StorageMap, decl_module, decl_event, decl_storage, ensure +}; +use system::{self, ensure_signed, ensure_root}; + +/// Simple index type for proposal counting. +pub type ProposalIndex = u32; + +/// A number of members. +/// +/// This also serves as a number of voting members, and since for motions, each member may +/// vote exactly once, therefore also the number of votes for any given motion. +pub type MemberCount = u32; + +pub trait Trait: system::Trait { + /// The outer origin type. + type Origin: From>; + + /// The outer call dispatch type. + type Proposal: Parameter + Dispatchable>::Origin>; + + /// The outer event type. + type Event: From> + Into<::Event>; +} + +/// Origin for the collective module. +#[derive(PartialEq, Eq, Clone)] +#[cfg_attr(feature = "std", derive(Debug))] +pub enum RawOrigin { + /// It has been condoned by a given number of members of the collective from a given total. + Members(MemberCount, MemberCount), + /// It has been condoned by a single member of the collective. + Member(AccountId), + /// Dummy to manage the fact we have instancing. + _Phantom(rstd::marker::PhantomData), +} + +/// Origin for the collective module. +pub type Origin = RawOrigin<::AccountId, I>; + +#[derive(PartialEq, Eq, Clone, Encode, Decode)] +#[cfg_attr(feature = "std", derive(Debug))] +/// Info for keeping track of a motion being voted on. +pub struct Votes { + /// The proposal's unique index. + index: ProposalIndex, + /// The number of approval votes that are needed to pass the motion. + threshold: MemberCount, + /// The current set of voters that approved it. + ayes: Vec, + /// The current set of voters that rejected it. + nays: Vec, +} + +decl_storage! { + trait Store for Module, I: Instance=DefaultInstance> as Collective { + /// The hashes of the active proposals. + pub Proposals get(proposals): Vec; + /// Actual proposal for a given hash, if it's current. + pub ProposalOf get(proposal_of): map T::Hash => Option<>::Proposal>; + /// Votes on a given proposal, if it is ongoing. + pub Voting get(voting): map T::Hash => Option>; + /// Proposals so far. + pub ProposalCount get(proposal_count): u32; + /// The current members of the collective. This is stored sorted (just by value). + pub Members get(members) config(): Vec; + } + add_extra_genesis { + config(phantom): rstd::marker::PhantomData; + } +} + +decl_event!( + pub enum Event where + ::Hash, + ::AccountId, + { + /// A motion (given hash) has been proposed (by given account) with a threshold (given + /// `MemberCount`). + Proposed(AccountId, ProposalIndex, Hash, MemberCount), + /// A motion (given hash) has been voted on by given account, leaving + /// a tally (yes votes and no votes given respectively as `MemberCount`). + Voted(AccountId, Hash, bool, MemberCount, MemberCount), + /// A motion was approved by the required threshold. + Approved(Hash), + /// A motion was not approved by the required threshold. + Disapproved(Hash), + /// A motion was executed; `bool` is true if returned without error. + Executed(Hash, bool), + /// A single member did some action; `bool` is true if returned without error. + MemberExecuted(Hash, bool), + } +); + +decl_module! { + pub struct Module, I: Instance=DefaultInstance> for enum Call where origin: ::Origin { + fn deposit_event() = default; + + /// Set the collective's membership manually to `new_members`. Be nice to the chain and + /// provide it pre-sorted. + /// + /// Requires root origin. + fn set_members(origin, new_members: Vec) { + ensure_root(origin)?; + + // stable sorting since they will generally be provided sorted. + let mut old_members = >::get(); + old_members.sort(); + let mut new_members = new_members; + new_members.sort(); + let mut old_iter = old_members.iter(); + let mut new_iter = new_members.iter(); + let mut incoming = vec![]; + let mut outgoing = vec![]; + let mut old_i = old_iter.next(); + let mut new_i = new_iter.next(); + loop { + match (old_i, new_i) { + (None, None) => break, + (Some(old), Some(new)) if old == new => { + old_i = old_iter.next(); + new_i = new_iter.next(); + } + (Some(old), Some(new)) if old < new => { + outgoing.push(old.clone()); + old_i = old_iter.next(); + } + (Some(old), None) => { + outgoing.push(old.clone()); + old_i = old_iter.next(); + } + (_, Some(new)) => { + incoming.push(new.clone()); + new_i = new_iter.next(); + } + } + } + + Self::change_members(&incoming, &outgoing, &new_members); + } + + /// Dispatch a proposal from a member using the `Member` origin. + /// + /// Origin must be a member of the collective. + fn execute(origin, proposal: Box<>::Proposal>) { + let who = ensure_signed(origin)?; + ensure!(Self::is_member(&who), "proposer not a member"); + + let proposal_hash = T::Hashing::hash_of(&proposal); + let ok = proposal.dispatch(RawOrigin::Member(who).into()).is_ok(); + Self::deposit_event(RawEvent::MemberExecuted(proposal_hash, ok)); + } + + /// # + /// - Bounded storage reads and writes. + /// - Argument `threshold` has bearing on weight. + /// # + fn propose(origin, #[compact] threshold: MemberCount, proposal: Box<>::Proposal>) { + let who = ensure_signed(origin)?; + + ensure!(Self::is_member(&who), "proposer not a member"); + + let proposal_hash = T::Hashing::hash_of(&proposal); + + ensure!(!>::exists(proposal_hash), "duplicate proposals not allowed"); + + if threshold < 2 { + let seats = Self::members().len() as MemberCount; + let ok = proposal.dispatch(RawOrigin::Members(1, seats).into()).is_ok(); + Self::deposit_event(RawEvent::Executed(proposal_hash, ok)); + } else { + let index = Self::proposal_count(); + >::mutate(|i| *i += 1); + >::mutate(|proposals| proposals.push(proposal_hash)); + >::insert(proposal_hash, *proposal); + let votes = Votes { index, threshold, ayes: vec![who.clone()], nays: vec![] }; + >::insert(proposal_hash, votes); + + Self::deposit_event(RawEvent::Proposed(who, index, proposal_hash, threshold)); + } + } + + /// # + /// - Bounded storage read and writes. + /// - Will be slightly heavier if the proposal is approved / disapproved after the vote. + /// # + fn vote(origin, proposal: T::Hash, #[compact] index: ProposalIndex, approve: bool) { + let who = ensure_signed(origin)?; + + ensure!(Self::is_member(&who), "voter not a member"); + + let mut voting = Self::voting(&proposal).ok_or("proposal must exist")?; + ensure!(voting.index == index, "mismatched index"); + + let position_yes = voting.ayes.iter().position(|a| a == &who); + let position_no = voting.nays.iter().position(|a| a == &who); + + if approve { + if position_yes.is_none() { + voting.ayes.push(who.clone()); + } else { + return Err("duplicate vote ignored") + } + if let Some(pos) = position_no { + voting.nays.swap_remove(pos); + } + } else { + if position_no.is_none() { + voting.nays.push(who.clone()); + } else { + return Err("duplicate vote ignored") + } + if let Some(pos) = position_yes { + voting.ayes.swap_remove(pos); + } + } + + let yes_votes = voting.ayes.len() as MemberCount; + let no_votes = voting.nays.len() as MemberCount; + Self::deposit_event(RawEvent::Voted(who, proposal, approve, yes_votes, no_votes)); + + let seats = Self::members().len() as MemberCount; + let approved = yes_votes >= voting.threshold; + let disapproved = seats.saturating_sub(no_votes) < voting.threshold; + if approved || disapproved { + if approved { + Self::deposit_event(RawEvent::Approved(proposal)); + + // execute motion, assuming it exists. + if let Some(p) = >::take(&proposal) { + let origin = RawOrigin::Members(voting.threshold, seats).into(); + let ok = p.dispatch(origin).is_ok(); + Self::deposit_event(RawEvent::Executed(proposal, ok)); + } + } else { + // disapproved + Self::deposit_event(RawEvent::Disapproved(proposal)); + } + + // remove vote + >::remove(&proposal); + >::mutate(|proposals| proposals.retain(|h| h != &proposal)); + } else { + // update voting + >::insert(&proposal, voting); + } + } + } +} + +impl, I: Instance> Module { + pub fn is_member(who: &T::AccountId) -> bool { + Self::members().contains(who) + } +} + +impl, I: Instance> ChangeMembers for Module { + fn change_members(_incoming: &[T::AccountId], outgoing: &[T::AccountId], new: &[T::AccountId]) { + // remove accounts from all current voting in motions. + let mut old = outgoing.to_vec(); + old.sort_unstable(); + for h in Self::proposals().into_iter() { + >::mutate(h, |v| + if let Some(mut votes) = v.take() { + votes.ayes = votes.ayes.into_iter() + .filter(|i| old.binary_search(i).is_err()) + .collect(); + votes.nays = votes.nays.into_iter() + .filter(|i| old.binary_search(i).is_err()) + .collect(); + *v = Some(votes); + } + ); + } + >::put_ref(new); + } +} + +/// Ensure that the origin `o` represents at least `n` members. Returns `Ok` or an `Err` +/// otherwise. +pub fn ensure_members(o: OuterOrigin, n: MemberCount) + -> result::Result +where + OuterOrigin: Into, OuterOrigin>> +{ + match o.into() { + Ok(RawOrigin::Members(x, _)) if x >= n => Ok(n), + _ => Err("bad origin: expected to be a threshold number of members"), + } +} + +pub struct EnsureMember(rstd::marker::PhantomData<(AccountId, I)>); +impl< + O: Into, O>> + From>, + AccountId, + I, +> EnsureOrigin for EnsureMember { + type Success = AccountId; + fn try_origin(o: O) -> Result { + o.into().and_then(|o| match o { + RawOrigin::Member(id) => Ok(id), + r => Err(O::from(r)), + }) + } +} + +pub struct EnsureMembers(rstd::marker::PhantomData<(N, AccountId, I)>); +impl< + O: Into, O>> + From>, + N: U32, + AccountId, + I, +> EnsureOrigin for EnsureMembers { + type Success = (MemberCount, MemberCount); + fn try_origin(o: O) -> Result { + o.into().and_then(|o| match o { + RawOrigin::Members(n, m) if n >= N::VALUE => Ok((n, m)), + r => Err(O::from(r)), + }) + } +} + +pub struct EnsureProportionMoreThan( + rstd::marker::PhantomData<(N, D, AccountId, I)> +); +impl< + O: Into, O>> + From>, + N: U32, + D: U32, + AccountId, + I, +> EnsureOrigin for EnsureProportionMoreThan { + type Success = (); + fn try_origin(o: O) -> Result { + o.into().and_then(|o| match o { + RawOrigin::Members(n, m) if n * D::VALUE > N::VALUE * m => Ok(()), + r => Err(O::from(r)), + }) + } +} + +pub struct EnsureProportionAtLeast( + rstd::marker::PhantomData<(N, D, AccountId, I)> +); +impl< + O: Into, O>> + From>, + N: U32, + D: U32, + AccountId, + I, +> EnsureOrigin for EnsureProportionAtLeast { + type Success = (); + fn try_origin(o: O) -> Result { + o.into().and_then(|o| match o { + RawOrigin::Members(n, m) if n * D::VALUE >= N::VALUE * m => Ok(()), + r => Err(O::from(r)), + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use srml_support::{Hashable, assert_ok, assert_noop, parameter_types}; + use system::{EventRecord, Phase}; + use hex_literal::hex; + use runtime_io::with_externalities; + use substrate_primitives::{H256, Blake2Hasher}; + use primitives::{ + traits::{BlakeTwo256, IdentityLookup, Block as BlockT}, testing::Header, BuildStorage + }; + use crate as collective; + + parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + } + impl system::Trait for Test { + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type Event = Event; + type WeightMultiplierUpdate = (); + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; + } + impl Trait for Test { + type Origin = Origin; + type Proposal = Call; + type Event = Event; + } + impl Trait for Test { + type Origin = Origin; + type Proposal = Call; + type Event = Event; + } + + pub type Block = primitives::generic::Block; + pub type UncheckedExtrinsic = primitives::generic::UncheckedExtrinsic; + + srml_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + System: system::{Module, Call, Event}, + Collective: collective::::{Module, Call, Event, Origin, Config}, + DefaultCollective: collective::{Module, Call, Event, Origin, Config}, + } + ); + + fn make_ext() -> runtime_io::TestExternalities { + GenesisConfig { + collective_Instance1: Some(collective::GenesisConfig { + members: vec![1, 2, 3], + phantom: Default::default(), + }), + collective: None, + }.build_storage().unwrap().0.into() + } + + #[test] + fn motions_basic_environment_works() { + with_externalities(&mut make_ext(), || { + System::set_block_number(1); + assert_eq!(Collective::members(), vec![1, 2, 3]); + assert_eq!(Collective::proposals(), Vec::::new()); + }); + } + + fn make_proposal(value: u64) -> Call { + Call::System(system::Call::remark(value.encode())) + } + + #[test] + fn removal_of_old_voters_votes_works() { + with_externalities(&mut make_ext(), || { + System::set_block_number(1); + let proposal = make_proposal(42); + let hash = BlakeTwo256::hash_of(&proposal); + assert_ok!(Collective::propose(Origin::signed(1), 3, Box::new(proposal.clone()))); + assert_ok!(Collective::vote(Origin::signed(2), hash.clone(), 0, true)); + assert_eq!( + Collective::voting(&hash), + Some(Votes { index: 0, threshold: 3, ayes: vec![1, 2], nays: vec![] }) + ); + Collective::change_members(&[4], &[1], &[2, 3, 4]); + assert_eq!( + Collective::voting(&hash), + Some(Votes { index: 0, threshold: 3, ayes: vec![2], nays: vec![] }) + ); + + let proposal = make_proposal(69); + let hash = BlakeTwo256::hash_of(&proposal); + assert_ok!(Collective::propose(Origin::signed(2), 2, Box::new(proposal.clone()))); + assert_ok!(Collective::vote(Origin::signed(3), hash.clone(), 1, false)); + assert_eq!( + Collective::voting(&hash), + Some(Votes { index: 1, threshold: 2, ayes: vec![2], nays: vec![3] }) + ); + Collective::change_members(&[], &[3], &[2, 4]); + assert_eq!( + Collective::voting(&hash), + Some(Votes { index: 1, threshold: 2, ayes: vec![2], nays: vec![] }) + ); + }); + } + + #[test] + fn removal_of_old_voters_votes_works_with_set_members() { + with_externalities(&mut make_ext(), || { + System::set_block_number(1); + let proposal = make_proposal(42); + let hash = BlakeTwo256::hash_of(&proposal); + assert_ok!(Collective::propose(Origin::signed(1), 3, Box::new(proposal.clone()))); + assert_ok!(Collective::vote(Origin::signed(2), hash.clone(), 0, true)); + assert_eq!( + Collective::voting(&hash), + Some(Votes { index: 0, threshold: 3, ayes: vec![1, 2], nays: vec![] }) + ); + assert_ok!(Collective::set_members(Origin::ROOT, vec![2, 3, 4])); + assert_eq!( + Collective::voting(&hash), + Some(Votes { index: 0, threshold: 3, ayes: vec![2], nays: vec![] }) + ); + + let proposal = make_proposal(69); + let hash = BlakeTwo256::hash_of(&proposal); + assert_ok!(Collective::propose(Origin::signed(2), 2, Box::new(proposal.clone()))); + assert_ok!(Collective::vote(Origin::signed(3), hash.clone(), 1, false)); + assert_eq!( + Collective::voting(&hash), + Some(Votes { index: 1, threshold: 2, ayes: vec![2], nays: vec![3] }) + ); + assert_ok!(Collective::set_members(Origin::ROOT, vec![2, 4])); + assert_eq!( + Collective::voting(&hash), + Some(Votes { index: 1, threshold: 2, ayes: vec![2], nays: vec![] }) + ); + }); + } + + #[test] + fn propose_works() { + with_externalities(&mut make_ext(), || { + System::set_block_number(1); + let proposal = make_proposal(42); + let hash = proposal.blake2_256().into(); + assert_ok!(Collective::propose(Origin::signed(1), 3, Box::new(proposal.clone()))); + assert_eq!(Collective::proposals(), vec![hash]); + assert_eq!(Collective::proposal_of(&hash), Some(proposal)); + assert_eq!( + Collective::voting(&hash), + Some(Votes { index: 0, threshold: 3, ayes: vec![1], nays: vec![] }) + ); + + assert_eq!(System::events(), vec![ + EventRecord { + phase: Phase::Finalization, + event: Event::collective_Instance1(RawEvent::Proposed( + 1, + 0, + hex!["10b209e55d0f37cd45574674bba42519a29bf0ccf3c85c3c773fcbacab820bb4"].into(), + 3, + )), + topics: vec![], + } + ]); + }); + } + + #[test] + fn motions_ignoring_non_collective_proposals_works() { + with_externalities(&mut make_ext(), || { + System::set_block_number(1); + let proposal = make_proposal(42); + assert_noop!( + Collective::propose(Origin::signed(42), 3, Box::new(proposal.clone())), + "proposer not a member" + ); + }); + } + + #[test] + fn motions_ignoring_non_collective_votes_works() { + with_externalities(&mut make_ext(), || { + 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"); + }); + } + + #[test] + fn motions_ignoring_bad_index_collective_vote_works() { + with_externalities(&mut make_ext(), || { + 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"); + }); + } + + #[test] + fn motions_revoting_works() { + with_externalities(&mut make_ext(), || { + System::set_block_number(1); + let proposal = make_proposal(42); + let hash: H256 = proposal.blake2_256().into(); + assert_ok!(Collective::propose(Origin::signed(1), 2, Box::new(proposal.clone()))); + assert_eq!( + 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_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_eq!(System::events(), vec![ + EventRecord { + phase: Phase::Finalization, + event: Event::collective_Instance1(RawEvent::Proposed( + 1, + 0, + hex!["10b209e55d0f37cd45574674bba42519a29bf0ccf3c85c3c773fcbacab820bb4"].into(), + 2, + )), + topics: vec![], + }, + EventRecord { + phase: Phase::Finalization, + event: Event::collective_Instance1(RawEvent::Voted( + 1, + hex!["10b209e55d0f37cd45574674bba42519a29bf0ccf3c85c3c773fcbacab820bb4"].into(), + false, + 0, + 1, + )), + topics: vec![], + } + ]); + }); + } + + #[test] + fn motions_disapproval_works() { + with_externalities(&mut make_ext(), || { + 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_ok!(Collective::vote(Origin::signed(2), hash.clone(), 0, false)); + + assert_eq!(System::events(), vec![ + EventRecord { + phase: Phase::Finalization, + event: Event::collective_Instance1( + RawEvent::Proposed( + 1, + 0, + hex!["10b209e55d0f37cd45574674bba42519a29bf0ccf3c85c3c773fcbacab820bb4"].into(), + 3, + )), + topics: vec![], + }, + EventRecord { + phase: Phase::Finalization, + event: Event::collective_Instance1(RawEvent::Voted( + 2, + hex!["10b209e55d0f37cd45574674bba42519a29bf0ccf3c85c3c773fcbacab820bb4"].into(), + false, + 1, + 1, + )), + topics: vec![], + }, + EventRecord { + phase: Phase::Finalization, + event: Event::collective_Instance1(RawEvent::Disapproved( + hex!["10b209e55d0f37cd45574674bba42519a29bf0ccf3c85c3c773fcbacab820bb4"].into(), + )), + topics: vec![], + } + ]); + }); + } + + #[test] + fn motions_approval_works() { + with_externalities(&mut make_ext(), || { + System::set_block_number(1); + let proposal = make_proposal(42); + let hash: H256 = proposal.blake2_256().into(); + assert_ok!(Collective::propose(Origin::signed(1), 2, Box::new(proposal.clone()))); + assert_ok!(Collective::vote(Origin::signed(2), hash.clone(), 0, true)); + + assert_eq!(System::events(), vec![ + EventRecord { + phase: Phase::Finalization, + event: Event::collective_Instance1(RawEvent::Proposed( + 1, + 0, + hex!["10b209e55d0f37cd45574674bba42519a29bf0ccf3c85c3c773fcbacab820bb4"].into(), + 2, + )), + topics: vec![], + }, + EventRecord { + phase: Phase::Finalization, + event: Event::collective_Instance1(RawEvent::Voted( + 2, + hex!["10b209e55d0f37cd45574674bba42519a29bf0ccf3c85c3c773fcbacab820bb4"].into(), + true, + 2, + 0, + )), + topics: vec![], + }, + EventRecord { + phase: Phase::Finalization, + event: Event::collective_Instance1(RawEvent::Approved( + hex!["10b209e55d0f37cd45574674bba42519a29bf0ccf3c85c3c773fcbacab820bb4"].into(), + )), + topics: vec![], + }, + EventRecord { + phase: Phase::Finalization, + event: Event::collective_Instance1(RawEvent::Executed( + hex!["10b209e55d0f37cd45574674bba42519a29bf0ccf3c85c3c773fcbacab820bb4"].into(), + false, + )), + topics: vec![], + } + ]); + }); + } +} diff --git a/srml/contracts/COMPLEXITY.md b/srml/contracts/COMPLEXITY.md index 319707021dba3ce4ded57e53717aba0e1d4b4b59..6ae7b8fb73c284a4d9a91e9c3106f079939a2dbd 100644 --- a/srml/contracts/COMPLEXITY.md +++ b/srml/contracts/COMPLEXITY.md @@ -172,15 +172,31 @@ Assuming marshaled size of a balance value is of the constant size we can neglec **complexity**: up to 2 DB reads and up to 2 DB writes (if flushed to the storage) in the standard case. If removal of the source account takes place then it will additionally perform a DB write per one storage entry that the account has. For the current `AccountDb` implementation computing complexity also depends on the depth of the `AccountDb` cascade. Memorywise it can be assumed to be constant. +## Initialization + +Before a call or create can be performed the execution context must be initialized. + +For the first call or instantiation in the handling of an extrinsic, this involves two calls: + +1. `>::now()` +2. `>::block_number()` + +The complexity of initialization depends on the complexity of these functions. In the current +implementation they just involve a DB read. + +For subsequent calls and instantiations during contract execution, the initialization requires no +expensive operations. + ## Call This function receives input data for the contract execution. The execution consists of the following steps: -1. Checking rent payment. -2. Loading code from the DB. -3. `transfer`-ing funds between the caller and the destination account. -4. Executing the code of the destination account. -5. Committing overlayed changed to the underlying `AccountDb`. +1. Initialization of the execution context. +2. Checking rent payment. +3. Loading code from the DB. +4. `transfer`-ing funds between the caller and the destination account. +5. Executing the code of the destination account. +6. Committing overlayed changed to the underlying `AccountDb`. **Note** that the complexity of executing the contract code should be considered separately. @@ -211,11 +227,12 @@ Finally, all changes are `commit`-ted into the underlying overlay. The complexit This function takes the code of the constructor and input data. Creation of a contract consists of the following steps: -1. Calling `DetermineContractAddress` hook to determine an address for the contract, -2. `transfer`-ing funds between self and the newly created contract. -3. Executing the constructor code. This will yield the final code of the code. -4. Storing the code for the newly created contract in the overlay. -5. Committing overlayed changed to the underlying `AccountDb`. +1. Initialization of the execution context. +2. Calling `DetermineContractAddress` hook to determine an address for the contract, +3. `transfer`-ing funds between self and the newly created contract. +4. Executing the constructor code. This will yield the final code of the code. +5. Storing the code for the newly created contract in the overlay. +6. Committing overlayed changed to the underlying `AccountDb`. **Note** that the complexity of executing the constructor code should be considered separately. @@ -384,3 +401,9 @@ It consists of the following steps: **complexity**: Assuming that the rent allowance is of constant size, this function has constant complexity. This function performs a DB read. + +## ext_block_number + +This function serializes the current block's number into the scratch buffer. + +**complexity**: Assuming that the block number is of constant size, this function has constant complexity. diff --git a/srml/contracts/Cargo.toml b/srml/contracts/Cargo.toml index 08c44bf63af1e664398d90bfa1387a71f612a667..19afc06406110c6ce9e3b9f127eb50f1089afef8 100644 --- a/srml/contracts/Cargo.toml +++ b/srml/contracts/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" [dependencies] serde = { version = "1.0", optional = true, features = ["derive"] } pwasm-utils = { version = "0.6.1", default-features = false } -parity-codec = { version = "3.3", default-features = false, features = ["derive"] } +parity-codec = { version = "4.1.1", default-features = false, features = ["derive"] } parity-wasm = { version = "0.31", default-features = false } wasmi-validation = { version = "0.1", default-features = false } substrate-primitives = { path = "../../core/primitives", default-features = false } diff --git a/srml/contracts/src/account_db.rs b/srml/contracts/src/account_db.rs index 80d5fbe3bba58d8af2319e54b4e11a3d333abcd0..f91226641575cf1563981293b41bdb6ddf2d85d6 100644 --- a/srml/contracts/src/account_db.rs +++ b/srml/contracts/src/account_db.rs @@ -14,10 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -//! Auxilliaries to help with managing partial changes to accounts state. +//! Auxiliaries to help with managing partial changes to accounts state. use super::{ - AliveContractInfo, BalanceOf, CodeHash, ContractInfo, ContractInfoOf, Module, Trait, TrieId, + AliveContractInfo, BalanceOf, CodeHash, ContractInfo, ContractInfoOf, Trait, TrieId, TrieIdGenerator, }; use crate::exec::StorageKey; @@ -26,7 +26,7 @@ use rstd::collections::btree_map::{BTreeMap, Entry}; use rstd::prelude::*; use runtime_io::blake2_256; use runtime_primitives::traits::{Bounded, Zero}; -use srml_support::traits::{Currency, Imbalance, SignedImbalance, UpdateBalanceOutcome}; +use srml_support::traits::{Currency, Get, Imbalance, SignedImbalance, UpdateBalanceOutcome}; use srml_support::{storage::child, StorageMap}; use system; @@ -125,7 +125,7 @@ impl AccountDb for DirectAccountDb { } else if let Some(code_hash) = changed.code_hash { AliveContractInfo:: { code_hash, - storage_size: >::storage_size_offset(), + storage_size: T::StorageSizeOffset::get(), trie_id: ::TrieIdGenerator::trie_id(&address), deduct_block: >::block_number(), rent_allowance: >::max_value(), diff --git a/srml/contracts/src/exec.rs b/srml/contracts/src/exec.rs index 7a16c9d60dc7c0ca0ffbf0033224eae1b4adc808..4a83e606ac86b6862e82d127381558495165bea5 100644 --- a/srml/contracts/src/exec.rs +++ b/srml/contracts/src/exec.rs @@ -15,19 +15,21 @@ // along with Substrate. If not, see . use super::{CodeHash, Config, ContractAddressFor, Event, RawEvent, Trait, - TrieId, BalanceOf, ContractInfoOf}; + TrieId, BalanceOf, ContractInfo}; use crate::account_db::{AccountDb, DirectAccountDb, OverlayAccountDb}; -use crate::gas::{GasMeter, Token, approx_gas_for_balance}; +use crate::gas::{Gas, GasMeter, Token, approx_gas_for_balance}; +use crate::rent; use rstd::prelude::*; use runtime_primitives::traits::{Bounded, CheckedAdd, CheckedSub, Zero}; -use srml_support::{StorageMap, traits::{WithdrawReason, Currency}}; +use srml_support::traits::{WithdrawReason, Currency}; use timestamp; pub type AccountIdOf = ::AccountId; pub type CallOf = ::Call; pub type MomentOf = ::Moment; pub type SeedOf = ::Hash; +pub type BlockNumberOf = ::BlockNumber; /// A type that represents a topic of an event. At the moment a hash is used. pub type TopicOf = ::Hash; @@ -59,15 +61,14 @@ pub trait Ext { /// was deleted. fn get_storage(&self, key: &StorageKey) -> Option>; - /// Sets the storage entry by the given key to the specified value. - /// - /// If `value` is `None` then the storage entry is deleted. - fn set_storage(&mut self, key: StorageKey, value: Option>); + /// Sets the storage entry by the given key to the specified value. If `value` is `None` then + /// the storage entry is deleted. Returns an Err if the value size is too large. + fn set_storage(&mut self, key: StorageKey, value: Option>) -> Result<(), &'static str>; /// Instantiate a contract from the given code. /// /// The newly created account will be associated with `code`. `value` specifies the amount of value - /// transfered from this to the newly created account (also known as endowment). + /// transferred from this to the newly created account (also known as endowment). fn instantiate( &mut self, code: &CodeHash, @@ -76,7 +77,7 @@ pub trait Ext { input_data: &[u8], ) -> Result>, &'static str>; - /// Call (possibly transfering some amount of funds) into the specified account. + /// Call (possibly transferring some amount of funds) into the specified account. fn call( &mut self, to: &AccountIdOf, @@ -100,7 +101,7 @@ pub trait Ext { /// The `value_transferred` is already added. fn balance(&self) -> BalanceOf; - /// Returns the value transfered along with this call or as endowment. + /// Returns the value transferred along with this call or as endowment. fn value_transferred(&self) -> BalanceOf; /// Returns a reference to the timestamp of the current block @@ -119,6 +120,12 @@ pub trait Ext { /// Rent allowance of the contract fn rent_allowance(&self) -> BalanceOf; + + /// Returns the current block number. + fn block_number(&self) -> BlockNumberOf; + + /// Returns the maximum allowed size of a storage item. + fn max_value_size(&self) -> u32; } /// Loader is a companion of the `Vm` trait. It loads an appropriate abstract @@ -239,10 +246,10 @@ pub enum ExecFeeToken { impl Token for ExecFeeToken { type Metadata = Config; #[inline] - fn calculate_amount(&self, metadata: &Config) -> T::Gas { + fn calculate_amount(&self, metadata: &Config) -> Gas { match *self { - ExecFeeToken::Call => metadata.call_base_fee, - ExecFeeToken::Instantiate => metadata.instantiate_base_fee, + ExecFeeToken::Call => metadata.schedule.call_base_cost, + ExecFeeToken::Instantiate => metadata.schedule.instantiate_base_cost, } } } @@ -257,6 +264,8 @@ pub struct ExecutionContext<'a, T: Trait + 'a, V, L> { pub config: &'a Config, pub vm: &'a V, pub loader: &'a L, + pub timestamp: T::Moment, + pub block_number: T::BlockNumber, } impl<'a, T, E, V, L> ExecutionContext<'a, T, V, L> @@ -267,11 +276,11 @@ where { /// Create the top level execution context. /// - /// The specified `origin` address will be used as `sender` for + /// The specified `origin` address will be used as `sender` for. The `origin` must be a regular + /// account (not a contract). pub fn top_level(origin: T::AccountId, cfg: &'a Config, vm: &'a V, loader: &'a L) -> Self { ExecutionContext { - self_trie_id: >::get(&origin) - .and_then(|i| i.as_alive().map(|i| i.trie_id.clone())), + self_trie_id: None, self_account: origin, overlay: OverlayAccountDb::::new(&DirectAccountDb), depth: 0, @@ -280,13 +289,16 @@ where config: &cfg, vm: &vm, loader: &loader, + timestamp: >::now(), + block_number: >::block_number(), } } - fn nested(&self, overlay: OverlayAccountDb<'a, T>, dest: T::AccountId) -> Self { + fn nested(&self, overlay: OverlayAccountDb<'a, T>, dest: T::AccountId, trie_id: Option) + -> Self + { ExecutionContext { - self_trie_id: >::get(&dest) - .and_then(|i| i.as_alive().map(|i| i.trie_id.clone())), + self_trie_id: trie_id, self_account: dest, overlay, depth: self.depth + 1, @@ -295,10 +307,12 @@ where config: self.config, vm: self.vm, loader: self.loader, + timestamp: self.timestamp.clone(), + block_number: self.block_number.clone(), } } - /// Make a call to the specified address, optionally transfering some funds. + /// Make a call to the specified address, optionally transferring some funds. pub fn call( &mut self, dest: T::AccountId, @@ -321,14 +335,20 @@ where // Assumption: pay_rent doesn't collide with overlay because // pay_rent will be done on first call and dest contract and balance // cannot be changed before the first call - crate::rent::pay_rent::(&dest); + let contract_info = rent::pay_rent::(&dest); + + // Calls to dead contracts always fail. + if let Some(ContractInfo::Tombstone(_)) = contract_info { + return Err("contract has been evicted"); + }; let mut output_data = Vec::new(); let (change_set, events, calls) = { let mut nested = self.nested( OverlayAccountDb::new(&self.overlay), - dest.clone() + dest.clone(), + contract_info.and_then(|i| i.as_alive().map(|i| i.trie_id.clone())) ); if value > BalanceOf::::zero() { @@ -342,6 +362,8 @@ where )?; } + // If code_hash is not none, then the destination account is a live contract, otherwise + // it is a regular account since tombstone accounts have already been rejected. if let Some(dest_code_hash) = self.overlay.get_code_hash(&dest) { let executable = self.loader.load_main(&dest_code_hash)?; output_data = self @@ -352,7 +374,8 @@ where ctx: &mut nested, caller: self.self_account.clone(), value_transferred: value, - timestamp: timestamp::Module::::now(), + timestamp: self.timestamp.clone(), + block_number: self.block_number.clone(), }, input_data, empty_output_buf, @@ -400,7 +423,8 @@ where overlay.create_contract(&dest, code_hash.clone())?; - let mut nested = self.nested(overlay, dest.clone()); + // TrieId has not been generated yet and storage is empty since contract is new. + let mut nested = self.nested(overlay, dest.clone(), None); // Send funds unconditionally here. If the `endowment` is below existential_deposit // then error will be returned here. @@ -421,7 +445,8 @@ where ctx: &mut nested, caller: self.self_account.clone(), value_transferred: endowment, - timestamp: timestamp::Module::::now(), + timestamp: self.timestamp.clone(), + block_number: self.block_number.clone(), }, input_data, EmptyOutputBuf::new(), @@ -465,13 +490,13 @@ impl Token for TransferFeeToken> { type Metadata = Config; #[inline] - fn calculate_amount(&self, metadata: &Config) -> T::Gas { + fn calculate_amount(&self, metadata: &Config) -> Gas { let balance_fee = match self.kind { TransferFeeKind::ContractInstantiate => metadata.contract_account_instantiate_fee, TransferFeeKind::AccountCreate => metadata.account_create_fee, TransferFeeKind::Transfer => metadata.transfer_fee, }; - approx_gas_for_balance::(self.gas_price, balance_fee) + approx_gas_for_balance(self.gas_price, balance_fee) } } @@ -487,11 +512,11 @@ enum TransferCause { /// /// This function also handles charging the fee. The fee depends /// on whether the transfer happening because of contract instantiation -/// (transfering endowment) or because of a transfer via `call`. This +/// (transferring endowment) or because of a transfer via `call`. This /// is specified using the `cause` parameter. /// /// NOTE: that the fee is denominated in `BalanceOf` units, but -/// charged in `T::Gas` from the provided `gas_meter`. This means +/// charged in `Gas` from the provided `gas_meter`. This means /// that the actual amount charged might differ. /// /// NOTE: that we allow for draining all funds of the contract so it @@ -574,6 +599,7 @@ struct CallContext<'a, 'b: 'a, T: Trait + 'b, V: Vm + 'b, L: Loader> { caller: T::AccountId, value_transferred: BalanceOf, timestamp: T::Moment, + block_number: T::BlockNumber, } impl<'a, 'b: 'a, T, E, V, L> Ext for CallContext<'a, 'b, T, V, L> @@ -588,10 +614,17 @@ where self.ctx.overlay.get_storage(&self.ctx.self_account, self.ctx.self_trie_id.as_ref(), key) } - fn set_storage(&mut self, key: StorageKey, value: Option>) { + fn set_storage(&mut self, key: StorageKey, value: Option>) -> Result<(), &'static str> { + if let Some(ref value) = value { + if self.max_value_size() < value.len() as u32 { + return Err("value size exceeds maximum"); + } + } + self.ctx .overlay - .set_storage(&self.ctx.self_account, key, value) + .set_storage(&self.ctx.self_account, key, value); + Ok(()) } fn instantiate( @@ -662,6 +695,12 @@ where self.ctx.overlay.get_rent_allowance(&self.ctx.self_account) .unwrap_or(>::max_value()) // Must never be triggered actually } + + fn block_number(&self) -> T::BlockNumber { self.block_number } + + fn max_value_size(&self) -> u32 { + self.ctx.config.max_value_size + } } /// These tests exercise the executive layer. diff --git a/srml/contracts/src/gas.rs b/srml/contracts/src/gas.rs index 1ea519634463c7ed232e99f1ad9c363247c0ca20..44d5b32fd938b1f1ad5677a5f26a2396a1f27ad6 100644 --- a/srml/contracts/src/gas.rs +++ b/srml/contracts/src/gas.rs @@ -15,13 +15,19 @@ // along with Substrate. If not, see . use crate::{GasSpent, Module, Trait, BalanceOf, NegativeImbalanceOf}; +use rstd::convert::TryFrom; use runtime_primitives::BLOCK_FULL; -use runtime_primitives::traits::{CheckedMul, CheckedSub, Zero, SaturatedConversion}; -use srml_support::{StorageValue, traits::{OnUnbalanced, ExistenceRequirement, WithdrawReason, Currency, Imbalance}}; +use runtime_primitives::traits::{CheckedMul, Zero, SaturatedConversion, SimpleArithmetic, UniqueSaturatedInto}; +use srml_support::StorageValue; +use srml_support::traits::{Currency, ExistenceRequirement, Get, Imbalance, OnUnbalanced, WithdrawReason}; #[cfg(test)] use std::{any::Any, fmt::Debug}; +// Gas units are chosen to be represented by u64 so that gas metering instructions can operate on +// them efficiently. +pub type Gas = u64; + #[must_use] #[derive(Debug, PartialEq, Eq)] pub enum GasMeterResult { @@ -68,7 +74,7 @@ pub trait Token: Copy + Clone + TestAuxiliaries { /// That said, implementors of this function still can run into overflows /// while calculating the amount. In this case it is ok to use saturating operations /// since on overflow they will return `max_value` which should consume all gas. - fn calculate_amount(&self, metadata: &Self::Metadata) -> T::Gas; + fn calculate_amount(&self, metadata: &Self::Metadata) -> Gas; } /// A wrapper around a type-erased trait object of what used to be a `Token`. @@ -79,17 +85,16 @@ pub struct ErasedToken { } pub struct GasMeter { - limit: T::Gas, + limit: Gas, /// Amount of gas left from initial gas limit. Can reach zero. - gas_left: T::Gas, + gas_left: Gas, gas_price: BalanceOf, #[cfg(test)] tokens: Vec, } impl GasMeter { - #[cfg(test)] - pub fn with_limit(gas_limit: T::Gas, gas_price: BalanceOf) -> GasMeter { + pub fn with_limit(gas_limit: Gas, gas_price: BalanceOf) -> GasMeter { GasMeter { limit: gas_limit, gas_left: gas_limit, @@ -125,9 +130,8 @@ impl GasMeter { } let amount = token.calculate_amount(metadata); - let new_value = match self.gas_left.checked_sub(&amount) { + let new_value = match self.gas_left.checked_sub(amount) { None => None, - Some(val) if val.is_zero() => None, Some(val) => Some(val), }; @@ -149,7 +153,7 @@ impl GasMeter { /// All unused gas in the nested gas meter is returned to this gas meter. pub fn with_nested>) -> R>( &mut self, - amount: T::Gas, + amount: Gas, f: F, ) -> R { // NOTE that it is ok to allocate all available gas since it still ensured @@ -158,13 +162,7 @@ impl GasMeter { f(None) } else { self.gas_left = self.gas_left - amount; - let mut nested = GasMeter { - limit: amount, - gas_left: amount, - gas_price: self.gas_price, - #[cfg(test)] - tokens: Vec::new(), - }; + let mut nested = GasMeter::with_limit(amount, self.gas_price); let r = f(Some(&mut nested)); @@ -179,12 +177,12 @@ impl GasMeter { } /// Returns how much gas left from the initial budget. - pub fn gas_left(&self) -> T::Gas { + pub fn gas_left(&self) -> Gas { self.gas_left } /// Returns how much gas was spent. - fn spent(&self) -> T::Gas { + fn spent(&self) -> Gas { self.limit - self.gas_left } @@ -200,11 +198,11 @@ impl GasMeter { /// The funds are deducted from `transactor`. pub fn buy_gas( transactor: &T::AccountId, - gas_limit: T::Gas, + gas_limit: Gas, ) -> Result<(GasMeter, NegativeImbalanceOf), &'static str> { // Check if the specified amount of gas is available in the current block. - // This cannot underflow since `gas_spent` is never greater than `block_gas_limit`. - let gas_available = >::block_gas_limit() - >::gas_spent(); + // This cannot underflow since `gas_spent` is never greater than `T::BlockGasLimit`. + let gas_available = T::BlockGasLimit::get() - >::gas_spent(); if gas_limit > gas_available { // gas limit reached, revert the transaction and retry again in the future return Err(BLOCK_FULL); @@ -212,9 +210,13 @@ pub fn buy_gas( // Buy the specified amount of gas. let gas_price = >::gas_price(); - let cost = gas_limit.clone().into() - .checked_mul(&gas_price) - .ok_or("overflow multiplying gas limit by price")?; + let cost = if gas_price.is_zero() { + >::zero() + } else { + as TryFrom>::try_from(gas_limit).ok() + .and_then(|gas_limit| gas_price.checked_mul(&gas_limit)) + .ok_or("overflow multiplying gas limit by price")? + }; let imbalance = T::Currency::withdraw( transactor, @@ -223,14 +225,7 @@ pub fn buy_gas( ExistenceRequirement::KeepAlive )?; - Ok((GasMeter { - limit: gas_limit, - gas_left: gas_limit, - gas_price, - - #[cfg(test)] - tokens: Vec::new(), - }, imbalance)) + Ok((GasMeter::with_limit(gas_limit, gas_price), imbalance)) } /// Refund the unused gas. @@ -244,11 +239,11 @@ pub fn refund_unused_gas( // Increase total spent gas. // This cannot overflow, since `gas_spent` is never greater than `block_gas_limit`, which - // also has T::Gas type. - >::mutate(|block_gas_spent| *block_gas_spent += gas_spent); + // also has Gas type. + GasSpent::mutate(|block_gas_spent| *block_gas_spent += gas_spent); // Refund gas left by the price it was bought at. - let refund = gas_left.into() * gas_meter.gas_price; + let refund = gas_meter.gas_price * gas_left.unique_saturated_into(); let refund_imbalance = T::Currency::deposit_creating(transactor, refund); if let Ok(imbalance) = imbalance.offset(refund_imbalance) { T::GasPayment::on_unbalanced(imbalance); @@ -257,8 +252,10 @@ pub fn refund_unused_gas( /// A little handy utility for converting a value in balance units into approximate value in gas units /// at the given gas price. -pub fn approx_gas_for_balance(gas_price: BalanceOf, balance: BalanceOf) -> T::Gas { - (balance / gas_price).saturated_into::() +pub fn approx_gas_for_balance(gas_price: Balance, balance: Balance) -> Gas + where Balance: SimpleArithmetic +{ + (balance / gas_price).saturated_into::() } /// A simple utility macro that helps to match against a @@ -277,7 +274,7 @@ macro_rules! match_tokens { // have an iterator of Box and to downcast we need to specify // the type which we want downcast to. // - // So what we do is we assign `_pattern_typed_next_ref` to the a variable which has + // So what we do is we assign `_pattern_typed_next_ref` to a variable which has // the required type. // // Then we make `_pattern_typed_next_ref = token.downcast_ref()`. This makes @@ -304,25 +301,25 @@ mod tests { use super::{GasMeter, Token}; use crate::tests::Test; - /// A trivial token that charges 1 unit of gas. + /// A trivial token that charges the specified number of gas units. #[derive(Copy, Clone, PartialEq, Eq, Debug)] - struct UnitToken; - impl Token for UnitToken { + struct SimpleToken(u64); + impl Token for SimpleToken { type Metadata = (); - fn calculate_amount(&self, _metadata: &()) -> u64 { 1 } + fn calculate_amount(&self, _metadata: &()) -> u64 { self.0 } } - struct DoubleTokenMetadata { + struct MultiplierTokenMetadata { multiplier: u64, } /// A simple token that charges for the given amount multiplied to /// a multiplier taken from a given metadata. #[derive(Copy, Clone, PartialEq, Eq, Debug)] - struct DoubleToken(u64); + struct MultiplierToken(u64); - impl Token for DoubleToken { - type Metadata = DoubleTokenMetadata; - fn calculate_amount(&self, metadata: &DoubleTokenMetadata) -> u64 { + impl Token for MultiplierToken { + type Metadata = MultiplierTokenMetadata; + fn calculate_amount(&self, metadata: &MultiplierTokenMetadata) -> u64 { // Probably you want to use saturating mul in production code. self.0 * metadata.multiplier } @@ -338,7 +335,8 @@ mod tests { fn simple() { let mut gas_meter = GasMeter::::with_limit(50000, 10); - let result = gas_meter.charge(&DoubleTokenMetadata { multiplier: 3 }, DoubleToken(10)); + let result = gas_meter + .charge(&MultiplierTokenMetadata { multiplier: 3 }, MultiplierToken(10)); assert!(!result.is_out_of_gas()); assert_eq!(gas_meter.gas_left(), 49_970); @@ -349,12 +347,44 @@ mod tests { #[test] fn tracing() { let mut gas_meter = GasMeter::::with_limit(50000, 10); - assert!(!gas_meter.charge(&(), UnitToken).is_out_of_gas()); + assert!(!gas_meter.charge(&(), SimpleToken(1)).is_out_of_gas()); assert!(!gas_meter - .charge(&DoubleTokenMetadata { multiplier: 3 }, DoubleToken(10)) + .charge(&MultiplierTokenMetadata { multiplier: 3 }, MultiplierToken(10)) .is_out_of_gas()); let mut tokens = gas_meter.tokens()[0..2].iter(); - match_tokens!(tokens, UnitToken, DoubleToken(10),); + match_tokens!(tokens, SimpleToken(1), MultiplierToken(10),); + } + + // This test makes sure that nothing can be executed if there is no gas. + #[test] + fn refuse_to_execute_anything_if_zero() { + let mut gas_meter = GasMeter::::with_limit(0, 10); + assert!(gas_meter.charge(&(), SimpleToken(1)).is_out_of_gas()); + } + + // Make sure that if the gas meter is charged by exceeding amount then not only an error + // returned for that charge, but also for all consequent charges. + // + // This is not strictly necessary, because the execution should be interrupted immediately + // if the gas meter runs out of gas. However, this is just a nice property to have. + #[test] + fn overcharge_is_unrecoverable() { + let mut gas_meter = GasMeter::::with_limit(200, 10); + + // The first charge is should lead to OOG. + assert!(gas_meter.charge(&(), SimpleToken(300)).is_out_of_gas()); + + // The gas meter is emptied at this moment, so this should also fail. + assert!(gas_meter.charge(&(), SimpleToken(1)).is_out_of_gas()); + } + + + // Charging the exact amount that the user paid for should be + // possible. + #[test] + fn charge_exact_amount() { + let mut gas_meter = GasMeter::::with_limit(25, 10); + assert!(!gas_meter.charge(&(), SimpleToken(25)).is_out_of_gas()); } } diff --git a/srml/contracts/src/lib.rs b/srml/contracts/src/lib.rs index b9c8976cc04be0a97ffb33309b8acaa533fe9462..f38010a3557a9a07a5bc9c6c1376c85c97b9f86c 100644 --- a/srml/contracts/src/lib.rs +++ b/srml/contracts/src/lib.rs @@ -68,7 +68,8 @@ //! The Contract module is a work in progress. The following examples show how this Contract module can be //! used to create and call contracts. //! -//! * [`pDSL`](https://github.com/Robbepop/pdsl) is a domain specific language that enables writing +//! * [`ink`](https://github.com/paritytech/ink) is +//! an [`eDSL`](https://wiki.haskell.org/Embedded_domain_specific_language) that enables writing //! WebAssembly based smart contracts in the Rust programming language. This is a work in progress. //! //! ## Related Modules @@ -90,22 +91,24 @@ mod tests; use crate::exec::ExecutionContext; use crate::account_db::{AccountDb, DirectAccountDb}; +pub use crate::gas::Gas; #[cfg(feature = "std")] use serde::{Serialize, Deserialize}; use substrate_primitives::crypto::UncheckedFrom; -use rstd::{prelude::*, marker::PhantomData, convert::TryFrom}; +use rstd::{prelude::*, marker::PhantomData}; use parity_codec::{Codec, Encode, Decode}; use runtime_io::blake2_256; use runtime_primitives::traits::{ - Hash, SimpleArithmetic, Bounded, StaticLookup, Zero, MaybeSerializeDebug, Member + Hash, StaticLookup, Zero, MaybeSerializeDebug, Member }; use srml_support::dispatch::{Result, Dispatchable}; use srml_support::{ - Parameter, StorageMap, StorageValue, decl_module, decl_event, decl_storage, storage::child + Parameter, StorageMap, StorageValue, decl_module, decl_event, decl_storage, storage::child, + parameter_types, }; -use srml_support::traits::{OnFreeBalanceZero, OnUnbalanced, Currency}; -use system::{ensure_signed, RawOrigin}; +use srml_support::traits::{OnFreeBalanceZero, OnUnbalanced, Currency, Get}; +use system::{ensure_signed, RawOrigin, ensure_root}; use substrate_primitives::storage::well_known_keys::CHILD_STORAGE_KEY_PREFIX; use timestamp; @@ -157,7 +160,7 @@ impl ContractInfo { } } - /// If contract is tombstone then return some alive info + /// If contract is tombstone then return some tombstone info pub fn get_tombstone(self) -> Option> { if let ContractInfo::Tombstone(tombstone) = self { Some(tombstone) @@ -256,7 +259,7 @@ where fn trie_id(account_id: &T::AccountId) -> TrieId { // Note that skipping a value due to error is not an issue here. // We only need uniqueness, not sequence. - let new_seed = >::mutate(|v| { + let new_seed = AccountCounter::mutate(|v| { *v = v.wrapping_add(1); *v }); @@ -278,6 +281,41 @@ pub type BalanceOf = <::Currency as Currency< pub type NegativeImbalanceOf = <::Currency as Currency<::AccountId>>::NegativeImbalance; +parameter_types! { + /// A resonable default value for [`Trait::SignedClaimedHandicap`]. + pub const DefaultSignedClaimHandicap: u32 = 2; + /// A resonable default value for [`Trait::TombstoneDeposit`]. + pub const DefaultTombstoneDeposit: u32 = 16; + /// A resonable default value for [`Trait::StorageSizeOffset`]. + pub const DefaultStorageSizeOffset: u32 = 8; + /// A resonable default value for [`Trait::RentByteFee`]. + pub const DefaultRentByteFee: u32 = 4; + /// A resonable default value for [`Trait::RentDepositOffset`]. + pub const DefaultRentDepositOffset: u32 = 1000; + /// A resonable default value for [`Trait::SurchargeReward`]. + pub const DefaultSurchargeReward: u32 = 150; + /// A resonable default value for [`Trait::TransferFee`]. + pub const DefaultTransferFee: u32 = 0; + /// A resonable default value for [`Trait::CreationFee`]. + pub const DefaultCreationFee: u32 = 0; + /// A resonable default value for [`Trait::TransactionBaseFee`]. + pub const DefaultTransactionBaseFee: u32 = 0; + /// A resonable default value for [`Trait::TransactionByteFee`]. + pub const DefaultTransactionByteFee: u32 = 0; + /// A resonable default value for [`Trait::ContractFee`]. + pub const DefaultContractFee: u32 = 21; + /// A resonable default value for [`Trait::CallBaseFee`]. + pub const DefaultCallBaseFee: u32 = 1000; + /// A resonable default value for [`Trait::CreateBaseFee`]. + pub const DefaultCreateBaseFee: u32 = 1000; + /// A resonable default value for [`Trait::MaxDepth`]. + pub const DefaultMaxDepth: u32 = 1024; + /// A resonable default value for [`Trait::MaxValueSize`]. + pub const DefaultMaxValueSize: u32 = 16_384; + /// A resonable default value for [`Trait::BlockGasLimit`]. + pub const DefaultBlockGasLimit: u32 = 10_000_000; +} + pub trait Trait: timestamp::Trait { type Currency: Currency; @@ -287,9 +325,6 @@ pub trait Trait: timestamp::Trait { /// The overarching event type. type Event: From> + Into<::Event>; - type Gas: Parameter + Default + Codec + SimpleArithmetic + Bounded + Copy + - Into> + TryFrom>; - /// A function type to get the contract address given the creator. type DetermineContractAddress: ContractAddressFor, Self::AccountId>; @@ -299,11 +334,70 @@ pub trait Trait: timestamp::Trait { /// by the Executive module for regular dispatch. type ComputeDispatchFee: ComputeDispatchFee>; - /// trieid id generator + /// trie id generator type TrieIdGenerator: TrieIdGenerator; /// Handler for the unbalanced reduction when making a gas payment. type GasPayment: OnUnbalanced>; + + /// Number of block delay an extrinsic claim surcharge has. + /// + /// When claim surcharge is called by an extrinsic the rent is checked + /// for current_block - delay + type SignedClaimHandicap: Get; + + /// The minimum amount required to generate a tombstone. + type TombstoneDeposit: Get>; + + /// Size of a contract at the time of creation. This is a simple way to ensure + /// that empty contracts eventually gets deleted. + type StorageSizeOffset: Get; + + /// Price of a byte of storage per one block interval. Should be greater than 0. + type RentByteFee: Get>; + + /// The amount of funds a contract should deposit in order to offset + /// the cost of one byte. + /// + /// Let's suppose the deposit is 1,000 BU (balance units)/byte and the rent is 1 BU/byte/day, + /// then a contract with 1,000,000 BU that uses 1,000 bytes of storage would pay no rent. + /// But if the balance reduced to 500,000 BU and the storage stayed the same at 1,000, + /// then it would pay 500 BU/day. + type RentDepositOffset: Get>; + + /// Reward that is received by the party whose touch has led + /// to removal of a contract. + type SurchargeReward: Get>; + + /// The fee required to make a transfer. + type TransferFee: Get>; + + /// The fee required to create an account. + type CreationFee: Get>; + + /// The fee to be paid for making a transaction; the base. + type TransactionBaseFee: Get>; + + /// The fee to be paid for making a transaction; the per-byte portion. + type TransactionByteFee: Get>; + + /// The fee required to create a contract instance. + type ContractFee: Get>; + + /// The base fee charged for calling into a contract. + type CallBaseFee: Get; + + /// The base fee charged for creating a contract. + type CreateBaseFee: Get; + + /// The maximum nesting level of a call/create stack. + type MaxDepth: Get; + + /// The maximum size of a storage value in bytes. + type MaxValueSize: Get; + + /// The maximum amount of gas that could be expended per block. + type BlockGasLimit: Get; } /// Simple contract address determiner. @@ -335,8 +429,8 @@ pub struct DefaultDispatchFeeComputor(PhantomData); impl ComputeDispatchFee> for DefaultDispatchFeeComputor { fn compute_dispatch_fee(call: &T::Call) -> BalanceOf { let encoded_len = call.using_encoded(|encoded| encoded.len() as u32); - let base_fee = >::transaction_base_fee(); - let byte_fee = >::transaction_byte_fee(); + let base_fee = T::TransactionBaseFee::get(); + let byte_fee = T::TransactionByteFee::get(); base_fee + byte_fee * encoded_len.into() } } @@ -344,18 +438,83 @@ impl ComputeDispatchFee> for DefaultDispatchFeeC decl_module! { /// Contracts module. pub struct Module for enum Call where origin: ::Origin { + /// Number of block delay an extrinsic claim surcharge has. + /// + /// When claim surcharge is called by an extrinsic the rent is checked + /// for current_block - delay + const SignedClaimHandicap: T::BlockNumber = T::SignedClaimHandicap::get(); + + /// The minimum amount required to generate a tombstone. + const TombstoneDeposit: BalanceOf = T::TombstoneDeposit::get(); + + /// Size of a contract at the time of creation. This is a simple way to ensure + /// that empty contracts eventually gets deleted. + const StorageSizeOffset: u32 = T::StorageSizeOffset::get(); + + /// Price of a byte of storage per one block interval. Should be greater than 0. + const RentByteFee: BalanceOf = T::RentByteFee::get(); + + /// The amount of funds a contract should deposit in order to offset + /// the cost of one byte. + /// + /// Let's suppose the deposit is 1,000 BU (balance units)/byte and the rent is 1 BU/byte/day, + /// then a contract with 1,000,000 BU that uses 1,000 bytes of storage would pay no rent. + /// But if the balance reduced to 500,000 BU and the storage stayed the same at 1,000, + /// then it would pay 500 BU/day. + const RentDepositOffset: BalanceOf = T::RentDepositOffset::get(); + + /// Reward that is received by the party whose touch has led + /// to removal of a contract. + const SurchargeReward: BalanceOf = T::SurchargeReward::get(); + + /// The fee required to make a transfer. + const TransferFee: BalanceOf = T::TransferFee::get(); + + /// The fee required to create an account. + const CreationFee: BalanceOf = T::CreationFee::get(); + + /// The fee to be paid for making a transaction; the base. + const TransactionBaseFee: BalanceOf = T::TransactionBaseFee::get(); + + /// The fee to be paid for making a transaction; the per-byte portion. + const TransactionByteFee: BalanceOf = T::TransactionByteFee::get(); + + /// The fee required to create a contract instance. A reasonable default value + /// is 21. + const ContractFee: BalanceOf = T::ContractFee::get(); + + /// The base fee charged for calling into a contract. A reasonable default + /// value is 135. + const CallBaseFee: Gas = T::CallBaseFee::get(); + + /// The base fee charged for creating a contract. A reasonable default value + /// is 175. + const CreateBaseFee: Gas = T::CreateBaseFee::get(); + + /// The maximum nesting level of a call/create stack. A reasonable default + /// value is 100. + const MaxDepth: u32 = T::MaxDepth::get(); + + /// The maximum size of a storage value in bytes. A reasonable default is 16 KiB. + const MaxValueSize: u32 = T::MaxValueSize::get(); + + /// The maximum amount of gas that could be expended per block. A reasonable + /// default value is 10_000_000. + const BlockGasLimit: Gas = T::BlockGasLimit::get(); + fn deposit_event() = default; /// Updates the schedule for metering contracts. /// /// The schedule must have a greater version than the stored schedule. - pub fn update_schedule(schedule: Schedule) -> Result { + pub fn update_schedule(origin, schedule: Schedule) -> Result { + ensure_root(origin)?; if >::current_schedule().version >= schedule.version { return Err("new schedule must have a greater version than current"); } Self::deposit_event(RawEvent::ScheduleUpdated(schedule.version)); - >::put(schedule); + CurrentSchedule::put(schedule); Ok(()) } @@ -364,7 +523,7 @@ decl_module! { /// You can instantiate contracts only with stored code. pub fn put_code( origin, - #[compact] gas_limit: T::Gas, + #[compact] gas_limit: Gas, code: Vec ) -> Result { let origin = ensure_signed(origin)?; @@ -393,7 +552,7 @@ decl_module! { origin, dest: ::Source, #[compact] value: BalanceOf, - #[compact] gas_limit: T::Gas, + #[compact] gas_limit: Gas, data: Vec ) -> Result { let origin = ensure_signed(origin)?; @@ -453,7 +612,7 @@ decl_module! { pub fn create( origin, #[compact] endowment: BalanceOf, - #[compact] gas_limit: T::Gas, + #[compact] gas_limit: Gas, code_hash: CodeHash, data: Vec ) -> Result { @@ -521,14 +680,14 @@ decl_module! { // adding a handicap: for signed extrinsics we use a slightly older block number // for the eviction check. This can be viewed as if we pushed regular users back in past. let handicap = if signed { - >::signed_claim_handicap() + T::SignedClaimHandicap::get() } else { Zero::zero() }; // If poking the contract has lead to eviction of the contract, give out the rewards. if rent::try_evict::(&dest, handicap) == rent::RentOutcome::Evicted { - T::Currency::deposit_into_existing(rewarded, Self::surcharge_reward())?; + T::Currency::deposit_into_existing(rewarded, T::SurchargeReward::get())?; } } @@ -611,7 +770,7 @@ decl_module! { } fn on_finalize() { - >::kill(); + GasSpent::kill(); } } } @@ -646,53 +805,10 @@ decl_event! { decl_storage! { trait Store for Module as Contract { - /// Number of block delay an extrinsic claim surcharge has. - /// - /// When claim surchage is called by an extrinsic the rent is checked - /// for current_block - delay - SignedClaimHandicap get(signed_claim_handicap) config(): T::BlockNumber; - /// The minimum amount required to generate a tombstone. - TombstoneDeposit get(tombstone_deposit) config(): BalanceOf; - /// Size of a contract at the time of creation. This is a simple way to ensure - /// that empty contracts eventually gets deleted. - StorageSizeOffset get(storage_size_offset) config(): u32; - /// Price of a byte of storage per one block interval. Should be greater than 0. - RentByteFee get(rent_byte_price) config(): BalanceOf; - /// The amount of funds a contract should deposit in order to offset - /// the cost of one byte. - /// - /// Let's suppose the deposit is 1,000 BU (balance units)/byte and the rent is 1 BU/byte/day, - /// then a contract with 1,000,000 BU that uses 1,000 bytes of storage would pay no rent. - /// But if the balance reduced to 500,000 BU and the storage stayed the same at 1,000, - /// then it would pay 500 BU/day. - RentDepositOffset get(rent_deposit_offset) config(): BalanceOf; - /// Reward that is received by the party whose touch has led - /// to removal of a contract. - SurchargeReward get(surcharge_reward) config(): BalanceOf; - /// The fee required to make a transfer. - TransferFee get(transfer_fee) config(): BalanceOf; - /// The fee required to create an account. - CreationFee get(creation_fee) config(): BalanceOf; - /// The fee to be paid for making a transaction; the base. - TransactionBaseFee get(transaction_base_fee) config(): BalanceOf; - /// The fee to be paid for making a transaction; the per-byte portion. - TransactionByteFee get(transaction_byte_fee) config(): BalanceOf; - /// The fee required to create a contract instance. - ContractFee get(contract_fee) config(): BalanceOf = 21.into(); - /// The base fee charged for calling into a contract. - CallBaseFee get(call_base_fee) config(): T::Gas = 135.into(); - /// The base fee charged for creating a contract. - CreateBaseFee get(create_base_fee) config(): T::Gas = 175.into(); - /// The price of one unit of gas. - GasPrice get(gas_price) config(): BalanceOf = 1.into(); - /// The maximum nesting level of a call/create stack. - MaxDepth get(max_depth) config(): u32 = 100; - /// The maximum amount of gas that could be expended per block. - BlockGasLimit get(block_gas_limit) config(): T::Gas = 10_000_000.into(); /// Gas spent so far in this block. - GasSpent get(gas_spent): T::Gas; + GasSpent get(gas_spent): Gas; /// Current cost schedule for contracts. - CurrentSchedule get(current_schedule) config(): Schedule = Schedule::default(); + CurrentSchedule get(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. @@ -701,6 +817,8 @@ decl_storage! { pub AccountCounter: u64 = 0; /// 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(); } } @@ -718,14 +836,13 @@ impl OnFreeBalanceZero for Module { /// We assume that these values can't be changed in the /// course of transaction execution. pub struct Config { - pub schedule: Schedule, + pub schedule: Schedule, pub existential_deposit: BalanceOf, pub max_depth: u32, + pub max_value_size: u32, pub contract_account_instantiate_fee: BalanceOf, pub account_create_fee: BalanceOf, pub transfer_fee: BalanceOf, - pub call_base_fee: T::Gas, - pub instantiate_base_fee: T::Gas, } impl Config { @@ -733,12 +850,11 @@ impl Config { Config { schedule: >::current_schedule(), existential_deposit: T::Currency::minimum_balance(), - max_depth: >::max_depth(), - contract_account_instantiate_fee: >::contract_fee(), - account_create_fee: >::creation_fee(), - transfer_fee: >::transfer_fee(), - call_base_fee: >::call_base_fee(), - instantiate_base_fee: >::create_base_fee(), + max_depth: T::MaxDepth::get(), + max_value_size: T::MaxValueSize::get(), + contract_account_instantiate_fee: T::ContractFee::get(), + account_create_fee: T::CreationFee::get(), + transfer_fee: T::TransferFee::get(), } } } @@ -746,7 +862,7 @@ 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)] -pub struct Schedule { +pub struct Schedule { /// Version of the schedule. pub version: u32, @@ -771,6 +887,12 @@ pub struct Schedule { /// Gas cost to deposit an event; the base. pub event_base_cost: Gas, + /// Base gas cost to call into a contract. + pub call_base_cost: Gas, + + /// Base gas cost to instantiate a contract. + pub instantiate_base_cost: Gas, + /// Gas cost per one byte read from the sandbox memory. pub sandbox_data_read_cost: Gas, @@ -789,6 +911,9 @@ pub struct Schedule { /// Maximum number of memory pages allowed for a contract. pub max_memory_pages: u32, + /// Maximum allowed size of a declared table. + pub max_table_size: u32, + /// Whether the `ext_println` function is allowed to be used contracts. /// MUST only be enabled for `dev` chains, NOT for production chains pub enable_println: bool, @@ -797,22 +922,25 @@ pub struct Schedule { pub max_subject_len: u32, } -impl> Default for Schedule { - fn default() -> Schedule { +impl Default for Schedule { + fn default() -> Schedule { Schedule { version: 0, - put_code_per_byte_cost: 1.into(), - grow_mem_cost: 1.into(), - regular_op_cost: 1.into(), - return_data_per_byte_cost: 1.into(), - event_data_per_byte_cost: 1.into(), - event_per_topic_cost: 1.into(), - event_base_cost: 1.into(), - sandbox_data_read_cost: 1.into(), - sandbox_data_write_cost: 1.into(), + put_code_per_byte_cost: 1, + grow_mem_cost: 1, + regular_op_cost: 1, + return_data_per_byte_cost: 1, + event_data_per_byte_cost: 1, + event_per_topic_cost: 1, + event_base_cost: 1, + call_base_cost: 135, + instantiate_base_cost: 175, + sandbox_data_read_cost: 1, + sandbox_data_write_cost: 1, max_event_topics: 4, max_stack_height: 64 * 1024, max_memory_pages: 16, + max_table_size: 16 * 1024, enable_println: false, max_subject_len: 32, } diff --git a/srml/contracts/src/rent.rs b/srml/contracts/src/rent.rs index 3baf043b90d37d16ceefd29e141f5c727a5d3eb0..96f8516a5f189c2bba8b23a60b9d350d99e7ae15 100644 --- a/srml/contracts/src/rent.rs +++ b/srml/contracts/src/rent.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::{BalanceOf, ContractInfo, ContractInfoOf, Module, TombstoneContractInfo, Trait}; +use crate::{BalanceOf, ContractInfo, ContractInfoOf, TombstoneContractInfo, Trait, AliveContractInfo}; use runtime_primitives::traits::{Bounded, CheckedDiv, CheckedMul, Saturating, Zero, SaturatedConversion}; -use srml_support::traits::{Currency, ExistenceRequirement, Imbalance, WithdrawReason}; +use srml_support::traits::{Currency, ExistenceRequirement, Get, WithdrawReason}; use srml_support::StorageMap; #[derive(PartialEq, Eq, Copy, Clone)] @@ -50,9 +50,10 @@ fn try_evict_or_and_pay_rent( account: &T::AccountId, handicap: T::BlockNumber, pay_rent: bool, -) -> RentOutcome { - let contract = match >::get(account) { - None | Some(ContractInfo::Tombstone(_)) => return RentOutcome::Exempted, +) -> (RentOutcome, Option>) { + let contract_info = >::get(account); + let contract = match contract_info { + None | Some(ContractInfo::Tombstone(_)) => return (RentOutcome::Exempted, contract_info), Some(ContractInfo::Alive(contract)) => contract, }; @@ -65,7 +66,7 @@ fn try_evict_or_and_pay_rent( let n = effective_block_number.saturating_sub(contract.deduct_block); if n.is_zero() { // Rent has already been paid - return RentOutcome::Exempted; + return (RentOutcome::Exempted, Some(ContractInfo::Alive(contract))); } n }; @@ -75,34 +76,46 @@ fn try_evict_or_and_pay_rent( // An amount of funds to charge per block for storage taken up by the contract. let fee_per_block = { let free_storage = balance - .checked_div(&>::rent_deposit_offset()) + .checked_div(&T::RentDepositOffset::get()) .unwrap_or_else(Zero::zero); let effective_storage_size = >::from(contract.storage_size).saturating_sub(free_storage); effective_storage_size - .checked_mul(&>::rent_byte_price()) + .checked_mul(&T::RentByteFee::get()) .unwrap_or(>::max_value()) }; if fee_per_block.is_zero() { // The rent deposit offset reduced the fee to 0. This means that the contract // gets the rent for free. - return RentOutcome::Exempted; + return (RentOutcome::Exempted, Some(ContractInfo::Alive(contract))); } // The minimal amount of funds required for a contract not to be evicted. - let subsistence_threshold = T::Currency::minimum_balance() + >::tombstone_deposit(); + let subsistence_threshold = T::Currency::minimum_balance() + T::TombstoneDeposit::get(); + + if balance < subsistence_threshold { + // The contract cannot afford to leave a tombstone, so remove the contract info altogether. + >::remove(account); + runtime_io::kill_child_storage(&contract.trie_id); + return (RentOutcome::Evicted, None); + } let dues = fee_per_block .checked_mul(&blocks_passed.saturated_into::().into()) .unwrap_or(>::max_value()); - - let dues_limited = dues.min(contract.rent_allowance); - let rent_allowance_exceeded = dues > contract.rent_allowance; - let is_below_subsistence = balance < subsistence_threshold; - let go_below_subsistence = balance.saturating_sub(dues_limited) < subsistence_threshold; + let rent_budget = contract.rent_allowance.min(balance - subsistence_threshold); + let insufficient_rent = rent_budget < dues; + + // If the rent payment cannot be withdrawn due to locks on the account balance, then evict the + // account. + // + // NOTE: This seems problematic because it provides a way to tombstone an account while + // avoiding the last rent payment. In effect, someone could retroactively set rent_allowance + // for their contract to 0. + let dues_limited = dues.min(rent_budget); let can_withdraw_rent = T::Currency::ensure_can_withdraw( account, dues_limited, @@ -111,75 +124,60 @@ fn try_evict_or_and_pay_rent( ) .is_ok(); - if !rent_allowance_exceeded && can_withdraw_rent && !go_below_subsistence { - // Collect dues - - if pay_rent { - let imbalance = T::Currency::withdraw( - account, - dues, - WithdrawReason::Fee, - ExistenceRequirement::KeepAlive, - ) - .expect( - "Withdraw has been checked above; - go_below_subsistence is false and subsistence > existencial_deposit; - qed", - ); - - >::mutate(account, |contract| { - let contract = contract - .as_mut() - .and_then(|c| c.as_alive_mut()) - .expect("Dead or inexistent account has been exempt above; qed"); - - contract.rent_allowance -= imbalance.peek(); // rent_allowance is not exceeded - contract.deduct_block = current_block_number; - }) - } + if can_withdraw_rent && (insufficient_rent || pay_rent) { + // Collect dues. + let _ = T::Currency::withdraw( + account, + dues_limited, + WithdrawReason::Fee, + ExistenceRequirement::KeepAlive, + ) + .expect( + "Withdraw has been checked above; + dues_limited < rent_budget < balance - subsistence < balance - existential_deposit; + qed", + ); + } - RentOutcome::Ok - } else { - // Evict - - if can_withdraw_rent && !go_below_subsistence { - T::Currency::withdraw( - account, - dues, - WithdrawReason::Fee, - ExistenceRequirement::KeepAlive, - ) - .expect("Can withdraw and don't go below subsistence"); - } else if !is_below_subsistence { - T::Currency::make_free_balance_be(account, subsistence_threshold); - } else { - T::Currency::make_free_balance_be(account, >::zero()); - } + if insufficient_rent || !can_withdraw_rent { + // The contract cannot afford the rent payment and has a balance above the subsistence + // threshold, so it leaves a tombstone. - if !is_below_subsistence { - // The contract has funds above subsistence deposit and that means it can afford to - // leave tombstone. + // Note: this operation is heavy. + let child_storage_root = runtime_io::child_storage_root(&contract.trie_id); - // Note: this operation is heavy. - let child_storage_root = runtime_io::child_storage_root(&contract.trie_id); + let tombstone = >::new( + &child_storage_root[..], + contract.code_hash, + ); + let tombstone_info = ContractInfo::Tombstone(tombstone); + >::insert(account, &tombstone_info); - let tombstone = >::new( - &child_storage_root[..], - contract.code_hash, - ); - >::insert(account, ContractInfo::Tombstone(tombstone)); - runtime_io::kill_child_storage(&contract.trie_id); - } + runtime_io::kill_child_storage(&contract.trie_id); + + return (RentOutcome::Evicted, Some(tombstone_info)); + } - RentOutcome::Evicted + if pay_rent { + let contract_info = ContractInfo::Alive(AliveContractInfo:: { + rent_allowance: contract.rent_allowance - dues, // rent_allowance is not exceeded + deduct_block: current_block_number, + ..contract + }); + + >::insert(account, &contract_info); + + return (RentOutcome::Ok, Some(contract_info)); } + + (RentOutcome::Ok, Some(ContractInfo::Alive(contract))) } /// Make account paying the rent for the current block number /// /// NOTE: This function acts eagerly. -pub fn pay_rent(account: &T::AccountId) { - let _ = try_evict_or_and_pay_rent::(account, Zero::zero(), true); +pub fn pay_rent(account: &T::AccountId) -> Option> { + try_evict_or_and_pay_rent::(account, Zero::zero(), true).1 } /// Evict the account if it should be evicted at the given block number. @@ -190,5 +188,5 @@ pub fn pay_rent(account: &T::AccountId) { /// /// NOTE: This function acts eagerly. pub fn try_evict(account: &T::AccountId, handicap: T::BlockNumber) -> RentOutcome { - try_evict_or_and_pay_rent::(account, handicap, false) + try_evict_or_and_pay_rent::(account, handicap, false).0 } diff --git a/srml/contracts/src/tests.rs b/srml/contracts/src/tests.rs index 065d7da890748c0b7bbaf9a81f881e0eaebb5817..3af81d8d8419496d23afa300294fe6611a97490b 100644 --- a/srml/contracts/src/tests.rs +++ b/srml/contracts/src/tests.rs @@ -30,12 +30,13 @@ use parity_codec::{Decode, Encode, KeyedVec}; use runtime_io; use runtime_io::with_externalities; use runtime_primitives::testing::{Digest, DigestItem, Header, UintAuthorityId, H256}; -use runtime_primitives::traits::{BlakeTwo256, IdentityLookup}; +use runtime_primitives::traits::{BlakeTwo256, Hash, IdentityLookup}; use runtime_primitives::BuildStorage; use srml_support::{ - assert_ok, assert_err, impl_outer_dispatch, impl_outer_event, impl_outer_origin, storage::child, - traits::Currency, StorageMap, StorageValue + assert_ok, assert_err, impl_outer_dispatch, impl_outer_event, impl_outer_origin, parameter_types, + storage::child, StorageMap, StorageValue, traits::{Currency, Get}, }; +use std::cell::RefCell; use std::sync::atomic::{AtomicUsize, Ordering}; use substrate_primitives::storage::well_known_keys; use substrate_primitives::Blake2Hasher; @@ -64,8 +65,42 @@ impl_outer_dispatch! { } } +thread_local! { + static EXISTENTIAL_DEPOSIT: RefCell = RefCell::new(0); + static TRANSFER_FEE: RefCell = RefCell::new(0); + static CREATION_FEE: RefCell = RefCell::new(0); + static BLOCK_GAS_LIMIT: RefCell = RefCell::new(0); +} + +pub struct ExistentialDeposit; +impl Get for ExistentialDeposit { + fn get() -> u64 { EXISTENTIAL_DEPOSIT.with(|v| *v.borrow()) } +} + +pub struct TransferFee; +impl Get for TransferFee { + fn get() -> u64 { TRANSFER_FEE.with(|v| *v.borrow()) } +} + +pub struct CreationFee; +impl Get for CreationFee { + fn get() -> u64 { CREATION_FEE.with(|v| *v.borrow()) } +} + +pub struct BlockGasLimit; +impl Get for BlockGasLimit { + fn get() -> u64 { BLOCK_GAS_LIMIT.with(|v| *v.borrow()) } +} + #[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 BalancesTransactionBaseFee: u64 = 0; + pub const BalancesTransactionByteFee: u64 = 0; +} impl system::Trait for Test { type Origin = Origin; type Index = u64; @@ -75,7 +110,11 @@ 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; + type MaximumBlockLength = MaximumBlockLength; } impl balances::Trait for Test { type Balance = u64; @@ -85,20 +124,59 @@ impl balances::Trait for Test { type TransactionPayment = (); type DustRemoval = (); type TransferPayment = (); + type ExistentialDeposit = ExistentialDeposit; + type TransferFee = TransferFee; + type CreationFee = CreationFee; + type TransactionBaseFee = BalancesTransactionBaseFee; + type TransactionByteFee = BalancesTransactionByteFee; +} +parameter_types! { + pub const MinimumPeriod: u64 = 1; } impl timestamp::Trait for Test { type Moment = u64; type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; +} +parameter_types! { + pub const SignedClaimHandicap: u64 = 2; + pub const TombstoneDeposit: u64 = 16; + pub const StorageSizeOffset: u32 = 8; + pub const RentByteFee: u64 = 4; + pub const RentDepositOffset: u64 = 10_000; + pub const SurchargeReward: u64 = 150; + pub const TransactionBaseFee: u64 = 2; + pub const TransactionByteFee: u64 = 6; + pub const ContractFee: u64 = 21; + pub const CallBaseFee: u64 = 135; + pub const CreateBaseFee: u64 = 175; + pub const MaxDepth: u32 = 100; + pub const MaxValueSize: u32 = 16_384; } impl Trait for Test { type Currency = Balances; type Call = Call; - type Gas = u64; type DetermineContractAddress = DummyContractAddressFor; type Event = MetaEvent; type ComputeDispatchFee = DummyComputeDispatchFee; type TrieIdGenerator = DummyTrieIdGenerator; type GasPayment = (); + type SignedClaimHandicap = SignedClaimHandicap; + type TombstoneDeposit = TombstoneDeposit; + type StorageSizeOffset = StorageSizeOffset; + type RentByteFee = RentByteFee; + type RentDepositOffset = RentDepositOffset; + type SurchargeReward = SurchargeReward; + type TransferFee = TransferFee; + type CreationFee = CreationFee; + type TransactionBaseFee = TransactionBaseFee; + type TransactionByteFee = TransactionByteFee; + type ContractFee = ContractFee; + type CallBaseFee = CallBaseFee; + type CreateBaseFee = CreateBaseFee; + type MaxDepth = MaxDepth; + type MaxValueSize = MaxValueSize; + type BlockGasLimit = BlockGasLimit; } type Balances = balances::Module; @@ -117,7 +195,7 @@ impl TrieIdGenerator for DummyTrieIdGenerator { fn trie_id(account_id: &u64) -> TrieId { use substrate_primitives::storage::well_known_keys; - let new_seed = >::mutate(|v| { + let new_seed = super::AccountCounter::mutate(|v| { *v = v.wrapping_add(1); *v }); @@ -183,61 +261,48 @@ impl ExtBuilder { self.creation_fee = creation_fee; self } + pub fn set_associated_consts(&self) { + EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); + TRANSFER_FEE.with(|v| *v.borrow_mut() = self.transfer_fee); + CREATION_FEE.with(|v| *v.borrow_mut() = self.creation_fee); + BLOCK_GAS_LIMIT.with(|v| *v.borrow_mut() = self.block_gas_limit); + } pub fn build(self) -> runtime_io::TestExternalities { - let mut t = system::GenesisConfig::::default() - .build_storage() - .unwrap() - .0; - t.extend( - balances::GenesisConfig:: { - transaction_base_fee: 0, - transaction_byte_fee: 0, - balances: vec![], - existential_deposit: self.existential_deposit, - transfer_fee: self.transfer_fee, - creation_fee: self.creation_fee, - vesting: vec![], - } - .build_storage() - .unwrap() - .0, - ); - t.extend( - GenesisConfig:: { - signed_claim_handicap: 2, - rent_byte_price: 4, - rent_deposit_offset: 10_000, - storage_size_offset: 8, - surcharge_reward: 150, - tombstone_deposit: 16, - transaction_base_fee: 2, - transaction_byte_fee: 6, - transfer_fee: self.transfer_fee, - creation_fee: self.creation_fee, - contract_fee: 21, - call_base_fee: 135, - create_base_fee: 175, - gas_price: self.gas_price, - max_depth: 100, - block_gas_limit: self.block_gas_limit, - current_schedule: Default::default(), - } - .build_storage() - .unwrap() - .0, - ); - runtime_io::TestExternalities::new(t) + self.set_associated_consts(); + let mut t = system::GenesisConfig::default().build_storage::().unwrap(); + balances::GenesisConfig:: { + balances: vec![], + vesting: vec![], + }.assimilate_storage(&mut t.0, &mut t.1).unwrap(); + GenesisConfig:: { + current_schedule: Default::default(), + gas_price: self.gas_price, + }.assimilate_storage(&mut t.0, &mut t.1).unwrap(); + runtime_io::TestExternalities::new_with_children(t) } } +/// Generate Wasm binary and code hash from wabt source. +fn compile_module(wabt_module: &str) + -> Result<(Vec, ::Output), wabt::Error> + where T: system::Trait +{ + let wasm = wabt::wat2wasm(wabt_module)?; + let code_hash = T::Hashing::hash(&wasm); + Ok((wasm, code_hash)) +} + +// Perform a simple transfer to a non-existent account supplying way more gas than needed. +// Then we check that the all unused gas is refunded. #[test] fn refunds_unused_gas() { - with_externalities(&mut ExtBuilder::default().build(), || { - Balances::deposit_creating(&0, 100_000_000); + with_externalities(&mut ExtBuilder::default().gas_price(2).build(), || { + Balances::deposit_creating(&ALICE, 100_000_000); - assert_ok!(Contract::call(Origin::signed(0), 1, 0, 100_000, Vec::new())); + assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, Vec::new())); - assert_eq!(Balances::free_balance(&0), 100_000_000 - (2 * 135)); + // 2 * 135 - gas price multiplied by the call base fee. + assert_eq!(Balances::free_balance(&ALICE), 100_000_000 - (2 * 135)); }); } @@ -256,7 +321,7 @@ fn account_removal_removes_storage() { Balances::deposit_creating(&1, 110); ContractInfoOf::::insert(1, &ContractInfo::Alive(RawAliveContractInfo { trie_id: trie_id1.clone(), - storage_size: Contract::storage_size_offset(), + storage_size: ::StorageSizeOffset::get(), deduct_block: System::block_number(), code_hash: H256::repeat_byte(1), rent_allowance: 40, @@ -271,7 +336,7 @@ fn account_removal_removes_storage() { Balances::deposit_creating(&2, 110); ContractInfoOf::::insert(2, &ContractInfo::Alive(RawAliveContractInfo { trie_id: trie_id2.clone(), - storage_size: Contract::storage_size_offset(), + storage_size: ::StorageSizeOffset::get(), deduct_block: System::block_number(), code_hash: H256::repeat_byte(2), rent_allowance: 40, @@ -338,11 +403,10 @@ const CODE_RETURN_FROM_START_FN: &str = r#" (data (i32.const 8) "\01\02\03\04") ) "#; -const HASH_RETURN_FROM_START_FN: [u8; 32] = hex!("66c45bd7c473a1746e1d241176166ef53b1f207f56c5e87d1b6650140704181b"); #[test] fn instantiate_and_call_and_deposit_event() { - let wasm = wabt::wat2wasm(CODE_RETURN_FROM_START_FN).unwrap(); + let (wasm, code_hash) = compile_module::(CODE_RETURN_FROM_START_FN).unwrap(); with_externalities( &mut ExtBuilder::default().existential_deposit(100).build(), @@ -356,7 +420,7 @@ fn instantiate_and_call_and_deposit_event() { Origin::signed(ALICE), 100, 100_000, - HASH_RETURN_FROM_START_FN.into(), + code_hash.into(), vec![], ); @@ -368,7 +432,7 @@ fn instantiate_and_call_and_deposit_event() { }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::CodeStored(HASH_RETURN_FROM_START_FN.into())), + event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())), topics: vec![], }, EventRecord { @@ -417,7 +481,6 @@ const CODE_DISPATCH_CALL: &str = r#" (data (i32.const 8) "\00\00\03\00\00\00\00\00\00\00\C8") ) "#; -const HASH_DISPATCH_CALL: [u8; 32] = hex!("49dfdcaf9c1553be10634467e95b8e71a3bc15a4f8bf5563c0312b0902e0afb9"); #[test] fn dispatch_call() { @@ -426,7 +489,7 @@ fn dispatch_call() { let encoded = Encode::encode(&Call::Balances(balances::Call::transfer(CHARLIE, 50))); assert_eq!(&encoded[..], &hex!("00000300000000000000C8")[..]); - let wasm = wabt::wat2wasm(CODE_DISPATCH_CALL).unwrap(); + let (wasm, code_hash) = compile_module::(CODE_DISPATCH_CALL).unwrap(); with_externalities( &mut ExtBuilder::default().existential_deposit(50).build(), @@ -445,7 +508,7 @@ fn dispatch_call() { }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::CodeStored(HASH_DISPATCH_CALL.into())), + event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())), topics: vec![], }, ]); @@ -454,7 +517,7 @@ fn dispatch_call() { Origin::signed(ALICE), 100, 100_000, - HASH_DISPATCH_CALL.into(), + code_hash.into(), vec![], )); @@ -474,7 +537,7 @@ fn dispatch_call() { }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::CodeStored(HASH_DISPATCH_CALL.into())), + event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())), topics: vec![], }, EventRecord { @@ -539,7 +602,6 @@ const CODE_DISPATCH_CALL_THEN_TRAP: &str = r#" (data (i32.const 8) "\00\00\03\00\00\00\00\00\00\00\C8") ) "#; -const HASH_DISPATCH_CALL_THEN_TRAP: [u8; 32] = hex!("55fe5c142dfe2519ca76c7c9b9f05012bd2624b7dcc128d2ce5a7af9d2da1846"); #[test] fn dispatch_call_not_dispatched_after_top_level_transaction_failure() { @@ -548,7 +610,7 @@ fn dispatch_call_not_dispatched_after_top_level_transaction_failure() { let encoded = Encode::encode(&Call::Balances(balances::Call::transfer(CHARLIE, 50))); assert_eq!(&encoded[..], &hex!("00000300000000000000C8")[..]); - let wasm = wabt::wat2wasm(CODE_DISPATCH_CALL_THEN_TRAP).unwrap(); + let (wasm, code_hash) = compile_module::(CODE_DISPATCH_CALL_THEN_TRAP).unwrap(); with_externalities( &mut ExtBuilder::default().existential_deposit(50).build(), @@ -567,7 +629,7 @@ fn dispatch_call_not_dispatched_after_top_level_transaction_failure() { }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::CodeStored(HASH_DISPATCH_CALL_THEN_TRAP.into())), + event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())), topics: vec![], }, ]); @@ -576,7 +638,7 @@ fn dispatch_call_not_dispatched_after_top_level_transaction_failure() { Origin::signed(ALICE), 100, 100_000, - HASH_DISPATCH_CALL_THEN_TRAP.into(), + code_hash.into(), vec![], )); @@ -600,7 +662,7 @@ fn dispatch_call_not_dispatched_after_top_level_transaction_failure() { }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::CodeStored(HASH_DISPATCH_CALL_THEN_TRAP.into())), + event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())), topics: vec![], }, EventRecord { @@ -734,9 +796,6 @@ const CODE_SET_RENT: &str = r#" ) "#; -// Use test_hash_and_code test to get the actual hash if the code changed. -const HASH_SET_RENT: [u8; 32] = hex!("69aedfb4f6c1c398e97f8a5204de0f95ad5e7dc3540960beab11a86c569fbfcf"); - /// Input data for each call in set_rent code mod call { pub fn set_storage_4_byte() -> Vec { vec![] } @@ -745,7 +804,7 @@ mod call { pub fn null() -> Vec { vec![0, 0, 0] } } -/// Test correspondance of set_rent code and its hash. +/// Test correspondence of set_rent code and its hash. /// Also test that encoded extrinsic in code correspond to the correct transfer #[test] fn test_set_rent_code_and_hash() { @@ -754,7 +813,7 @@ fn test_set_rent_code_and_hash() { let encoded = Encode::encode(&Call::Balances(balances::Call::transfer(CHARLIE, 50))); assert_eq!(&encoded[..], &hex!("00000300000000000000C8")[..]); - let wasm = wabt::wat2wasm(CODE_SET_RENT).unwrap(); + let (wasm, code_hash) = compile_module::(CODE_SET_RENT).unwrap(); with_externalities( &mut ExtBuilder::default().existential_deposit(50).build(), @@ -772,7 +831,7 @@ fn test_set_rent_code_and_hash() { }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::CodeStored(HASH_SET_RENT.into())), + event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())), topics: vec![], }, ]); @@ -782,7 +841,7 @@ fn test_set_rent_code_and_hash() { #[test] fn storage_size() { - let wasm = wabt::wat2wasm(CODE_SET_RENT).unwrap(); + let (wasm, code_hash) = compile_module::(CODE_SET_RENT).unwrap(); // Storage size with_externalities( @@ -794,26 +853,26 @@ fn storage_size() { assert_ok!(Contract::create( Origin::signed(ALICE), 30_000, - 100_000, HASH_SET_RENT.into(), + 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, Contract::storage_size_offset() + 4); + 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, Contract::storage_size_offset() + 4 + 4); + 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, Contract::storage_size_offset() + 4); + assert_eq!(bob_contract.storage_size, ::StorageSizeOffset::get() + 4); } ); } #[test] fn deduct_blocks() { - let wasm = wabt::wat2wasm(CODE_SET_RENT).unwrap(); + let (wasm, code_hash) = compile_module::(CODE_SET_RENT).unwrap(); with_externalities( &mut ExtBuilder::default().existential_deposit(50).build(), @@ -824,7 +883,7 @@ fn deduct_blocks() { assert_ok!(Contract::create( Origin::signed(ALICE), 30_000, - 100_000, HASH_SET_RENT.into(), + 100_000, code_hash.into(), ::Balance::from(1_000u32).encode() // rent allowance )); @@ -875,7 +934,11 @@ fn deduct_blocks() { #[test] fn call_contract_removals() { - removals(|| Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()).is_ok()); + removals(|| { + // Call on already-removed account might fail, and this is fine. + Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()); + true + }); } #[test] @@ -906,7 +969,7 @@ fn claim_surcharge_malus() { /// Claim surcharge with the given trigger_call at the given blocks. /// if removes is true then assert that the contract is a tombstonedead fn claim_surcharge(blocks: u64, trigger_call: impl Fn() -> bool, removes: bool) { - let wasm = wabt::wat2wasm(CODE_SET_RENT).unwrap(); + let (wasm, code_hash) = compile_module::(CODE_SET_RENT).unwrap(); with_externalities( &mut ExtBuilder::default().existential_deposit(50).build(), @@ -917,7 +980,7 @@ fn claim_surcharge(blocks: u64, trigger_call: impl Fn() -> bool, removes: bool) assert_ok!(Contract::create( Origin::signed(ALICE), 100, - 100_000, HASH_SET_RENT.into(), + 100_000, code_hash.into(), ::Balance::from(1_000u32).encode() // rent allowance )); @@ -941,7 +1004,7 @@ fn claim_surcharge(blocks: u64, trigger_call: impl Fn() -> bool, removes: bool) /// * if allowance is exceeded /// * if balance is reached and balance < subsistence threshold fn removals(trigger_call: impl Fn() -> bool) { - let wasm = wabt::wat2wasm(CODE_SET_RENT).unwrap(); + let (wasm, code_hash) = compile_module::(CODE_SET_RENT).unwrap(); // Balance reached and superior to subsistence threshold with_externalities( @@ -953,13 +1016,16 @@ fn removals(trigger_call: impl Fn() -> bool) { assert_ok!(Contract::create( Origin::signed(ALICE), 100, - 100_000, HASH_SET_RENT.into(), + 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()); @@ -967,6 +1033,7 @@ fn removals(trigger_call: impl Fn() -> bool) { // Trigger rent through call assert!(trigger_call()); assert!(ContractInfoOf::::get(BOB).unwrap().get_tombstone().is_some()); + assert_eq!(Balances::free_balance(&BOB), subsistence_threshold); // Advance blocks System::initialize(&20, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); @@ -974,6 +1041,7 @@ fn removals(trigger_call: impl Fn() -> bool) { // Trigger rent must have no effect assert!(trigger_call()); assert!(ContractInfoOf::::get(BOB).unwrap().get_tombstone().is_some()); + assert_eq!(Balances::free_balance(&BOB), subsistence_threshold); } ); @@ -987,13 +1055,14 @@ fn removals(trigger_call: impl Fn() -> bool) { assert_ok!(Contract::create( Origin::signed(ALICE), 1_000, - 100_000, HASH_SET_RENT.into(), + 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()); @@ -1001,6 +1070,8 @@ fn removals(trigger_call: impl Fn() -> bool) { // 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()); @@ -1008,6 +1079,7 @@ fn removals(trigger_call: impl Fn() -> bool) { // Trigger rent must have no effect assert!(trigger_call()); assert!(ContractInfoOf::::get(BOB).unwrap().get_tombstone().is_some()); + assert_eq!(Balances::free_balance(&BOB), 900); } ); @@ -1021,17 +1093,19 @@ fn removals(trigger_call: impl Fn() -> bool) { assert_ok!(Contract::create( Origin::signed(ALICE), 50+Balances::minimum_balance(), - 100_000, HASH_SET_RENT.into(), + 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()); @@ -1039,6 +1113,7 @@ fn removals(trigger_call: impl Fn() -> bool) { // Trigger rent through call assert!(trigger_call()); assert!(ContractInfoOf::::get(BOB).is_none()); + assert_eq!(Balances::free_balance(&BOB), Balances::minimum_balance()); // Advance blocks System::initialize(&20, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); @@ -1046,10 +1121,50 @@ fn removals(trigger_call: impl Fn() -> bool) { // Trigger rent must have no effect assert!(trigger_call()); assert!(ContractInfoOf::::get(BOB).is_none()); + assert_eq!(Balances::free_balance(&BOB), Balances::minimum_balance()); } ); } +#[test] +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::create( + 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#" (module (import "env" "ext_rent_allowance" (func $ext_rent_allowance)) @@ -1099,12 +1214,10 @@ const CODE_CHECK_DEFAULT_RENT_ALLOWANCE: &str = r#" ) ) "#; -const HASH_CHECK_DEFAULT_RENT_ALLOWANCE: [u8; 32] = - hex!("4f9ec2b94eea522cfff10b77ef4056c631045c00978a457d283950521ecf07b6"); #[test] fn default_rent_allowance_on_create() { - let wasm = wabt::wat2wasm(CODE_CHECK_DEFAULT_RENT_ALLOWANCE).unwrap(); + let (wasm, code_hash) = compile_module::(CODE_CHECK_DEFAULT_RENT_ALLOWANCE).unwrap(); with_externalities( &mut ExtBuilder::default().existential_deposit(50).build(), @@ -1116,7 +1229,7 @@ fn default_rent_allowance_on_create() { Origin::signed(ALICE), 30_000, 100_000, - HASH_CHECK_DEFAULT_RENT_ALLOWANCE.into(), + code_hash.into(), vec![], )); @@ -1187,7 +1300,6 @@ const CODE_RESTORATION: &str = r#" ) ) "#; -const HASH_RESTORATION: [u8; 32] = hex!("02988182efba70fe605031f5c55bfa59e47f72c0a4707f22b6b74fffbf7803dc"); #[test] fn restorations_dirty_storage_and_different_storage() { @@ -1210,6 +1322,10 @@ fn restoration_success() { } fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: bool) { + let (restoration_wasm, restoration_code_hash) = + compile_module::(CODE_RESTORATION).unwrap(); + let (set_rent_wasm, set_rent_code_hash) = compile_module::(CODE_SET_RENT).unwrap(); + let acl_key = { let mut s = [0u8; 32]; s[0] = 1; @@ -1220,7 +1336,7 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: // let's rewrite so as we use this module controlled call or we serialize it in runtime. let encoded = hex::encode(Encode::encode(&Call::Contract(super::Call::restore_to( BOB, - HASH_SET_RENT.into(), + set_rent_code_hash.into(), ::Balance::from(50u32), vec![acl_key, acl_key], )))); @@ -1229,7 +1345,7 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: // same copy of this (modulo hex notation differences) in `CODE_RESTORATION`. // // When this assert is triggered make sure that you update the literals here and in - // `CODE_RESTORATION`. Hopefully, we switch to automatical injection of the code. + // `CODE_RESTORATION`. Hopefully, we switch to automatic injection of the code. const ENCODED_CALL_LITERAL: &str = "0105020000000000000069aedfb4f6c1c398e97f8a5204de0f95ad5e7dc3540960beab11a86c569fbfcf320000\ 0000000000080100000000000000000000000000000000000000000000000000000000000000010000000000000\ @@ -1245,8 +1361,6 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: "The size of the literal was changed and requires updating in `CODE_RESTORATION`", ); - let restoration_wasm = wabt::wat2wasm(CODE_RESTORATION).unwrap(); - let set_rent_wasm = wabt::wat2wasm(CODE_SET_RENT).unwrap(); with_externalities( &mut ExtBuilder::default().existential_deposit(50).build(), @@ -1265,23 +1379,23 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::CodeStored(HASH_RESTORATION.into())), + event: MetaEvent::contract(RawEvent::CodeStored(restoration_code_hash.into())), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::CodeStored(HASH_SET_RENT.into())), + event: MetaEvent::contract(RawEvent::CodeStored(set_rent_code_hash.into())), topics: vec![], }, ]); - // Create an account with address `BOB` with code `HASH_SET_RENT`. + // Create an account with address `BOB` with code `CODE_SET_RENT`. // The input parameter sets the rent allowance to 0. assert_ok!(Contract::create( Origin::signed(ALICE), 30_000, 100_000, - HASH_SET_RENT.into(), + set_rent_code_hash.into(), ::Balance::from(0u32).encode() )); @@ -1303,7 +1417,10 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: // Call `BOB`, which makes it pay rent. Since the rent allowance is set to 0 // we expect that it will get removed leaving tombstone. - assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null())); + 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`. @@ -1315,7 +1432,7 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: Origin::signed(CHARLIE), 30_000, 100_000, - HASH_RESTORATION.into(), + restoration_code_hash.into(), ::Balance::from(0u32).encode() )); @@ -1361,3 +1478,113 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: } ); } + +const CODE_STORAGE_SIZE: &str = r#" +(module + (import "env" "ext_get_storage" (func $ext_get_storage (param i32) (result i32))) + (import "env" "ext_set_storage" (func $ext_set_storage (param i32 i32 i32 i32))) + (import "env" "ext_scratch_size" (func $ext_scratch_size (result i32))) + (import "env" "ext_scratch_copy" (func $ext_scratch_copy (param i32 i32 i32))) + (import "env" "memory" (memory 16 16)) + + (func $assert (param i32) + (block $ok + (br_if $ok + (get_local 0) + ) + (unreachable) + ) + ) + + (func (export "call") + ;; assert $ext_scratch_size == 8 + (call $assert + (i32.eq + (call $ext_scratch_size) + (i32.const 4) + ) + ) + + ;; copy contents of the scratch buffer into the contract's memory. + (call $ext_scratch_copy + (i32.const 32) ;; Pointer in memory to the place where to copy. + (i32.const 0) ;; Offset from the start of the scratch buffer. + (i32.const 4) ;; Count of bytes to copy. + ) + + ;; place a garbage value in storage, the size of which is specified by the call input. + (call $ext_set_storage + (i32.const 0) ;; Pointer to storage key + (i32.const 1) ;; Value is not null + (i32.const 0) ;; Pointer to value + (i32.load (i32.const 32)) ;; Size of value + ) + + (call $assert + (i32.eq + (call $ext_get_storage + (i32.const 0) ;; Pointer to storage key + ) + (i32.const 0) + ) + ) + + (call $assert + (i32.eq + (call $ext_scratch_size) + (i32.load (i32.const 32)) + ) + ) + ) + + (func (export "deploy")) + + (data (i32.const 0) "\01") ;; Storage key (32 B) +) +"#; + +#[test] +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::create( + 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() + 1)), + ), + "during execution" + ); + } + ); +} diff --git a/srml/contracts/src/wasm/code_cache.rs b/srml/contracts/src/wasm/code_cache.rs index 30c02ef94200f4b89822c4787efdc0587db315aa..140878f28b8c7018a09722da9b5b0a04ea85daaf 100644 --- a/srml/contracts/src/wasm/code_cache.rs +++ b/srml/contracts/src/wasm/code_cache.rs @@ -26,11 +26,11 @@ //! this guarantees that every instrumented contract code in cache cannot have the version equal to the current one. //! Thus, before executing a contract it should be reinstrument with new schedule. -use crate::gas::{GasMeter, Token}; +use crate::gas::{Gas, GasMeter, Token}; use crate::wasm::{prepare, runtime::Env, PrefabWasmModule}; use crate::{CodeHash, CodeStorage, PristineCode, Schedule, Trait}; use rstd::prelude::*; -use runtime_primitives::traits::{CheckedMul, Hash, Bounded}; +use runtime_primitives::traits::{Hash, Bounded}; use srml_support::StorageMap; /// Gas metering token that used for charging storing code into the code storage. @@ -41,12 +41,12 @@ use srml_support::StorageMap; pub struct PutCodeToken(u32); impl Token for PutCodeToken { - type Metadata = Schedule; + type Metadata = Schedule; - fn calculate_amount(&self, metadata: &Schedule) -> T::Gas { + fn calculate_amount(&self, metadata: &Schedule) -> Gas { metadata .put_code_per_byte_cost - .checked_mul(&self.0.into()) + .checked_mul(self.0.into()) .unwrap_or_else(|| Bounded::max_value()) } } @@ -58,7 +58,7 @@ impl Token for PutCodeToken { pub fn save( original_code: Vec, gas_meter: &mut GasMeter, - schedule: &Schedule, + schedule: &Schedule, ) -> Result, &'static str> { // The first time instrumentation is on the user. However, consequent reinstrumentation // due to the schedule changes is on governance system. @@ -69,7 +69,7 @@ pub fn save( return Err("there is not enough gas for storing the code"); } - let prefab_module = prepare::prepare_contract::(&original_code, schedule)?; + let prefab_module = prepare::prepare_contract::(&original_code, schedule)?; let code_hash = T::Hashing::hash(&original_code); >::insert(code_hash, prefab_module); @@ -85,7 +85,7 @@ pub fn save( /// re-instrumentation and update the cache in the storage. pub fn load( code_hash: &CodeHash, - schedule: &Schedule, + schedule: &Schedule, ) -> Result { let mut prefab_module = >::get(code_hash).ok_or_else(|| "code is not found")?; @@ -97,7 +97,7 @@ pub fn load( // We need to re-instrument the code with the latest schedule here. let original_code = >::get(code_hash).ok_or_else(|| "pristine code is not found")?; - prefab_module = prepare::prepare_contract::(&original_code, schedule)?; + prefab_module = prepare::prepare_contract::(&original_code, schedule)?; >::insert(code_hash, prefab_module.clone()); } Ok(prefab_module) diff --git a/srml/contracts/src/wasm/env_def/macros.rs b/srml/contracts/src/wasm/env_def/macros.rs index 32d02f5abea76f9bd536377878efb257a26216dc..4f8bce99040007420c98367894a384872d9379c8 100644 --- a/srml/contracts/src/wasm/env_def/macros.rs +++ b/srml/contracts/src/wasm/env_def/macros.rs @@ -200,7 +200,7 @@ mod tests { use crate::wasm::tests::MockExt; use crate::wasm::Runtime; use crate::exec::Ext; - use crate::Trait; + use crate::gas::Gas; #[test] fn macro_unmarshall_then_body_then_marshall_value_or_trap() { @@ -256,7 +256,7 @@ mod tests { #[test] fn macro_define_func() { define_func!( ext_gas (_ctx, amount: u32) => { - let amount = ::Gas::from(amount); + let amount = Gas::from(amount); if !amount.is_zero() { Ok(()) } else { @@ -308,7 +308,7 @@ mod tests { define_env!(Env, , ext_gas( _ctx, amount: u32 ) => { - let amount = ::Gas::from(amount); + let amount = Gas::from(amount); if !amount.is_zero() { Ok(()) } else { diff --git a/srml/contracts/src/wasm/mod.rs b/srml/contracts/src/wasm/mod.rs index 28f71dc9dfea0193a80a897aa98771318f3ad2d5..3a6d3ad56624f755d09865dde9130ddd29a4c2d6 100644 --- a/srml/contracts/src/wasm/mod.rs +++ b/srml/contracts/src/wasm/mod.rs @@ -63,17 +63,17 @@ pub struct WasmExecutable { } /// Loader which fetches `WasmExecutable` from the code cache. -pub struct WasmLoader<'a, T: Trait> { - schedule: &'a Schedule, +pub struct WasmLoader<'a> { + schedule: &'a Schedule, } -impl<'a, T: Trait> WasmLoader<'a, T> { - pub fn new(schedule: &'a Schedule) -> Self { +impl<'a> WasmLoader<'a> { + pub fn new(schedule: &'a Schedule) -> Self { WasmLoader { schedule } } } -impl<'a, T: Trait> crate::exec::Loader for WasmLoader<'a, T> { +impl<'a, T: Trait> crate::exec::Loader for WasmLoader<'a> { type Executable = WasmExecutable; fn load_init(&self, code_hash: &CodeHash) -> Result { @@ -93,17 +93,17 @@ impl<'a, T: Trait> crate::exec::Loader for WasmLoader<'a, T> { } /// Implementation of `Vm` that takes `WasmExecutable` and executes it. -pub struct WasmVm<'a, T: Trait> { - schedule: &'a Schedule, +pub struct WasmVm<'a> { + schedule: &'a Schedule, } -impl<'a, T: Trait> WasmVm<'a, T> { - pub fn new(schedule: &'a Schedule) -> Self { +impl<'a> WasmVm<'a> { + pub fn new(schedule: &'a Schedule) -> Self { WasmVm { schedule } } } -impl<'a, T: Trait> crate::exec::Vm for WasmVm<'a, T> { +impl<'a, T: Trait> crate::exec::Vm for WasmVm<'a> { type Executable = WasmExecutable; fn execute>( @@ -175,7 +175,7 @@ mod tests { use std::collections::HashMap; use substrate_primitives::H256; use crate::exec::{CallReceipt, Ext, InstantiateReceipt, EmptyOutputBuf, StorageKey}; - use crate::gas::GasMeter; + use crate::gas::{Gas, GasMeter}; use crate::tests::{Test, Call}; use crate::wasm::prepare::prepare_contract; use crate::CodeHash; @@ -215,8 +215,11 @@ mod tests { fn get_storage(&self, key: &StorageKey) -> Option> { self.storage.get(key).cloned() } - fn set_storage(&mut self, key: StorageKey, value: Option>) { + fn set_storage(&mut self, key: StorageKey, value: Option>) + -> Result<(), &'static str> + { *self.storage.entry(key).or_insert(Vec::new()) = value.unwrap_or(Vec::new()); + Ok(()) } fn instantiate( &mut self, @@ -291,6 +294,10 @@ mod tests { fn rent_allowance(&self) -> u64 { self.rent_allowance } + + fn block_number(&self) -> u64 { 121 } + + fn max_value_size(&self) -> u32 { 16_384 } } fn execute( @@ -303,9 +310,9 @@ mod tests { use crate::exec::Vm; let wasm = wabt::wat2wasm(wat).unwrap(); - let schedule = crate::Schedule::::default(); + let schedule = crate::Schedule::default(); let prefab_module = - prepare_contract::(&wasm, &schedule).unwrap(); + prepare_contract::(&wasm, &schedule).unwrap(); let exec = WasmExecutable { // Use a "call" convention. @@ -906,14 +913,20 @@ mod tests { fn gas_left() { let mut mock_ext = MockExt::default(); let mut gas_meter = GasMeter::with_limit(50_000, 1312); + + let mut return_buf = Vec::new(); execute( CODE_GAS_LEFT, &[], - &mut Vec::new(), + &mut return_buf, &mut mock_ext, &mut gas_meter, ) .unwrap(); + + let gas_left = Gas::decode(&mut &return_buf[..]).unwrap(); + assert!(gas_left < 50_000, "gas_left must be less than initial"); + assert!(gas_left > gas_meter.gas_left(), "gas_left must be greater than final"); } const CODE_VALUE_TRANSFERRED: &str = r#" @@ -1336,4 +1349,70 @@ mod tests { Err("during execution"), ); } + + /// calls `ext_block_number`, loads the current block number from the scratch buffer and + /// compares it with the constant 121. + const CODE_BLOCK_NUMBER: &str = r#" +(module + (import "env" "ext_block_number" (func $ext_block_number)) + (import "env" "ext_scratch_size" (func $ext_scratch_size (result i32))) + (import "env" "ext_scratch_copy" (func $ext_scratch_copy (param i32 i32 i32))) + (import "env" "memory" (memory 1 1)) + + (func $assert (param i32) + (block $ok + (br_if $ok + (get_local 0) + ) + (unreachable) + ) + ) + + (func (export "call") + ;; This stores the block height in the scratch buffer + (call $ext_block_number) + + ;; assert $ext_scratch_size == 8 + (call $assert + (i32.eq + (call $ext_scratch_size) + (i32.const 8) + ) + ) + + ;; copy contents of the scratch buffer into the contract's memory. + (call $ext_scratch_copy + (i32.const 8) ;; Pointer in memory to the place where to copy. + (i32.const 0) ;; Offset from the start of the scratch buffer. + (i32.const 8) ;; Count of bytes to copy. + ) + + ;; assert that contents of the buffer is equal to the i64 value of 121. + (call $assert + (i64.eq + (i64.load + (i32.const 8) + ) + (i64.const 121) + ) + ) + ) + + (func (export "deploy")) +) +"#; + + #[test] + fn block_number() { + let mut mock_ext = MockExt::default(); + execute( + CODE_BLOCK_NUMBER, + &[], + &mut Vec::new(), + &mut mock_ext, + &mut GasMeter::with_limit(50_000, 1), + ) + .unwrap(); + } + } diff --git a/srml/contracts/src/wasm/prepare.rs b/srml/contracts/src/wasm/prepare.rs index d780cc1a28328ac6c467fefb7f2094d8988080c0..c135c45d3a8d0cab003d1dc93dbd5adc6fe1c492 100644 --- a/srml/contracts/src/wasm/prepare.rs +++ b/srml/contracts/src/wasm/prepare.rs @@ -20,33 +20,29 @@ use crate::wasm::env_def::ImportSatisfyCheck; use crate::wasm::PrefabWasmModule; -use crate::{Schedule, Trait}; +use crate::Schedule; use parity_wasm::elements::{self, Internal, External, MemoryType, Type}; use pwasm_utils; use pwasm_utils::rules; use rstd::prelude::*; -use runtime_primitives::traits::{UniqueSaturatedInto, SaturatedConversion}; +use runtime_primitives::traits::{SaturatedConversion}; -struct ContractModule<'a, Gas: 'a> { +struct ContractModule<'a> { /// A deserialized module. The module is valid (this is Guaranteed by `new` method). - /// - /// An `Option` is used here for loaning (`take()`-ing) the module. - /// Invariant: Can't be `None` (i.e. on enter and on exit from the function - /// the value *must* be `Some`). - module: Option, - schedule: &'a Schedule, + module: elements::Module, + schedule: &'a Schedule, } -impl<'a, Gas: 'a + From + UniqueSaturatedInto + Clone> ContractModule<'a, Gas> { +impl<'a> ContractModule<'a> { /// Creates a new instance of `ContractModule`. /// /// Returns `Err` if the `original_code` couldn't be decoded or /// if it contains an invalid module. fn new( original_code: &[u8], - schedule: &'a Schedule, - ) -> Result, &'static str> { + schedule: &'a Schedule, + ) -> Result { use wasmi_validation::{validate_module, PlainValidator}; let module = @@ -58,7 +54,7 @@ impl<'a, Gas: 'a + From + UniqueSaturatedInto + Clone> ContractModule< // Return a `ContractModule` instance with // __valid__ module. Ok(ContractModule { - module: Some(module), + module, schedule, }) } @@ -69,11 +65,7 @@ impl<'a, Gas: 'a + From + UniqueSaturatedInto + Clone> ContractModule< /// Memory section contains declarations of internal linear memories, so if we find one /// we reject such a module. fn ensure_no_internal_memory(&self) -> Result<(), &'static str> { - let module = self - .module - .as_ref() - .expect("On entry to the function `module` can't be None; qed"); - if module + if self.module .memory_section() .map_or(false, |ms| ms.entries().len() > 0) { @@ -82,7 +74,26 @@ impl<'a, Gas: 'a + From + UniqueSaturatedInto + Clone> ContractModule< Ok(()) } - fn inject_gas_metering(&mut self) -> Result<(), &'static str> { + /// Ensures that tables declared in the module are not too big. + fn ensure_table_size_limit(&self, limit: u32) -> Result<(), &'static str> { + if let Some(table_section) = self.module.table_section() { + // In Wasm MVP spec, there may be at most one table declared. Double check this + // explicitly just in case the Wasm version changes. + if table_section.entries().len() > 1 { + return Err("multiple tables declared"); + } + if let Some(table_type) = table_section.entries().first() { + // Check the table's initial size as there is no instruction or environment function + // capable of growing the table. + if table_type.limits().initial() > limit { + return Err("table exceeds maximum size allowed") + } + } + } + Ok(()) + } + + fn inject_gas_metering(self) -> Result { let gas_rules = rules::Set::new( self.schedule.regular_op_cost.clone().saturated_into(), @@ -91,30 +102,22 @@ impl<'a, Gas: 'a + From + UniqueSaturatedInto + Clone> ContractModule< .with_grow_cost(self.schedule.grow_mem_cost.clone().saturated_into()) .with_forbidden_floats(); - let module = self - .module - .take() - .expect("On entry to the function `module` can't be `None`; qed"); - - let contract_module = pwasm_utils::inject_gas_counter(module, &gas_rules) + let contract_module = pwasm_utils::inject_gas_counter(self.module, &gas_rules) .map_err(|_| "gas instrumentation failed")?; - - self.module = Some(contract_module); - Ok(()) + Ok(ContractModule { + module: contract_module, + schedule: self.schedule, + }) } - fn inject_stack_height_metering(&mut self) -> Result<(), &'static str> { - let module = self - .module - .take() - .expect("On entry to the function `module` can't be `None`; qed"); - + fn inject_stack_height_metering(self) -> Result { let contract_module = - pwasm_utils::stack_height::inject_limiter(module, self.schedule.max_stack_height) + pwasm_utils::stack_height::inject_limiter(self.module, self.schedule.max_stack_height) .map_err(|_| "stack height instrumentation failed")?; - - self.module = Some(contract_module); - Ok(()) + Ok(ContractModule { + module: contract_module, + schedule: self.schedule, + }) } /// Check that the module has required exported functions. For now @@ -128,10 +131,7 @@ impl<'a, Gas: 'a + From + UniqueSaturatedInto + Clone> ContractModule< let mut deploy_found = false; let mut call_found = false; - let module = self - .module - .as_ref() - .expect("On entry to the function `module` can't be `None`; qed"); + let module = &self.module; let types = module.type_section().map(|ts| ts.types()).unwrap_or(&[]); let export_entries = module @@ -213,10 +213,7 @@ impl<'a, Gas: 'a + From + UniqueSaturatedInto + Clone> ContractModule< /// their signatures. /// - if there is a memory import, returns it's descriptor fn scan_imports(&self) -> Result, &'static str> { - let module = self - .module - .as_ref() - .expect("On entry to the function `module` can't be `None`; qed"); + let module = &self.module; let types = module.type_section().map(|ts| ts.types()).unwrap_or(&[]); let import_entries = module @@ -269,13 +266,9 @@ impl<'a, Gas: 'a + From + UniqueSaturatedInto + Clone> ContractModule< Ok(imported_mem_type) } - fn into_wasm_code(mut self) -> Result, &'static str> { - elements::serialize( - self.module - .take() - .expect("On entry to the function `module` can't be `None`; qed"), - ) - .map_err(|_| "error serializing instrumented module") + fn into_wasm_code(self) -> Result, &'static str> { + elements::serialize(self.module) + .map_err(|_| "error serializing instrumented module") } } @@ -290,13 +283,14 @@ impl<'a, Gas: 'a + From + UniqueSaturatedInto + Clone> ContractModule< /// - all imported functions from the external environment matches defined by `env` module, /// /// The preprocessing includes injecting code for gas metering and metering the height of stack. -pub fn prepare_contract( +pub fn prepare_contract( original_code: &[u8], - schedule: &Schedule, + schedule: &Schedule, ) -> Result { let mut contract_module = ContractModule::new(original_code, schedule)?; contract_module.scan_exports()?; contract_module.ensure_no_internal_memory()?; + contract_module.ensure_table_size_limit(schedule.max_table_size)?; struct MemoryDefinition { initial: u32, @@ -332,8 +326,9 @@ pub fn prepare_contract( } }; - contract_module.inject_gas_metering()?; - contract_module.inject_stack_height_metering()?; + contract_module = contract_module + .inject_gas_metering()? + .inject_stack_height_metering()?; Ok(PrefabWasmModule { schedule_version: schedule.version, @@ -347,7 +342,6 @@ pub fn prepare_contract( #[cfg(test)] mod tests { use super::*; - use crate::tests::Test; use crate::exec::Ext; use std::fmt; use wabt; @@ -377,8 +371,8 @@ mod tests { #[test] fn $name() { let wasm = wabt::Wat2Wasm::new().validate(false).convert($wat).unwrap(); - let schedule = Schedule::::default(); - let r = prepare_contract::(wasm.as_ref(), &schedule); + let schedule = Schedule::default(); + let r = prepare_contract::(wasm.as_ref(), &schedule); assert_matches!(r, $($expected)*); } }; @@ -406,7 +400,7 @@ mod tests { // Tests below assumes that maximum page number is configured to a certain number. #[test] fn assume_memory_size() { - assert_eq!(Schedule::::default().max_memory_pages, 16); + assert_eq!(Schedule::default().max_memory_pages, 16); } prepare_test!(memory_with_one_page, @@ -529,6 +523,49 @@ mod tests { ); } + mod tables { + use super::*; + + // Tests below assumes that maximum table size is configured to a certain number. + #[test] + fn assume_table_size() { + assert_eq!(Schedule::default().max_table_size, 16384); + } + + prepare_test!(no_tables, + r#" + (module + (func (export "call")) + (func (export "deploy")) + ) + "#, + Ok(_) + ); + + prepare_test!(table_valid_size, + r#" + (module + (table 10000 funcref) + + (func (export "call")) + (func (export "deploy")) + ) + "#, + Ok(_) + ); + + prepare_test!(table_too_big, + r#" + (module + (table 20000 funcref) + + (func (export "call")) + (func (export "deploy")) + )"#, + Err("table exceeds maximum size allowed") + ); + } + mod imports { use super::*; @@ -620,9 +657,9 @@ mod tests { ) "# ).unwrap(); - let mut schedule = Schedule::::default(); + let mut schedule = Schedule::default(); schedule.enable_println = true; - let r = prepare_contract::(wasm.as_ref(), &schedule); + let r = prepare_contract::(wasm.as_ref(), &schedule); assert_matches!(r, Ok(_)); } } diff --git a/srml/contracts/src/wasm/runtime.rs b/srml/contracts/src/wasm/runtime.rs index 8f7c10b4071c5b5d75dfe6e2808e3889f29efbb3..92d9b98acf2cd86877d5621269c686053a991151 100644 --- a/srml/contracts/src/wasm/runtime.rs +++ b/srml/contracts/src/wasm/runtime.rs @@ -21,13 +21,13 @@ use crate::exec::{ Ext, VmExecResult, OutputBuf, EmptyOutputBuf, CallReceipt, InstantiateReceipt, StorageKey, TopicOf, }; -use crate::gas::{GasMeter, Token, GasMeterResult, approx_gas_for_balance}; +use crate::gas::{Gas, GasMeter, Token, GasMeterResult, approx_gas_for_balance}; use sandbox; use system; use rstd::prelude::*; use rstd::mem; use parity_codec::{Decode, Encode}; -use runtime_primitives::traits::{CheckedMul, CheckedAdd, Bounded, SaturatedConversion}; +use runtime_primitives::traits::{Bounded, SaturatedConversion}; /// Enumerates all possible *special* trap conditions. /// @@ -45,7 +45,7 @@ pub(crate) struct Runtime<'a, E: Ext + 'a> { // we wrap output buffer to make it possible to take the buffer out. empty_output_buf: Option, scratch_buf: Vec, - schedule: &'a Schedule<::Gas>, + schedule: &'a Schedule, memory: sandbox::Memory, gas_meter: &'a mut GasMeter, special_trap: Option, @@ -55,7 +55,7 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { ext: &'a mut E, input_data: Vec, empty_output_buf: EmptyOutputBuf, - schedule: &'a Schedule<::Gas>, + schedule: &'a Schedule, memory: sandbox::Memory, gas_meter: &'a mut GasMeter, ) -> Self { @@ -97,7 +97,7 @@ pub(crate) fn to_execution_result( #[cfg_attr(test, derive(Debug, PartialEq, Eq))] #[derive(Copy, Clone)] -pub enum RuntimeToken { +pub enum RuntimeToken { /// Explicit call to the `gas` function. Charge the gas meter /// with the value provided. Explicit(u32), @@ -115,39 +115,39 @@ pub enum RuntimeToken { DepositEvent(u32, u32), } -impl Token for RuntimeToken { - type Metadata = Schedule; +impl Token for RuntimeToken { + type Metadata = Schedule; - fn calculate_amount(&self, metadata: &Schedule) -> T::Gas { + fn calculate_amount(&self, metadata: &Schedule) -> Gas { use self::RuntimeToken::*; let value = match *self { Explicit(amount) => Some(amount.into()), ReadMemory(byte_count) => metadata .sandbox_data_read_cost - .checked_mul(&byte_count.into()), + .checked_mul(byte_count.into()), WriteMemory(byte_count) => metadata .sandbox_data_write_cost - .checked_mul(&byte_count.into()), + .checked_mul(byte_count.into()), ReturnData(byte_count) => metadata .return_data_per_byte_cost - .checked_mul(&byte_count.into()), + .checked_mul(byte_count.into()), DepositEvent(topic_count, data_byte_count) => { let data_cost = metadata .event_data_per_byte_cost - .checked_mul(&data_byte_count.into()); + .checked_mul(data_byte_count.into()); let topics_cost = metadata .event_per_topic_cost - .checked_mul(&topic_count.into()); + .checked_mul(topic_count.into()); data_cost .and_then(|data_cost| { topics_cost.and_then(|topics_cost| { - data_cost.checked_add(&topics_cost) + data_cost.checked_add(topics_cost) }) }) .and_then(|data_and_topics_cost| - data_and_topics_cost.checked_add(&metadata.event_base_cost) + data_and_topics_cost.checked_add(metadata.event_base_cost) ) }, ComputedDispatchFee(gas) => Some(gas), @@ -221,7 +221,7 @@ fn read_sandbox_memory_into_buf( /// - out of gas /// - designated area is not within the bounds of the sandbox memory. fn write_sandbox_memory( - schedule: &Schedule, + schedule: &Schedule, gas_meter: &mut GasMeter, memory: &sandbox::Memory, ptr: u32, @@ -254,6 +254,7 @@ define_env!(Env, , }, // Change the value at the given key in the storage or remove the entry. + // The value length must not exceed the maximum defined by the Contracts module parameters. // // - key_ptr: pointer into the linear // memory where the location of the requested value is placed. @@ -263,6 +264,9 @@ define_env!(Env, , // where the value to set is placed. If `value_non_null` is set to 0, then this parameter is ignored. // - value_len: the length of the value. If `value_non_null` is set to 0, then this parameter is ignored. ext_set_storage(ctx, key_ptr: u32, value_non_null: u32, value_ptr: u32, value_len: u32) => { + if value_non_null != 0 && ctx.ext.max_value_size() < value_len { + return Err(sandbox::HostError); + } let mut key: StorageKey = [0; 32]; read_sandbox_memory_into_buf(ctx, key_ptr, &mut key)?; let value = @@ -271,12 +275,12 @@ define_env!(Env, , } else { None }; - ctx.ext.set_storage(key, value); + ctx.ext.set_storage(key, value).map_err(|_| sandbox::HostError)?; Ok(()) }, - // Retrieve the value at the given location from the strorage and return 0. + // Retrieve the value at the given location from the storage and return 0. // If there is no entry at the given location then this function will return 1 and // clear the scratch buffer. // @@ -509,7 +513,7 @@ define_env!(Env, , // Stores the amount of gas left into the scratch buffer. // - // The data is encoded as T::Balance. The current contents of the scratch buffer are overwritten. + // The data is encoded as Gas. The current contents of the scratch buffer are overwritten. ext_gas_left(ctx) => { ctx.scratch_buf.clear(); ctx.gas_meter.gas_left().encode_to(&mut ctx.scratch_buf); @@ -573,7 +577,7 @@ define_env!(Env, , // Charge gas for dispatching this call. let fee = { let balance_fee = <::T as Trait>::ComputeDispatchFee::compute_dispatch_fee(&call); - approx_gas_for_balance::<::T>(ctx.gas_meter.gas_price(), balance_fee) + approx_gas_for_balance(ctx.gas_meter.gas_price(), balance_fee) }; charge_gas(&mut ctx.gas_meter, ctx.schedule, RuntimeToken::ComputedDispatchFee(fee))?; @@ -701,6 +705,13 @@ define_env!(Env, , } Ok(()) }, + + // Stores the current block number of the current contract into the scratch buffer. + ext_block_number(ctx) => { + ctx.scratch_buf.clear(); + ctx.ext.block_number().encode_to(&mut ctx.scratch_buf); + Ok(()) + }, ); /// Finds duplicates in a given vector. diff --git a/srml/council/src/lib.rs b/srml/council/src/lib.rs index 681bc731be895ee3855a4b910dee4ebe741bff99..0ebf50ce165088f59165a1627bd94984f27ec34d 100644 --- a/srml/council/src/lib.rs +++ b/srml/council/src/lib.rs @@ -17,6 +17,7 @@ //! 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; @@ -39,11 +40,12 @@ mod tests { pub use super::*; pub use runtime_io::with_externalities; use srml_support::{impl_outer_origin, impl_outer_event, impl_outer_dispatch, parameter_types}; + use srml_support::traits::Get; pub use substrate_primitives::{H256, Blake2Hasher, u32_trait::{_1, _2, _3, _4}}; - pub use primitives::{ - BuildStorage, traits::{BlakeTwo256, IdentityLookup}, testing::{Digest, DigestItem, Header} - }; + pub use primitives::traits::{BlakeTwo256, IdentityLookup}; + pub use primitives::testing::{Digest, DigestItem, Header}; pub use {seats, motions}; + use std::cell::RefCell; impl_outer_origin! { pub enum Origin for Test { @@ -64,9 +66,41 @@ mod tests { } } + 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; + } impl system::Trait for Test { type Origin = Origin; type Index = u64; @@ -76,16 +110,32 @@ mod tests { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; + type WeightMultiplierUpdate = (); type Event = Event; + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; + } + 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 OnFreeBalanceZero = (); 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; } parameter_types! { pub const LaunchPeriod: u64 = 1; @@ -110,6 +160,12 @@ mod tests { 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 = (); @@ -117,6 +173,14 @@ mod tests { 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; @@ -171,11 +235,16 @@ mod tests { 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 { - let mut t = system::GenesisConfig::::default().build_storage().unwrap().0; - t.extend(balances::GenesisConfig::{ - transaction_base_fee: 0, - transaction_byte_fee: 0, + 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), @@ -184,30 +253,19 @@ mod tests { (5, 50 * self.balance_factor), (6, 60 * self.balance_factor) ], - existential_deposit: 0, - transfer_fee: 0, - creation_fee: 0, vesting: vec![], - }.build_storage().unwrap().0); - t.extend(seats::GenesisConfig:: { - candidacy_bond: 3, - voter_bond: self.voter_bond, - present_slash_per_voter: self.bad_presentation_punishment, - carry_count: 2, - inactive_grace_period: 1, + }.assimilate_storage(&mut t.0, &mut t.1).unwrap(); + seats::GenesisConfig:: { active_council: if self.with_council { vec![ (1, 10), (2, 10), (3, 10) ] } else { vec![] }, - approval_voting_period: 4, - presentation_duration: 2, desired_seats: 2, - decay_ratio: self.decay_ratio, - voting_fee: self.voting_fee, + presentation_duration: 2, term_duration: 5, - }.build_storage().unwrap().0); - runtime_io::TestExternalities::new(t) + }.assimilate_storage(&mut t.0, &mut t.1).unwrap(); + runtime_io::TestExternalities::new_with_children(t) } } diff --git a/srml/council/src/motions.rs b/srml/council/src/motions.rs deleted file mode 100644 index a53752d71f90525b67fb9e715509262277a02ca9..0000000000000000000000000000000000000000 --- a/srml/council/src/motions.rs +++ /dev/null @@ -1,583 +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 voting system. - -use rstd::{prelude::*, result}; -use substrate_primitives::u32_trait::Value as U32; -use primitives::traits::{Hash, EnsureOrigin}; -use srml_support::{ - dispatch::{Dispatchable, Parameter}, codec::{Encode, Decode}, - StorageValue, StorageMap, decl_module, decl_event, decl_storage, ensure -}; -use super::{Trait as CouncilTrait, Module as Council, OnMembersChanged}; -use system::{self, ensure_signed}; - -/// Simple index type for proposal counting. -pub type ProposalIndex = u32; -/// A number of council members. -/// -/// This also serves as a number of voting members, and since for motions, each council member may -/// vote exactly once, therefore also the number of votes for any given motion. -pub type MemberCount = u32; - -pub trait Trait: CouncilTrait { - /// The outer origin type. - type Origin: From>; - - /// The outer call dispatch type. - type Proposal: Parameter + Dispatchable::Origin>; - - /// The outer event type. - type Event: From> + Into<::Event>; -} - -/// Origin for the council module. -#[derive(PartialEq, Eq, Clone)] -#[cfg_attr(feature = "std", derive(Debug))] -pub enum RawOrigin { - /// It has been condoned by a given number of council members from a given total. - Members(MemberCount, MemberCount), - /// It has been condoned by a single council member. - Member(AccountId), -} - -/// Origin for the council module. -pub type Origin = RawOrigin<::AccountId>; - -#[derive(PartialEq, Eq, Clone, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] -/// Info for keeping track of a motion being voted on. -pub struct Votes { - /// The proposal's unique index. - index: ProposalIndex, - /// The number of approval votes that are needed to pass the motion. - threshold: MemberCount, - /// The current set of voters that approved it. - ayes: Vec, - /// The current set of voters that rejected it. - nays: Vec, -} - -decl_storage! { - trait Store for Module as CouncilMotions { - /// The hashes of the active proposals. - pub Proposals get(proposals): Vec; - /// Actual proposal for a given hash, if it's current. - pub ProposalOf get(proposal_of): map T::Hash => Option<::Proposal>; - /// Votes on a given proposal, if it is ongoing. - pub Voting get(voting): map T::Hash => Option>; - /// Proposals so far. - pub ProposalCount get(proposal_count): u32; - } -} - -decl_event!( - pub enum Event where ::Hash, ::AccountId { - /// A motion (given hash) has been proposed (by given account) with a threshold (given - /// `MemberCount`). - Proposed(AccountId, ProposalIndex, Hash, MemberCount), - /// A motion (given hash) has been voted on by given account, leaving - /// a tally (yes votes and no votes given respectively as `MemberCount`). - Voted(AccountId, Hash, bool, MemberCount, MemberCount), - /// A motion was approved by the required threshold. - Approved(Hash), - /// A motion was not approved by the required threshold. - Disapproved(Hash), - /// A motion was executed; `bool` is true if returned without error. - Executed(Hash, bool), - /// A single councillor did some action; `bool` is true if returned without error. - MemberExecuted(Hash, bool), - } -); - -decl_module! { - pub struct Module for enum Call where origin: ::Origin { - fn deposit_event() = default; - - /// Dispatch a proposal from a councilor using the `Member` origin. - /// - /// Origin must be a council member. - fn execute(origin, proposal: Box<::Proposal>) { - let who = ensure_signed(origin)?; - ensure!(Self::is_councillor(&who), "proposer not on council"); - - let proposal_hash = T::Hashing::hash_of(&proposal); - let ok = proposal.dispatch(RawOrigin::Member(who).into()).is_ok(); - Self::deposit_event(RawEvent::MemberExecuted(proposal_hash, ok)); - } - - /// # - /// - Bounded storage reads and writes. - /// - Argument `threshold` has bearing on weight. - /// # - fn propose(origin, #[compact] threshold: MemberCount, proposal: Box<::Proposal>) { - let who = ensure_signed(origin)?; - - ensure!(Self::is_councillor(&who), "proposer not on council"); - - let proposal_hash = T::Hashing::hash_of(&proposal); - - ensure!(!>::exists(proposal_hash), "duplicate proposals not allowed"); - - if threshold < 2 { - let seats = >::active_council().len() as MemberCount; - let ok = proposal.dispatch(RawOrigin::Members(1, seats).into()).is_ok(); - Self::deposit_event(RawEvent::Executed(proposal_hash, ok)); - } else { - let index = Self::proposal_count(); - >::mutate(|i| *i += 1); - >::mutate(|proposals| proposals.push(proposal_hash)); - >::insert(proposal_hash, *proposal); - let votes = Votes { index, threshold, ayes: vec![who.clone()], nays: vec![] }; - >::insert(proposal_hash, votes); - - Self::deposit_event(RawEvent::Proposed(who, index, proposal_hash, threshold)); - } - } - - /// # - /// - Bounded storage read and writes. - /// - Will be slightly heavier if the proposal is approved / disapproved after the vote. - /// # - fn vote(origin, proposal: T::Hash, #[compact] index: ProposalIndex, approve: bool) { - let who = ensure_signed(origin)?; - - ensure!(Self::is_councillor(&who), "voter not on council"); - - let mut voting = Self::voting(&proposal).ok_or("proposal must exist")?; - ensure!(voting.index == index, "mismatched index"); - - let position_yes = voting.ayes.iter().position(|a| a == &who); - let position_no = voting.nays.iter().position(|a| a == &who); - - if approve { - if position_yes.is_none() { - voting.ayes.push(who.clone()); - } else { - return Err("duplicate vote ignored") - } - if let Some(pos) = position_no { - voting.nays.swap_remove(pos); - } - } else { - if position_no.is_none() { - voting.nays.push(who.clone()); - } else { - return Err("duplicate vote ignored") - } - if let Some(pos) = position_yes { - voting.ayes.swap_remove(pos); - } - } - - let yes_votes = voting.ayes.len() as MemberCount; - let no_votes = voting.nays.len() as MemberCount; - Self::deposit_event(RawEvent::Voted(who, proposal, approve, yes_votes, no_votes)); - - let seats = >::active_council().len() as MemberCount; - let approved = yes_votes >= voting.threshold; - let disapproved = seats.saturating_sub(no_votes) < voting.threshold; - if approved || disapproved { - if approved { - Self::deposit_event(RawEvent::Approved(proposal)); - - // execute motion, assuming it exists. - if let Some(p) = >::take(&proposal) { - let origin = RawOrigin::Members(voting.threshold, seats).into(); - let ok = p.dispatch(origin).is_ok(); - Self::deposit_event(RawEvent::Executed(proposal, ok)); - } - } else { - // disapproved - Self::deposit_event(RawEvent::Disapproved(proposal)); - } - - // remove vote - >::remove(&proposal); - >::mutate(|proposals| proposals.retain(|h| h != &proposal)); - } else { - // update voting - >::insert(&proposal, voting); - } - } - } -} - -impl Module { - pub fn is_councillor(who: &T::AccountId) -> bool { - >::active_council().iter() - .any(|&(ref a, _)| a == who) - } -} - -impl OnMembersChanged for Module { - fn on_members_changed(_new: &[T::AccountId], old: &[T::AccountId]) { - // remove accounts from all current voting in motions. - let mut old = old.to_vec(); - old.sort_unstable(); - for h in Self::proposals().into_iter() { - >::mutate(h, |v| - if let Some(mut votes) = v.take() { - votes.ayes = votes.ayes.into_iter() - .filter(|i| old.binary_search(i).is_err()) - .collect(); - votes.nays = votes.nays.into_iter() - .filter(|i| old.binary_search(i).is_err()) - .collect(); - *v = Some(votes); - } - ); - } - } -} - -/// Ensure that the origin `o` represents at least `n` council members. Returns `Ok` or an `Err` -/// otherwise. -pub fn ensure_council_members(o: OuterOrigin, n: MemberCount) - -> result::Result - where OuterOrigin: Into, OuterOrigin>> -{ - match o.into() { - Ok(RawOrigin::Members(x, _)) if x >= n => Ok(n), - _ => Err("bad origin: expected to be a threshold number of council members"), - } -} - -pub struct EnsureMember(::rstd::marker::PhantomData); -impl< - O: Into, O>> + From>, - AccountId -> EnsureOrigin for EnsureMember { - type Success = AccountId; - fn try_origin(o: O) -> Result { - o.into().and_then(|o| match o { - RawOrigin::Member(id) => Ok(id), - r => Err(O::from(r)), - }) - } -} - -pub struct EnsureMembers(::rstd::marker::PhantomData<(N, AccountId)>); -impl< - O: Into, O>> + From>, - N: U32, - AccountId, -> EnsureOrigin for EnsureMembers { - type Success = (MemberCount, MemberCount); - fn try_origin(o: O) -> Result { - o.into().and_then(|o| match o { - RawOrigin::Members(n, m) if n >= N::VALUE => Ok((n, m)), - r => Err(O::from(r)), - }) - } -} - -pub struct EnsureProportionMoreThan( - ::rstd::marker::PhantomData<(N, D, AccountId)> -); -impl< - O: Into, O>> + From>, - N: U32, - D: U32, - AccountId, -> EnsureOrigin for EnsureProportionMoreThan { - type Success = (); - fn try_origin(o: O) -> Result { - o.into().and_then(|o| match o { - RawOrigin::Members(n, m) if n * D::VALUE > N::VALUE * m => Ok(()), - r => Err(O::from(r)), - }) - } -} - -pub struct EnsureProportionAtLeast( - ::rstd::marker::PhantomData<(N, D, AccountId)> -); -impl< - O: Into, O>> + From>, - N: U32, - D: U32, - AccountId, -> EnsureOrigin for EnsureProportionAtLeast { - type Success = (); - fn try_origin(o: O) -> Result { - o.into().and_then(|o| match o { - RawOrigin::Members(n, m) if n * D::VALUE >= N::VALUE * m => Ok(()), - r => Err(O::from(r)), - }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use super::RawEvent; - use crate::tests::*; - use crate::tests::{Call, Origin, Event as OuterEvent}; - use primitives::traits::BlakeTwo256; - use srml_support::{Hashable, assert_ok, assert_noop}; - use system::{EventRecord, Phase}; - use hex_literal::hex; - - #[test] - fn motions_basic_environment_works() { - with_externalities(&mut ExtBuilder::default().with_council(true).build(), || { - System::set_block_number(1); - assert_eq!(Balances::free_balance(&42), 0); - assert_eq!(CouncilMotions::proposals(), Vec::::new()); - }); - } - - fn set_balance_proposal(value: u64) -> Call { - Call::Balances(balances::Call::set_balance(42, value.into(), 0)) - } - - #[test] - fn removal_of_old_voters_votes_works() { - with_externalities(&mut ExtBuilder::default().with_council(true).build(), || { - System::set_block_number(1); - let proposal = set_balance_proposal(42); - let hash = BlakeTwo256::hash_of(&proposal); - assert_ok!(CouncilMotions::propose(Origin::signed(1), 3, Box::new(proposal.clone()))); - assert_ok!(CouncilMotions::vote(Origin::signed(2), hash.clone(), 0, true)); - assert_eq!( - CouncilMotions::voting(&hash), - Some(Votes { index: 0, threshold: 3, ayes: vec![1, 2], nays: vec![] }) - ); - CouncilMotions::on_members_changed(&[], &[1]); - assert_eq!( - CouncilMotions::voting(&hash), - Some(Votes { index: 0, threshold: 3, ayes: vec![2], nays: vec![] }) - ); - - let proposal = set_balance_proposal(69); - let hash = BlakeTwo256::hash_of(&proposal); - assert_ok!(CouncilMotions::propose(Origin::signed(2), 2, Box::new(proposal.clone()))); - assert_ok!(CouncilMotions::vote(Origin::signed(3), hash.clone(), 1, false)); - assert_eq!( - CouncilMotions::voting(&hash), - Some(Votes { index: 1, threshold: 2, ayes: vec![2], nays: vec![3] }) - ); - CouncilMotions::on_members_changed(&[], &[3]); - assert_eq!( - CouncilMotions::voting(&hash), - Some(Votes { index: 1, threshold: 2, ayes: vec![2], nays: vec![] }) - ); - }); - } - - #[test] - fn propose_works() { - with_externalities(&mut ExtBuilder::default().with_council(true).build(), || { - System::set_block_number(1); - let proposal = set_balance_proposal(42); - let hash = proposal.blake2_256().into(); - assert_ok!(CouncilMotions::propose(Origin::signed(1), 3, Box::new(proposal.clone()))); - assert_eq!(CouncilMotions::proposals(), vec![hash]); - assert_eq!(CouncilMotions::proposal_of(&hash), Some(proposal)); - assert_eq!( - CouncilMotions::voting(&hash), - Some(Votes { index: 0, threshold: 3, ayes: vec![1], nays: vec![] }) - ); - - assert_eq!(System::events(), vec![ - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Proposed( - 1, - 0, - hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), - 3, - )), - topics: vec![], - } - ]); - }); - } - - #[test] - fn motions_ignoring_non_council_proposals_works() { - with_externalities(&mut ExtBuilder::default().with_council(true).build(), || { - System::set_block_number(1); - let proposal = set_balance_proposal(42); - assert_noop!( - CouncilMotions::propose(Origin::signed(42), 3, Box::new(proposal.clone())), - "proposer not on council" - ); - }); - } - - #[test] - fn motions_ignoring_non_council_votes_works() { - with_externalities(&mut ExtBuilder::default().with_council(true).build(), || { - System::set_block_number(1); - let proposal = set_balance_proposal(42); - let hash: H256 = proposal.blake2_256().into(); - assert_ok!(CouncilMotions::propose(Origin::signed(1), 3, Box::new(proposal.clone()))); - assert_noop!(CouncilMotions::vote(Origin::signed(42), hash.clone(), 0, true), "voter not on council"); - }); - } - - #[test] - fn motions_ignoring_bad_index_council_vote_works() { - with_externalities(&mut ExtBuilder::default().with_council(true).build(), || { - System::set_block_number(3); - let proposal = set_balance_proposal(42); - let hash: H256 = proposal.blake2_256().into(); - assert_ok!(CouncilMotions::propose(Origin::signed(1), 3, Box::new(proposal.clone()))); - assert_noop!(CouncilMotions::vote(Origin::signed(2), hash.clone(), 1, true), "mismatched index"); - }); - } - - #[test] - fn motions_revoting_works() { - with_externalities(&mut ExtBuilder::default().with_council(true).build(), || { - System::set_block_number(1); - let proposal = set_balance_proposal(42); - let hash: H256 = proposal.blake2_256().into(); - assert_ok!(CouncilMotions::propose(Origin::signed(1), 2, Box::new(proposal.clone()))); - assert_eq!( - CouncilMotions::voting(&hash), - Some(Votes { index: 0, threshold: 2, ayes: vec![1], nays: vec![] }) - ); - assert_noop!(CouncilMotions::vote(Origin::signed(1), hash.clone(), 0, true), "duplicate vote ignored"); - assert_ok!(CouncilMotions::vote(Origin::signed(1), hash.clone(), 0, false)); - assert_eq!( - CouncilMotions::voting(&hash), - Some(Votes { index: 0, threshold: 2, ayes: vec![], nays: vec![1] }) - ); - assert_noop!(CouncilMotions::vote(Origin::signed(1), hash.clone(), 0, false), "duplicate vote ignored"); - - assert_eq!(System::events(), vec![ - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Proposed( - 1, - 0, - hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), - 2, - )), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Voted( - 1, - hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), - false, - 0, - 1, - )), - topics: vec![], - } - ]); - }); - } - - #[test] - fn motions_disapproval_works() { - with_externalities(&mut ExtBuilder::default().with_council(true).build(), || { - System::set_block_number(1); - let proposal = set_balance_proposal(42); - let hash: H256 = proposal.blake2_256().into(); - assert_ok!(CouncilMotions::propose(Origin::signed(1), 3, Box::new(proposal.clone()))); - assert_ok!(CouncilMotions::vote(Origin::signed(2), hash.clone(), 0, false)); - - assert_eq!(System::events(), vec![ - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions( - RawEvent::Proposed( - 1, - 0, - hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), - 3, - )), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Voted( - 2, - hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), - false, - 1, - 1, - )), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Disapproved( - hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), - )), - topics: vec![], - } - ]); - }); - } - - #[test] - fn motions_approval_works() { - with_externalities(&mut ExtBuilder::default().with_council(true).build(), || { - System::set_block_number(1); - let proposal = set_balance_proposal(42); - let hash: H256 = proposal.blake2_256().into(); - assert_ok!(CouncilMotions::propose(Origin::signed(1), 2, Box::new(proposal.clone()))); - assert_ok!(CouncilMotions::vote(Origin::signed(2), hash.clone(), 0, true)); - - assert_eq!(System::events(), vec![ - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Proposed( - 1, - 0, - hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), - 2, - )), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Voted( - 2, - hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), - true, - 2, - 0, - )), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Approved( - hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), - )), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Executed( - hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), - false, - )), - topics: vec![], - } - ]); - }); - } -} diff --git a/srml/council/src/seats.rs b/srml/council/src/seats.rs deleted file mode 100644 index 84b6f388f29fc61cac7595dd11496ea6b0145eee..0000000000000000000000000000000000000000 --- a/srml/council/src/seats.rs +++ /dev/null @@ -1,2580 +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. - -use rstd::prelude::*; -use primitives::traits::{Zero, One, StaticLookup, Bounded, Saturating}; -use runtime_io::print; -use srml_support::{ - StorageValue, StorageMap, - dispatch::Result, decl_storage, decl_event, ensure, decl_module, - traits::{ - Currency, ReservableCurrency, OnUnbalanced, LockIdentifier, - LockableCurrency, WithdrawReasons, WithdrawReason, ExistenceRequirement - } -}; -use democracy; -use parity_codec::{Encode, Decode}; -use system::{self, ensure_signed}; -use super::OnMembersChanged; - -// no polynomial attacks: -// -// all unbonded public operations should be constant time. -// all other public operations must be linear time in terms of prior public operations and: -// - those "valid" ones that cost nothing be limited to a constant number per single protected operation -// - the rest costing the same order as the computational complexity -// all protected operations must complete in at most O(public operations) -// -// we assume "beneficial" transactions will have the same access as attack transactions. -// -// any storage requirements should be bonded by the same order as the volume. - -// public operations: -// - express approvals (you pay in a "voter" bond the first time you do this; O(1); one extra DB entry, one DB change) -// - remove active voter (you get your "voter" bond back; O(1); one fewer DB entry, one DB change) -// - remove inactive voter (either you or the target is removed; if the target, you get their "voter" bond back; O(1); one fewer DB entry, one DB change) -// - submit candidacy (you pay a "candidate" bond; O(1); one extra DB entry, two DB changes) -// - present winner/runner-up (you may pay a "presentation" bond of O(voters) if the presentation is invalid; O(voters) compute; ) -// protected operations: -// - remove candidacy (remove all votes for a candidate) (one fewer DB entry, two DB changes) - -// to avoid a potentially problematic case of not-enough approvals prior to voting causing a -// back-to-back votes that have no way of ending, then there's a forced grace period between votes. -// to keep the system as stateless as possible (making it a bit easier to reason about), we just -// restrict when votes can begin to blocks that lie on boundaries (`voting_period`). - -// for an approval vote of C councillors: - -// top K runners-up are maintained between votes. all others are discarded. -// - candidate removed & bond returned when elected. -// - candidate removed & bond burned when discarded. - -// at the point that the vote ends (), all voters' balances are snapshotted. - -// for B blocks following, there's a counting period whereby each of the candidates that believe -// they fall in the top K+C voted can present themselves. they get the total stake -// recorded (based on the snapshot); an ordered list is maintained (the leaderboard). Noone may -// present themselves that, if elected, would result in being included twice on the council -// (important since existing councillors will have their approval votes as it may be that they -// don't get removed), nor if existing presenters would mean they're not in the top K+C. - -// following B blocks, the top C candidates are elected and have their bond returned. the top C -// candidates and all other candidates beyond the top C+K are cleared. - -// vote-clearing happens lazily; for an approval to count, the most recent vote at the time of the -// voter's most recent vote must be no later than the most recent vote at the time that the -// candidate in the approval position was registered there. as candidates are removed from the -// register and others join in their place, this prevents an approval meant for an earlier candidate -// being used to elect a new candidate. - -// the candidate list increases as needed, but the contents (though not really the capacity) reduce -// after each vote as all but K entries are cleared. newly registering candidates must use cleared -// 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))] -pub struct VoterInfo { - /// Last VoteIndex in which this voter assigned (or initialized) approvals. - last_active: VoteIndex, - /// Last VoteIndex in which one of this voter's approvals won. - /// Note that `last_win = N` indicates a last win at index `N-1`, hence `last_win = 0` means no win ever. - last_win: VoteIndex, - /// The amount of stored weight as a result of not winning but changing approvals. - pot: Balance, - /// Current staked amount. A lock equal to this value always exists. - stake: Balance, -} - -/// Used to demonstrate the status of a particular index in the global voter list. -#[derive(PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(Debug))] -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. - Head, - /// Already occupied by another voter. Voting fee is applied. - Occupied, - /// Empty hole which should be filled. No fee will be applied. - Hole, -} - -const COUNCIL_SEATS_ID: LockIdentifier = *b"councils"; - -pub const VOTER_SET_SIZE: usize = 64; -pub const APPROVAL_SET_SIZE: usize = 8; - -type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; -type NegativeImbalanceOf = <::Currency as Currency<::AccountId>>::NegativeImbalance; - -type SetIndex = u32; -pub type VoteIndex = u32; - -// all three must be in sync. -type ApprovalFlag = u32; -pub const APPROVAL_FLAG_MASK: ApprovalFlag = 0x8000_0000; -pub const APPROVAL_FLAG_LEN: usize = 32; - -pub trait Trait: democracy::Trait { - type Event: From> + Into<::Event>; - - /// Handler for the unbalanced reduction when slashing a validator. - type BadPresentation: OnUnbalanced>; - - /// Handler for the unbalanced reduction when slashing an invalid reaping attempt. - type BadReaper: OnUnbalanced>; - - /// Handler for the unbalanced reduction when submitting a bad `voter_index`. - type BadVoterIndex: OnUnbalanced>; - - /// Handler for the unbalanced reduction when a candidate has lost (and is not a runner up) - type LoserCandidate: OnUnbalanced>; - /// What to do when the members change. - type OnMembersChanged: OnMembersChanged; -} - -decl_module! { - pub struct Module for enum Call where origin: T::Origin { - fn deposit_event() = default; - - /// Set candidate approvals. Approval slots stay valid as long as candidates in those slots - /// are registered. - /// - /// Locks the total balance of caller indefinitely. - /// Only [`retract_voter`] or [`reap_inactive_voter`] can unlock the balance. - /// - /// `hint` argument is interpreted differently based on: - /// - if `origin` is setting approvals for the first time: The index will be checked - /// for being a valid _hole_ in the voter list. - /// - if the hint is correctly pointing to a hole, no fee is deducted from `origin`. - /// - Otherwise, the call will succeed but the index is ignored and simply a push to the last chunk - /// with free space happens. If the new push causes a new chunk to be created, a fee indicated by - /// [`VotingFee`] is deducted. - /// - if `origin` is already a voter: the index __must__ be valid and point to the correct - /// position of the `origin` in the current voters list. - /// - /// Note that any trailing `false` votes in `votes` is ignored; In approval voting, not voting for a candidate - /// and voting false, are equal. - /// - /// # - /// - O(1). - /// - Two extra DB entries, one DB change. - /// - Argument `votes` is limited in length to number of candidates. - /// # - fn set_approvals(origin, votes: Vec, #[compact] index: VoteIndex, hint: SetIndex) -> Result { - let who = ensure_signed(origin)?; - Self::do_set_approvals(who, votes, index, hint) - } - - /// Set candidate approvals from a proxy. Approval slots stay valid as long as candidates in those slots - /// are registered. - /// - /// # - /// - Same as `set_approvals` with one additional storage read. - /// # - fn proxy_set_approvals(origin, votes: Vec, #[compact] index: VoteIndex, hint: SetIndex) -> Result { - let who = >::proxy(ensure_signed(origin)?).ok_or("not a proxy")?; - Self::do_set_approvals(who, votes, index, hint) - } - - /// Remove a voter. For it not to be a bond-consuming no-op, all approved candidate indices - /// must now be either unregistered or registered to a candidate that registered the slot after - /// the voter gave their last approval set. - /// - /// Both indices must be provided as explained in [`voter_at`] function. - /// - /// May be called by anyone. Returns the voter deposit to `signed`. - /// - /// # - /// - O(1). - /// - Two fewer DB entries, one DB change. - /// # - fn reap_inactive_voter( - origin, - #[compact] reporter_index: u32, - who: ::Source, - #[compact] who_index: u32, - #[compact] assumed_vote_index: VoteIndex - ) { - let reporter = ensure_signed(origin)?; - let who = T::Lookup::lookup(who)?; - - ensure!(!Self::presentation_active(), "cannot reap during presentation period"); - ensure!(Self::voter_info(&reporter).is_some(), "reporter must be a voter"); - - let info = Self::voter_info(&who).ok_or("target for inactivity cleanup must be active")?; - let last_active = info.last_active; - - ensure!(assumed_vote_index == Self::vote_index(), "vote index not current"); - ensure!( - assumed_vote_index > last_active+ Self::inactivity_grace_period(), - "cannot reap during grace period" - ); - - let reporter_index = reporter_index as usize; - let who_index = who_index as usize; - let assumed_reporter = Self::voter_at(reporter_index).ok_or("invalid reporter index")?; - let assumed_who = Self::voter_at(who_index).ok_or("invalid target index")?; - - ensure!(assumed_reporter == reporter, "bad reporter index"); - ensure!(assumed_who == who, "bad target index"); - - // will definitely kill one of reporter or who now. - - let valid = !Self::all_approvals_of(&who).iter() - .zip(Self::candidates().iter()) - .any(|(&appr, addr)| - appr && - *addr != T::AccountId::default() && - // defensive only: all items in candidates list are registered - Self::candidate_reg_info(addr).map_or(false, |x| x.0 <= last_active) - ); - - Self::remove_voter( - if valid { &who } else { &reporter }, - if valid { who_index } else { reporter_index } - ); - - T::Currency::remove_lock( - COUNCIL_SEATS_ID, - if valid { &who } else { &reporter } - ); - - if valid { - // This only fails if `reporter` doesn't exist, which it clearly must do since its the origin. - // Still, it's no more harmful to propagate any error at this point. - T::Currency::repatriate_reserved(&who, &reporter, Self::voting_bond())?; - Self::deposit_event(RawEvent::VoterReaped(who, reporter)); - } else { - let imbalance = T::Currency::slash_reserved(&reporter, Self::voting_bond()).0; - T::BadReaper::on_unbalanced(imbalance); - Self::deposit_event(RawEvent::BadReaperSlashed(reporter)); - } - } - - /// Remove a voter. All votes are cancelled and the voter deposit is returned. - /// - /// The index must be provided as explained in [`voter_at`] function. - /// - /// Also removes the lock on the balance of the voter. See [`do_set_approvals()`]. - /// - /// # - /// - O(1). - /// - Two fewer DB entries, one DB change. - /// # - fn retract_voter(origin, #[compact] index: u32) { - let who = ensure_signed(origin)?; - - ensure!(!Self::presentation_active(), "cannot retract when presenting"); - ensure!(>::exists(&who), "cannot retract non-voter"); - let index = index as usize; - let voter = Self::voter_at(index).ok_or("retraction index invalid")?; - ensure!(voter == who, "retraction index mismatch"); - - Self::remove_voter(&who, index); - T::Currency::unreserve(&who, Self::voting_bond()); - T::Currency::remove_lock(COUNCIL_SEATS_ID, &who); - } - - /// Submit oneself for candidacy. - /// - /// Account must have enough transferrable funds in it to pay the bond. - /// - /// NOTE: if `origin` has already assigned approvals via [`set_approvals`], - /// it will NOT have any usable funds to pass candidacy bond and must first retract. - /// Note that setting approvals will lock the entire balance of the voter until - /// retraction or being reported. - /// - /// # - /// - Independent of input. - /// - Three DB changes. - /// # - fn submit_candidacy(origin, #[compact] slot: u32) { - let who = ensure_signed(origin)?; - - ensure!(!Self::is_a_candidate(&who), "duplicate candidate submission"); - let slot = slot as usize; - let count = Self::candidate_count() as usize; - let candidates = Self::candidates(); - ensure!( - (slot == count && count == candidates.len()) || - (slot < candidates.len() && candidates[slot] == T::AccountId::default()), - "invalid candidate slot" - ); - // NOTE: This must be last as it has side-effects. - T::Currency::reserve(&who, Self::candidacy_bond()) - .map_err(|_| "candidate has not enough funds")?; - - >::insert(&who, (Self::vote_index(), slot as u32)); - let mut candidates = candidates; - if slot == candidates.len() { - candidates.push(who); - } else { - candidates[slot] = who; - } - >::put(candidates); - >::put(count as u32 + 1); - } - - /// Claim that `signed` is one of the top Self::carry_count() + current_vote().1 candidates. - /// Only works if the `block_number >= current_vote().0` and `< current_vote().0 + presentation_duration()` - /// `signed` should have at least - /// - /// # - /// - O(voters) compute. - /// - One DB change. - /// # - fn present_winner( - origin, - candidate: ::Source, - #[compact] total: BalanceOf, - #[compact] index: VoteIndex - ) -> Result { - let who = ensure_signed(origin)?; - ensure!( - !total.is_zero(), - "stake deposited to present winner and be added to leaderboard should be non-zero" - ); - - let candidate = T::Lookup::lookup(candidate)?; - ensure!(index == Self::vote_index(), "index not current"); - let (_, _, expiring) = Self::next_finalize().ok_or("cannot present outside of presentation period")?; - let bad_presentation_punishment = - Self::present_slash_per_voter() - * BalanceOf::::from(Self::voter_count() as u32); - ensure!( - T::Currency::can_slash(&who, bad_presentation_punishment), - "presenter must have sufficient slashable funds" - ); - - let mut leaderboard = Self::leaderboard().ok_or("leaderboard must exist while present phase active")?; - ensure!(total > leaderboard[0].0, "candidate not worthy of leaderboard"); - - if let Some(p) = Self::active_council().iter().position(|&(ref c, _)| c == &candidate) { - ensure!(p < expiring.len(), "candidate must not form a duplicated member if elected"); - } - - let voters = Self::all_voters(); - let (registered_since, candidate_index): (VoteIndex, u32) = - Self::candidate_reg_info(&candidate).ok_or("presented candidate must be current")?; - let actual_total = voters.iter() - .filter_map(|maybe_voter| maybe_voter.as_ref()) - .filter_map(|voter| match Self::voter_info(voter) { - Some(b) if b.last_active >= registered_since => { - let last_win = b.last_win; - let now = Self::vote_index(); - let stake = b.stake; - let offset = Self::get_offset(stake, now - last_win); - let weight = stake + offset + b.pot; - if Self::approvals_of_at(voter, candidate_index as usize) { - Some(weight) - } else { None } - }, - _ => None, - }) - .fold(Zero::zero(), |acc, n| acc + n); - let dupe = leaderboard.iter().find(|&&(_, ref c)| c == &candidate).is_some(); - if total == actual_total && !dupe { - // insert into leaderboard - leaderboard[0] = (total, candidate); - leaderboard.sort_by_key(|&(t, _)| t); - >::put(leaderboard); - Ok(()) - } else { - // we can rest assured it will be Ok since we checked `can_slash` earlier; still - // better safe than sorry. - let imbalance = T::Currency::slash(&who, bad_presentation_punishment).0; - T::BadPresentation::on_unbalanced(imbalance); - Err(if dupe { "duplicate presentation" } else { "incorrect total" }) - } - } - - /// Set the desired member count; if lower than the current count, then seats will not be up - /// election when they expire. If more, then a new vote will be started if one is not - /// already in progress. - fn set_desired_seats(#[compact] count: u32) { - >::put(count); - } - - /// Remove a particular member from the council. This is effective immediately. - /// - /// Note: A tally should happen instantly (if not already in a presentation - /// period) to fill the seat if removal means that the desired members are not met. - fn remove_member(who: ::Source) { - let who = T::Lookup::lookup(who)?; - let new_council: Vec<(T::AccountId, T::BlockNumber)> = Self::active_council() - .into_iter() - .filter(|i| i.0 != who) - .collect(); - >::put(new_council); - T::OnMembersChanged::on_members_changed(&[], &[who]); - } - - /// Set the presentation duration. If there is currently a vote being presented for, will - /// invoke `finalize_vote`. - fn set_presentation_duration(#[compact] count: T::BlockNumber) { - >::put(count); - } - - /// Set the presentation duration. If there is current a vote being presented for, will - /// invoke `finalize_vote`. - fn set_term_duration(#[compact] count: T::BlockNumber) { - >::put(count); - } - - fn on_initialize(n: T::BlockNumber) { - if let Err(e) = Self::end_block(n) { - print("Guru meditation"); - print(e); - } - } - } -} - -decl_storage! { - trait Store for Module as Council { - - // ---- parameters - /// How much should be locked up in order to submit one's candidacy. - pub CandidacyBond get(candidacy_bond) config(): BalanceOf = 9.into(); - /// How much should be locked up in order to be able to submit votes. - pub VotingBond get(voting_bond) config(voter_bond): BalanceOf; - /// The amount of fee paid upon each vote submission, unless if they submit a _hole_ index and replace it. - pub VotingFee get(voting_fee) config(voting_fee): BalanceOf; - /// The punishment, per voter, if you provide an invalid presentation. - pub PresentSlashPerVoter get(present_slash_per_voter) config(): BalanceOf = 1.into(); - /// How many runners-up should have their approvals persist until the next vote. - pub CarryCount get(carry_count) config(): u32 = 2; - /// How long to give each top candidate to present themselves after the vote ends. - pub PresentationDuration get(presentation_duration) config(): T::BlockNumber = 1000.into(); - /// How many vote indices need to go by after a target voter's last vote before they can be reaped if their - /// approvals are moot. - pub InactiveGracePeriod get(inactivity_grace_period) config(inactive_grace_period): VoteIndex = 1; - /// How often (in blocks) to check for new votes. - pub VotingPeriod get(voting_period) config(approval_voting_period): T::BlockNumber = 1000.into(); - /// How long each position is active for. - pub TermDuration get(term_duration) config(): T::BlockNumber = 5.into(); - /// Number of accounts that should be sitting on the council. - pub DesiredSeats get(desired_seats) config(): u32; - /// Decay factor of weight when being accumulated. It should typically be set to - /// __at least__ `council_size -1` to keep the council secure. - /// When set to `N`, it indicates `(1/N)^t` of staked is decayed at weight increment step `t`. - /// 0 will result in no weight being added at all (normal approval voting). - pub DecayRatio get(decay_ratio) config(decay_ratio): u32 = 24; - - // ---- permanent state (always relevant, changes only at the finalization of voting) - /// The current council. When there's a vote going on, this should still be used for 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 council member was elected - /// and their term duration). - pub ActiveCouncil get(active_council) 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; - - // ---- persistent state (always relevant, changes constantly) - /// A list of votes for each voter. The votes are stored as numeric values and parsed in a 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; - /// 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)>; - /// Basic information about a voter. - pub VoterInfoOf get(voter_info): map T::AccountId => Option>>; - /// The present voter list (chunked and capped at [`VOTER_SET_SIZE`]). - pub Voters get(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; - /// Current number of Voters. - pub VoterCount get(voter_count): SetIndex = 0; - /// The present candidate list. - pub Candidates get(candidates): Vec; // has holes - /// Current number of active candidates - pub CandidateCount get(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)>; - /// 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. - pub Leaderboard get(leaderboard): Option, T::AccountId)> >; // ORDERED low -> high - } -} - -decl_event!( - pub enum Event where ::AccountId { - /// reaped voter, reaper - VoterReaped(AccountId, AccountId), - /// slashed reaper - BadReaperSlashed(AccountId), - /// A tally (for approval votes of council seat(s)) has started. - TallyStarted(u32), - /// A tally (for approval votes of council seat(s)) has ended (with one or more new members). - TallyFinalized(Vec, Vec), - } -); - -impl Module { - // exposed immutables. - - /// True if we're currently in a presentation period. - pub fn presentation_active() -> bool { - >::exists() - } - - /// If `who` a candidate at the moment? - pub fn is_a_candidate(who: &T::AccountId) -> bool { - >::exists(who) - } - - /// Iff the councillor `who` still has a seat at blocknumber `n` returns `true`. - pub fn will_still_be_councillor_at(who: &T::AccountId, n: T::BlockNumber) -> bool { - Self::active_council().iter() - .find(|&&(ref a, _)| a == who) - .map(|&(_, expires)| expires > n) - .unwrap_or(false) - } - - /// Determine the block that a vote can happen on which is no less than `n`. - pub fn next_vote_from(n: T::BlockNumber) -> T::BlockNumber { - let voting_period = Self::voting_period(); - (n + voting_period - One::one()) / voting_period * voting_period - } - - /// The block number on which the tally for the next election will happen. `None` only if the - /// desired seats of the council is zero. - pub fn next_tally() -> Option { - let desired_seats = Self::desired_seats(); - if desired_seats == 0 { - None - } else { - let c = Self::active_council(); - let (next_possible, count, coming) = - if let Some((tally_end, comers, leavers)) = Self::next_finalize() { - // if there's a tally in progress, then next tally can begin immediately afterwards - (tally_end, c.len() - leavers.len() + comers as usize, comers) - } else { - (>::block_number(), c.len(), 0) - }; - if count < desired_seats as usize { - Some(next_possible) - } else { - // next tally begins once enough council members expire to bring members below desired. - if desired_seats <= coming { - // the entire amount of desired seats is less than those new members - we'll have - // to wait until they expire. - Some(next_possible + Self::term_duration()) - } else { - Some(c[c.len() - (desired_seats - coming) as usize].1) - } - }.map(Self::next_vote_from) - } - } - - // Private - /// Check there's nothing to do this block - fn end_block(block_number: T::BlockNumber) -> Result { - if (block_number % Self::voting_period()).is_zero() { - if let Some(number) = Self::next_tally() { - if block_number == number { - Self::start_tally(); - } - } - } - if let Some((number, _, _)) = Self::next_finalize() { - if block_number == number { - Self::finalize_tally()? - } - } - Ok(()) - } - - /// Remove a voter at a specified index from the system. - fn remove_voter(voter: &T::AccountId, index: usize) { - let (set_index, vec_index) = Self::split_index(index, VOTER_SET_SIZE); - let mut set = Self::voters(set_index); - set[vec_index] = None; - >::insert(set_index, set); - >::mutate(|c| *c = *c - 1); - Self::remove_all_approvals_of(voter); - >::remove(voter); - } - - /// Actually do the voting. - /// - /// The voter index must be provided as explained in [`voter_at`] function. - fn do_set_approvals(who: T::AccountId, votes: Vec, index: VoteIndex, hint: SetIndex) -> Result { - let candidates = Self::candidates(); - - ensure!(!Self::presentation_active(), "no approval changes during presentation period"); - ensure!(index == Self::vote_index(), "incorrect vote index"); - ensure!(!candidates.is_empty(), "amount of candidates to receive approval votes should be non-zero"); - // Prevent a vote from voters that provide a list of votes that exceeds the candidates length - // since otherwise an attacker may be able to submit a very long list of `votes` that far exceeds - // the amount of candidates and waste more computation than a reasonable voting bond would cover. - ensure!(candidates.len() >= votes.len(), "amount of candidate votes cannot exceed amount of candidates"); - - // Amount to be locked up. - let mut locked_balance = T::Currency::total_balance(&who); - let mut pot_to_set = Zero::zero(); - let hint = hint as usize; - - if let Some(info) = Self::voter_info(&who) { - // already a voter. Index must be valid. No fee. update pot. O(1) - let voter = Self::voter_at(hint).ok_or("invalid voter index")?; - ensure!(voter == who, "wrong voter index"); - - // write new accumulated offset. - let last_win = info.last_win; - let now = index; - let offset = Self::get_offset(info.stake, now - last_win); - pot_to_set = info.pot + offset; - } else { - // not yet a voter. Index _could be valid_. Fee might apply. Bond will be reserved O(1). - ensure!( - T::Currency::free_balance(&who) > Self::voting_bond(), - "new voter must have sufficient funds to pay the bond" - ); - - let (set_index, vec_index) = Self::split_index(hint, VOTER_SET_SIZE); - match Self::cell_status(set_index, vec_index) { - CellStatus::Hole => { - // requested cell was a valid hole. - >::mutate(set_index, |set| set[vec_index] = Some(who.clone())); - }, - CellStatus::Head | CellStatus::Occupied => { - // Either occupied or out-of-range. - let next = Self::next_nonfull_voter_set(); - let mut set = Self::voters(next); - // Caused a new set to be created. Pay for it. - // This is the last potential error. Writes will begin afterwards. - if set.is_empty() { - let imbalance = T::Currency::withdraw( - &who, - Self::voting_fee(), - WithdrawReason::Fee, - ExistenceRequirement::KeepAlive, - )?; - T::BadVoterIndex::on_unbalanced(imbalance); - // NOTE: this is safe since the `withdraw()` will check this. - locked_balance -= Self::voting_fee(); - } - Self::checked_push_voter(&mut set, who.clone(), next); - >::insert(next, set); - } - } - - T::Currency::reserve(&who, Self::voting_bond())?; - >::mutate(|c| *c = *c + 1); - } - - T::Currency::set_lock( - COUNCIL_SEATS_ID, - &who, - locked_balance, - T::BlockNumber::max_value(), - WithdrawReasons::all() - ); - - >::insert( - &who, - VoterInfo::> { - last_active: index, - last_win: index, - stake: locked_balance, - pot: pot_to_set, - } - ); - Self::set_approvals_chunked(&who, votes); - - Ok(()) - } - - /// Close the voting, record the number of seats that are actually up for grabs. - fn start_tally() { - let active_council = Self::active_council(); - let desired_seats = Self::desired_seats() as usize; - let number = >::block_number(); - let expiring = active_council.iter().take_while(|i| i.1 <= number).map(|i| i.0.clone()).collect::>(); - let retaining_seats = active_council.len() - expiring.len(); - if retaining_seats < desired_seats { - let empty_seats = desired_seats - retaining_seats; - >::put((number + Self::presentation_duration(), empty_seats as u32, expiring)); - - // initialize leaderboard. - let leaderboard_size = empty_seats + Self::carry_count() as usize; - >::put(vec![(Zero::zero(), T::AccountId::default()); leaderboard_size]); - - Self::deposit_event(RawEvent::TallyStarted(empty_seats as u32)); - } - } - - /// Finalize the vote, removing each of the `removals` and inserting `seats` of the most approved - /// candidates in their place. If the total council members is less than the desired membership - /// a new vote is started. - /// Clears all presented candidates, returning the bond of the elected ones. - fn finalize_tally() -> Result { - let (_, coming, expiring): (T::BlockNumber, u32, Vec) = - >::take().ok_or("finalize can only be called after a tally is started.")?; - let leaderboard: Vec<(BalanceOf, T::AccountId)> = >::take().unwrap_or_default(); - let new_expiry = >::block_number() + Self::term_duration(); - - // return bond to winners. - let candidacy_bond = Self::candidacy_bond(); - let incoming: Vec<_> = leaderboard.iter() - .rev() - .take_while(|&&(b, _)| !b.is_zero()) - .take(coming as usize) - .map(|(_, a)| a) - .cloned() - .inspect(|a| { T::Currency::unreserve(a, candidacy_bond); }) - .collect(); - - // Update last win index for anyone voted for any of the incomings. - incoming.iter().filter_map(|i| Self::candidate_reg_info(i)).for_each(|r| { - let index = r.1 as usize; - Self::all_voters() - .iter() - .filter_map(|mv| mv.as_ref()) - .filter(|v| Self::approvals_of_at(*v, index)) - .for_each(|v| >::mutate(v, |a| { - if let Some(activity) = a { activity.last_win = Self::vote_index() + 1; } - })); - }); - let active_council = Self::active_council(); - let outgoing: Vec<_> = active_council.iter() - .take(expiring.len()) - .map(|a| a.0.clone()).collect(); - - // set the new council. - let mut new_council: Vec<_> = active_council - .into_iter() - .skip(expiring.len()) - .chain(incoming.iter().cloned().map(|a| (a, new_expiry))) - .collect(); - new_council.sort_by_key(|&(_, expiry)| expiry); - >::put(new_council); - - T::OnMembersChanged::on_members_changed(&incoming, &outgoing); - - // clear all except runners-up from candidate list. - let candidates = Self::candidates(); - let mut new_candidates = vec![T::AccountId::default(); candidates.len()]; // shrink later. - let runners_up = leaderboard.into_iter() - .rev() - .take_while(|&(b, _)| !b.is_zero()) - .skip(coming as usize) - .filter_map(|(_, a)| Self::candidate_reg_info(&a).map(|i| (a, i.1))); - let mut count = 0u32; - for (address, slot) in runners_up { - new_candidates[slot as usize] = address; - count += 1; - } - for (old, new) in candidates.iter().zip(new_candidates.iter()) { - if old != new { - // removed - kill it - >::remove(old); - } - } - // discard any superfluous slots. - if let Some(last_index) = new_candidates.iter().rposition(|c| *c != T::AccountId::default()) { - new_candidates.truncate(last_index + 1); - } - - Self::deposit_event(RawEvent::TallyFinalized(incoming, outgoing)); - - >::put(new_candidates); - >::put(count); - >::put(Self::vote_index() + 1); - Ok(()) - } - - fn checked_push_voter(set: &mut Vec>, who: T::AccountId, index: u32) { - let len = set.len(); - - // Defensive only: this should never happen. Don't push since it will break more things. - if len == VOTER_SET_SIZE { return; } - - set.push(Some(who)); - if len + 1 == VOTER_SET_SIZE { - >::put(index + 1); - } - } - - /// Get the set and vector index of a global voter index. - /// - /// Note that this function does not take holes into account. - /// See [`voter_at`]. - fn split_index(index: usize, scale: usize) -> (SetIndex, usize) { - let set_index = (index / scale) as u32; - let vec_index = index % scale; - (set_index, vec_index) - } - - /// Return a concatenated vector over all voter sets. - fn all_voters() -> Vec> { - let mut all = >::get(0); - let mut index = 1; - // NOTE: we could also use `Self::next_nonfull_voter_set()` here but that might change based - // on how we do chunking. This is more generic. - loop { - let next_set = >::get(index); - if next_set.is_empty() { - break; - } else { - index += 1; - all.extend(next_set); - } - } - all - } - - /// Shorthand for fetching a voter at a specific (global) index. - /// - /// NOTE: this function is used for checking indices. Yet, it does not take holes into account. - /// This means that any account submitting an index at any point in time should submit: - /// `VOTER_SET_SIZE * set_index + local_index`, meaning that you are ignoring all holes in the - /// first `set_index` sets. - fn voter_at(index: usize) -> Option { - let (set_index, vec_index) = Self::split_index(index, VOTER_SET_SIZE); - let set = Self::voters(set_index); - if vec_index < set.len() { - set[vec_index].clone() - } else { - None - } - } - - /// A more sophisticated version of `voter_at`. Will be kept separate as most often it is an overdue - /// compared to `voter_at`. Only used when setting approvals. - fn cell_status(set_index: SetIndex, vec_index: usize) -> CellStatus { - let set = Self::voters(set_index); - if vec_index < set.len() { - if let Some(_) = set[vec_index] { - CellStatus::Occupied - } else { - CellStatus::Hole - } - } else { - CellStatus::Head - } - } - - /// Sets the approval of a voter in a chunked manner. - fn set_approvals_chunked(who: &T::AccountId, approvals: Vec) { - let approvals_flag_vec = Self::bool_to_flag(approvals); - approvals_flag_vec - .chunks(APPROVAL_SET_SIZE) - .enumerate() - .for_each(|(index, slice)| >::insert((who.clone(), index as SetIndex), slice.to_vec())); - } - - /// shorthand for fetching a specific approval of a voter at a specific (global) index. - /// - /// Using this function to read a vote is preferred as it reads `APPROVAL_SET_SIZE` items of type - /// `ApprovalFlag` from storage at most; not all of them. - /// - /// Note that false is returned in case of no-vote or an explicit `false`. - fn approvals_of_at(who: &T::AccountId, index: usize) -> bool { - let (flag_index, bit) = Self::split_index(index, APPROVAL_FLAG_LEN); - let (set_index, vec_index) = Self::split_index(flag_index as usize, APPROVAL_SET_SIZE); - let set = Self::approvals_of((who.clone(), set_index)); - if vec_index < set.len() { - // This is because bit_at treats numbers in lsb -> msb order. - let reversed_index = set.len() - 1 - vec_index; - Self::bit_at(set[reversed_index], bit) - } else { - false - } - } - - /// Return true of the bit `n` of scalar `x` is set to `1` and false otherwise. - fn bit_at(x: ApprovalFlag, n: usize) -> bool { - if n < APPROVAL_FLAG_LEN { - // x & ( APPROVAL_FLAG_MASK >> n ) != 0 - x & ( 1 << n ) != 0 - } else { - false - } - } - - /// Convert a vec of boolean approval flags to a vec of integers, as denoted by - /// the type `ApprovalFlag`. see `bool_to_flag_should_work` test for examples. - pub fn bool_to_flag(x: Vec) -> Vec { - let mut result: Vec = Vec::with_capacity(x.len() / APPROVAL_FLAG_LEN); - if x.is_empty() { - return result; - } - result.push(0); - let mut index = 0; - let mut counter = 0; - loop { - let shl_index = counter % APPROVAL_FLAG_LEN; - result[index] += (if x[counter] { 1 } else { 0 }) << shl_index; - counter += 1; - if counter > x.len() - 1 { break; } - if counter % APPROVAL_FLAG_LEN == 0 { - result.push(0); - index += 1; - } - } - result - } - - /// Convert a vec of flags (u32) to boolean. - pub fn flag_to_bool(chunk: Vec) -> Vec { - let mut result = Vec::with_capacity(chunk.len()); - if chunk.is_empty() { return vec![] } - chunk.into_iter() - .map(|num| (0..APPROVAL_FLAG_LEN).map(|bit| Self::bit_at(num, bit)).collect::>()) - .for_each(|c| { - let last_approve = match c.iter().rposition(|n| *n) { - Some(index) => index + 1, - None => 0 - }; - result.extend(c.into_iter().take(last_approve)); - }); - result - } - - /// Return a concatenated vector over all approvals of a voter as boolean. - /// The trailing zeros are removed. - fn all_approvals_of(who: &T::AccountId) -> Vec { - let mut all: Vec = vec![]; - let mut index = 0_u32; - loop { - let chunk = Self::approvals_of((who.clone(), index)); - if chunk.is_empty() { break; } - all.extend(Self::flag_to_bool(chunk)); - index += 1; - } - all - } - - /// Remove all approvals associated with one account. - fn remove_all_approvals_of(who: &T::AccountId) { - let mut index = 0; - loop { - let set = Self::approvals_of((who.clone(), index)); - if set.len() > 0 { - >::remove((who.clone(), index)); - index += 1; - } else { - break - } - } - } - - /// Calculates the offset value (stored pot) of a stake, based on the distance - /// to the last win_index, `t`. Regardless of the internal implementation, - /// it should always be used with the following structure: - /// - /// Given Stake of voter `V` being `x` and distance to last_win index `t`, the new weight - /// of `V` is `x + get_offset(x, t)`. - /// - /// In other words, this function returns everything extra that should be added - /// to a voter's stake value to get the correct weight. Indeed, zero is - /// returned if `t` is zero. - fn get_offset(stake: BalanceOf, t: VoteIndex) -> BalanceOf { - let decay_ratio: BalanceOf = Self::decay_ratio().into(); - if t > 150 { return stake * decay_ratio } - let mut offset = stake; - let mut r = Zero::zero(); - let decay = decay_ratio + One::one(); - for _ in 0..t { - offset = offset.saturating_sub(offset / decay); - r += offset - } - r - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::tests::*; - use srml_support::{assert_ok, assert_noop, assert_err}; - - fn voter_ids() -> Vec { - Council::all_voters().iter().map(|v| v.unwrap_or(0) ).collect::>() - } - - fn vote(i: u64, l: usize) { - let _ = Balances::make_free_balance_be(&i, 20); - assert_ok!(Council::set_approvals(Origin::signed(i), (0..l).map(|_| true).collect::>(), 0, 0)); - } - - fn vote_at(i: u64, l: usize, index: VoteIndex) { - let _ = Balances::make_free_balance_be(&i, 20); - assert_ok!(Council::set_approvals(Origin::signed(i), (0..l).map(|_| true).collect::>(), 0, index)); - } - - fn create_candidate(i: u64, index: u32) { - let _ = Balances::make_free_balance_be(&i, 20); - assert_ok!(Council::submit_candidacy(Origin::signed(i), index)); - } - - fn bond() -> u64 { - Council::voting_bond() - } - - - #[test] - fn bool_to_flag_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { - assert_eq!(Council::bool_to_flag(vec![]), vec![]); - assert_eq!(Council::bool_to_flag(vec![false]), vec![0]); - assert_eq!(Council::bool_to_flag(vec![true]), vec![1]); - assert_eq!(Council::bool_to_flag(vec![true, true, true, true]), vec![15]); - assert_eq!(Council::bool_to_flag(vec![true, true, true, true, true]), vec![15 + 16]); - - let set_1 = vec![ - true, false, false, false, // 0x1 - false, true, true, true, // 0xE - ]; - assert_eq!( - Council::bool_to_flag(set_1.clone()), - vec![0x00_00_00_E1_u32] - ); - assert_eq!( - Council::flag_to_bool(vec![0x00_00_00_E1_u32]), - set_1 - ); - - let set_2 = vec![ - false, false, false, true, // 0x8 - false, true, false, true, // 0xA - ]; - assert_eq!( - Council::bool_to_flag(set_2.clone()), - vec![0x00_00_00_A8_u32] - ); - assert_eq!( - Council::flag_to_bool(vec![0x00_00_00_A8_u32]), - set_2 - ); - - let mut rhs = (0..100/APPROVAL_FLAG_LEN).map(|_| 0xFFFFFFFF_u32).collect::>(); - // NOTE: this might be need change based on `APPROVAL_FLAG_LEN`. - rhs.extend(vec![0x00_00_00_0F]); - assert_eq!( - Council::bool_to_flag((0..100).map(|_| true).collect()), - rhs - ) - }) - } - - #[test] - fn params_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { - System::set_block_number(1); - assert_eq!(Council::next_vote_from(1), 4); - assert_eq!(Council::next_vote_from(4), 4); - assert_eq!(Council::next_vote_from(5), 8); - assert_eq!(Council::vote_index(), 0); - assert_eq!(Council::candidacy_bond(), 3); - assert_eq!(Council::voting_bond(), 0); - assert_eq!(Council::voting_fee(), 0); - assert_eq!(Council::present_slash_per_voter(), 1); - assert_eq!(Council::presentation_duration(), 2); - assert_eq!(Council::inactivity_grace_period(), 1); - assert_eq!(Council::voting_period(), 4); - assert_eq!(Council::term_duration(), 5); - assert_eq!(Council::desired_seats(), 2); - assert_eq!(Council::carry_count(), 2); - - assert_eq!(Council::active_council(), vec![]); - assert_eq!(Council::next_tally(), Some(4)); - assert_eq!(Council::presentation_active(), false); - assert_eq!(Council::next_finalize(), None); - - assert_eq!(Council::candidates(), Vec::::new()); - assert_eq!(Council::is_a_candidate(&1), false); - assert_eq!(Council::candidate_reg_info(1), None); - - assert_eq!(Council::voters(0), Vec::>::new()); - assert_eq!(Council::voter_info(1), None); - assert_eq!(Council::all_approvals_of(&1), vec![]); - }); - } - - #[test] - fn voter_set_growth_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 1)); - - // create 65. 64 (set0) + 1 (set1) - (1..=63).for_each(|i| vote(i, 2)); - assert_eq!(Council::next_nonfull_voter_set(), 0); - vote(64, 2); - assert_eq!(Council::next_nonfull_voter_set(), 1); - vote(65, 2); - - let set1 = Council::voters(0); - let set2 = Council::voters(1); - - assert_eq!(set1.len(), 64); - assert_eq!(set2.len(), 1); - - assert_eq!(set1[0], Some(1)); - assert_eq!(set1[10], Some(11)); - assert_eq!(set2[0], Some(65)); - }) - } - - #[test] - fn voter_set_reclaim_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 1)); - - (1..=129).for_each(|i| vote(i, 2)); - assert_eq!(Council::next_nonfull_voter_set(), 2); - - assert_ok!(Council::retract_voter(Origin::signed(11), 10)); - - assert_ok!(Council::retract_voter(Origin::signed(66), 65)); - assert_ok!(Council::retract_voter(Origin::signed(67), 66)); - - // length does not show it but holes do exist. - assert_eq!(Council::voters(0).len(), 64); - assert_eq!(Council::voters(1).len(), 64); - assert_eq!(Council::voters(2).len(), 1); - - assert_eq!(Council::voters(0)[10], None); - assert_eq!(Council::voters(1)[1], None); - assert_eq!(Council::voters(1)[2], None); - // Next set with capacity is 2. - assert_eq!(Council::next_nonfull_voter_set(), 2); - - // But we can fill a hole. - vote_at(130, 2, 10); - - // Nothing added to set 2. A hole was filled. - assert_eq!(Council::voters(0).len(), 64); - assert_eq!(Council::voters(1).len(), 64); - assert_eq!(Council::voters(2).len(), 1); - - // and the next two (scheduled) to the second set. - assert_eq!(Council::next_nonfull_voter_set(), 2); - }) - } - - #[test] - fn approvals_set_growth_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { - // 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)); - - // all approvals of should return the exact expected vector. - assert_eq!(Council::all_approvals_of(&180), (0..180).map(|_| true).collect::>()); - - assert_eq!(Council::all_approvals_of(&32), (0..32).map(|_| true).collect::>()); - assert_eq!(Council::all_approvals_of(&8), (0..8).map(|_| true).collect::>()); - assert_eq!(Council::all_approvals_of(&64), (0..64).map(|_| true).collect::>()); - assert_eq!(Council::all_approvals_of(&65), (0..65).map(|_| true).collect::>()); - assert_eq!(Council::all_approvals_of(&63), (0..63).map(|_| true).collect::>()); - - // NOTE: assuming that APPROVAL_SET_SIZE is more or less small-ish. Might fail otherwise. - let full_sets = (180 / APPROVAL_FLAG_LEN) / APPROVAL_SET_SIZE; - let left_over = (180 / APPROVAL_FLAG_LEN) / APPROVAL_SET_SIZE; - let rem = 180 % APPROVAL_FLAG_LEN; - - // grab and check the last full set, if it exists. - if full_sets > 0 { - assert_eq!( - Council::approvals_of((180, (full_sets-1) as SetIndex )), - Council::bool_to_flag((0..APPROVAL_SET_SIZE * APPROVAL_FLAG_LEN).map(|_| true).collect::>()) - ); - } - - // grab and check the last, half-empty, set. - if left_over > 0 { - assert_eq!( - Council::approvals_of((180, full_sets as SetIndex)), - Council::bool_to_flag((0..left_over * APPROVAL_FLAG_LEN + rem).map(|_| true).collect::>()) - ); - } - }) - } - - - #[test] - fn cell_status_works() { - with_externalities(&mut ExtBuilder::default().build(), || { - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 1)); - - (1..=63).for_each(|i| vote(i, 2)); - - assert_ok!(Council::retract_voter(Origin::signed(11), 10)); - assert_ok!(Council::retract_voter(Origin::signed(21), 20)); - - assert_eq!(Council::cell_status(0, 10), CellStatus::Hole); - assert_eq!(Council::cell_status(0, 0), CellStatus::Occupied); - assert_eq!(Council::cell_status(0, 20), CellStatus::Hole); - assert_eq!(Council::cell_status(0, 63), CellStatus::Head); - assert_eq!(Council::cell_status(1, 0), CellStatus::Head); - assert_eq!(Council::cell_status(1, 10), CellStatus::Head); - }) - } - - #[test] - fn initial_set_approvals_ignores_voter_index() { - with_externalities(&mut ExtBuilder::default().build(), || { - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - - // Last argument is essentially irrelevant. You might get or miss a tip. - assert_ok!(Council::set_approvals(Origin::signed(3), vec![true], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![true], 0, 5)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![true], 0, 100)); - - // indices are more or less ignored. all is pushed. - assert_eq!(voter_ids(), vec![3, 4, 5]); - }) - } - - #[test] - fn bad_approval_index_slashes_voters_and_bond_reduces_stake() { - with_externalities(&mut ExtBuilder::default().voting_fee(5).voter_bond(2).build(), || { - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 1)); - - (1..=63).for_each(|i| vote(i, 2)); - assert_eq!(Balances::free_balance(&1), 20 - 5 - 2); // -5 fee -2 bond - assert_eq!(Balances::free_balance(&10), 20 - 2); - assert_eq!(Balances::free_balance(&60), 20 - 2); - - // still no fee - vote(64, 2); - assert_eq!(Balances::free_balance(&64), 20 - 2); // -2 bond - assert_eq!( - Council::voter_info(&64).unwrap(), - VoterInfo { last_win: 0, last_active: 0, stake: 20, pot:0 } - ); - - assert_eq!(Council::next_nonfull_voter_set(), 1); - - // now we charge the next voter. - vote(65, 2); - assert_eq!(Balances::free_balance(&65), 20 - 5 - 2); - assert_eq!( - Council::voter_info(&65).unwrap(), - VoterInfo { last_win: 0, last_active: 0, stake: 15, pot:0 } - ); - }); - } - - #[test] - fn subsequent_set_approvals_checks_voter_index() { - with_externalities(&mut ExtBuilder::default().build(), || { - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - - assert_ok!(Council::set_approvals(Origin::signed(3), vec![true], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![true], 0, 5)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![true], 0, 100)); - - // invalid index - assert_noop!(Council::set_approvals(Origin::signed(4), vec![true], 0, 5), "invalid voter index"); - // wrong index - assert_noop!(Council::set_approvals(Origin::signed(4), vec![true], 0, 0), "wrong voter index"); - // correct - assert_ok!(Council::set_approvals(Origin::signed(4), vec![true], 0, 1)); - }) - } - - #[test] - fn voter_index_does_not_take_holes_into_account() { - with_externalities(&mut ExtBuilder::default().build(), || { - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 1)); - - // create 65. 64 (set0) + 1 (set1) - (1..=65).for_each(|i| vote(i, 2)); - - // account 65 has global index 65. - assert_eq!(Council::voter_at(64).unwrap(), 65); - - assert_ok!(Council::retract_voter(Origin::signed(1), 0)); - assert_ok!(Council::retract_voter(Origin::signed(2), 1)); - - // still the same. These holes are in some other set. - assert_eq!(Council::voter_at(64).unwrap(), 65); - // proof: can submit a new approval with the old index. - assert_noop!(Council::set_approvals(Origin::signed(65), vec![false, true], 0, 64 - 2), "wrong voter index"); - assert_ok!(Council::set_approvals(Origin::signed(65), vec![false, true], 0, 64)); - }) - } - - #[test] - fn simple_candidate_submission_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { - System::set_block_number(1); - assert_eq!(Council::candidates(), Vec::::new()); - assert_eq!(Council::candidate_reg_info(1), None); - assert_eq!(Council::candidate_reg_info(2), None); - assert_eq!(Council::is_a_candidate(&1), false); - assert_eq!(Council::is_a_candidate(&2), false); - - assert_ok!(Council::submit_candidacy(Origin::signed(1), 0)); - assert_eq!(Council::candidates(), vec![1]); - assert_eq!(Council::candidate_reg_info(1), Some((0, 0))); - assert_eq!(Council::candidate_reg_info(2), None); - assert_eq!(Council::is_a_candidate(&1), true); - assert_eq!(Council::is_a_candidate(&2), false); - - assert_ok!(Council::submit_candidacy(Origin::signed(2), 1)); - assert_eq!(Council::candidates(), vec![1, 2]); - assert_eq!(Council::candidate_reg_info(1), Some((0, 0))); - assert_eq!(Council::candidate_reg_info(2), Some((0, 1))); - assert_eq!(Council::is_a_candidate(&1), true); - assert_eq!(Council::is_a_candidate(&2), true); - }); - } - - fn new_test_ext_with_candidate_holes() -> runtime_io::TestExternalities { - let mut t = ExtBuilder::default().build(); - with_externalities(&mut t, || { - >::put(vec![0, 0, 1]); - >::put(1); - >::insert(1, (0, 2)); - }); - t - } - - #[test] - fn candidate_submission_using_free_slot_should_work() { - let mut t = new_test_ext_with_candidate_holes(); - - with_externalities(&mut t, || { - System::set_block_number(1); - assert_eq!(Council::candidates(), vec![0, 0, 1]); - - assert_ok!(Council::submit_candidacy(Origin::signed(2), 1)); - assert_eq!(Council::candidates(), vec![0, 2, 1]); - - assert_ok!(Council::submit_candidacy(Origin::signed(3), 0)); - assert_eq!(Council::candidates(), vec![3, 2, 1]); - }); - } - - #[test] - fn candidate_submission_using_alternative_free_slot_should_work() { - let mut t = new_test_ext_with_candidate_holes(); - - with_externalities(&mut t, || { - System::set_block_number(1); - assert_eq!(Council::candidates(), vec![0, 0, 1]); - - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_eq!(Council::candidates(), vec![2, 0, 1]); - - assert_ok!(Council::submit_candidacy(Origin::signed(3), 1)); - assert_eq!(Council::candidates(), vec![2, 3, 1]); - }); - } - - #[test] - fn candidate_submission_not_using_free_slot_should_not_work() { - let mut t = new_test_ext_with_candidate_holes(); - - with_externalities(&mut t, || { - System::set_block_number(1); - assert_noop!(Council::submit_candidacy(Origin::signed(4), 3), "invalid candidate slot"); - }); - } - - #[test] - fn bad_candidate_slot_submission_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { - System::set_block_number(1); - assert_eq!(Council::candidates(), Vec::::new()); - assert_noop!(Council::submit_candidacy(Origin::signed(1), 1), "invalid candidate slot"); - }); - } - - #[test] - fn non_free_candidate_slot_submission_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { - System::set_block_number(1); - assert_eq!(Council::candidates(), Vec::::new()); - assert_ok!(Council::submit_candidacy(Origin::signed(1), 0)); - assert_eq!(Council::candidates(), vec![1]); - assert_noop!(Council::submit_candidacy(Origin::signed(2), 0), "invalid candidate slot"); - }); - } - - #[test] - fn dupe_candidate_submission_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { - System::set_block_number(1); - assert_eq!(Council::candidates(), Vec::::new()); - assert_ok!(Council::submit_candidacy(Origin::signed(1), 0)); - assert_eq!(Council::candidates(), vec![1]); - assert_noop!(Council::submit_candidacy(Origin::signed(1), 1), "duplicate candidate submission"); - }); - } - - #[test] - fn poor_candidate_submission_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { - System::set_block_number(1); - assert_eq!(Council::candidates(), Vec::::new()); - assert_noop!(Council::submit_candidacy(Origin::signed(7), 0), "candidate has not enough funds"); - }); - } - - #[test] - fn balance_should_lock_to_the_maximum() { - with_externalities(&mut ExtBuilder::default().build(), || { - System::set_block_number(1); - assert_eq!(Council::candidates(), Vec::::new()); - assert_eq!(Balances::free_balance(&2), 20); - - assert_ok!(Council::submit_candidacy(Origin::signed(5), 0)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true], 0, 0)); - - assert_eq!(Balances::free_balance(&2), 20 - bond() ); - assert_noop!(Balances::reserve(&2, 1), "account liquidity restrictions prevent withdrawal"); // locked. - - // deposit a bit more. - let _ = Balances::deposit_creating(&2, 100); - assert_ok!(Balances::reserve(&2, 1)); // locked but now has enough. - - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true], 0, 0)); - assert_noop!(Balances::reserve(&2, 1), "account liquidity restrictions prevent withdrawal"); // locked. - assert_eq!(Balances::locks(&2).len(), 1); - assert_eq!(Balances::locks(&2)[0].amount, 100 + 20); - - assert_ok!(Council::retract_voter(Origin::signed(2), 0)); - - assert_eq!(Balances::locks(&2).len(), 0); - assert_eq!(Balances::free_balance(&2), 120 - 1); // 1 ok call to .reserve() happened. - assert_ok!(Balances::reserve(&2, 1)); // unlocked. - }); - } - - #[test] - fn balance_should_lock_on_submit_approvals_unlock_on_retract() { - with_externalities(&mut ExtBuilder::default().voter_bond(8).voting_fee(0).build(), || { - System::set_block_number(1); - assert_eq!(Council::candidates(), Vec::::new()); - assert_eq!(Balances::free_balance(&2), 20); - - assert_ok!(Council::submit_candidacy(Origin::signed(5), 0)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true], 0, 0)); - - assert_eq!(Balances::free_balance(&2), 12); // 20 - 8 (bond) - assert_noop!(Balances::reserve(&2, 10), "account liquidity restrictions prevent withdrawal"); // locked. - - assert_ok!(Council::retract_voter(Origin::signed(2), 0)); - - assert_eq!(Balances::free_balance(&2), 20); - assert_ok!(Balances::reserve(&2, 10)); // unlocked. - }); - } - - #[test] - fn accumulating_weight_and_decaying_should_work() { - with_externalities(&mut ExtBuilder::default().balance_factor(10).build(), || { - System::set_block_number(4); - assert!(!Council::presentation_active()); - - assert_ok!(Council::submit_candidacy(Origin::signed(6), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 1)); - assert_ok!(Council::submit_candidacy(Origin::signed(1), 2)); - - assert_ok!(Council::set_approvals(Origin::signed(6), vec![true, false, false], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, true, false], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(1), vec![false, false, true], 0, 0)); - - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert!(Council::presentation_active()); - - assert_eq!(Council::present_winner(Origin::signed(6), 6, 600, 0), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(5), 5, 500, 0), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(1), 1, 100, 0), Ok(())); - assert_eq!(Council::leaderboard(), Some(vec![(0, 0), (100, 1), (500, 5), (600, 6)])); - assert_ok!(Council::end_block(System::block_number())); - - assert_eq!(Council::active_council(), vec![(6, 11), (5, 11)]); - assert_eq!(Council::voter_info(6).unwrap(), VoterInfo { last_win: 1, last_active: 0, stake: 600, pot: 0}); - assert_eq!(Council::voter_info(5).unwrap(), VoterInfo { last_win: 1, last_active: 0, stake: 500, pot: 0}); - assert_eq!(Council::voter_info(1).unwrap(), VoterInfo { last_win: 0, last_active: 0, stake: 100, pot: 0}); - - System::set_block_number(12); - // retract needed to unlock approval funds => submit candidacy again. - assert_ok!(Council::retract_voter(Origin::signed(6), 0)); - assert_ok!(Council::retract_voter(Origin::signed(5), 1)); - assert_ok!(Council::submit_candidacy(Origin::signed(6), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 1)); - assert_ok!(Council::set_approvals(Origin::signed(6), vec![true, false, false], 1, 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, true, false], 1, 1)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(14); - assert!(Council::presentation_active()); - assert_eq!(Council::present_winner(Origin::signed(6), 6, 600, 1), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(5), 5, 500, 1), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(1), 1, 100 + Council::get_offset(100, 1), 1), Ok(())); - assert_eq!(Council::leaderboard(), Some(vec![(0, 0), (100 + 96, 1), (500, 5), (600, 6)])); - assert_ok!(Council::end_block(System::block_number())); - - assert_eq!(Council::active_council(), vec![(6, 19), (5, 19)]); - assert_eq!( - Council::voter_info(6).unwrap(), - VoterInfo { last_win: 2, last_active: 1, stake: 600, pot:0 } - ); - assert_eq!(Council::voter_info(5).unwrap(), VoterInfo { last_win: 2, last_active: 1, stake: 500, pot:0 }); - assert_eq!(Council::voter_info(1).unwrap(), VoterInfo { last_win: 0, last_active: 0, stake: 100, pot:0 }); - - System::set_block_number(20); - assert_ok!(Council::retract_voter(Origin::signed(6), 0)); - assert_ok!(Council::retract_voter(Origin::signed(5), 1)); - assert_ok!(Council::submit_candidacy(Origin::signed(6), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 1)); - assert_ok!(Council::set_approvals(Origin::signed(6), vec![true, false, false], 2, 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, true, false], 2, 1)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(22); - assert!(Council::presentation_active()); - assert_eq!(Council::present_winner(Origin::signed(6), 6, 600, 2), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(5), 5, 500, 2), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(1), 1, 100 + Council::get_offset(100, 2), 2), Ok(())); - assert_eq!(Council::leaderboard(), Some(vec![(0, 0), (100 + 96 + 93, 1), (500, 5), (600, 6)])); - assert_ok!(Council::end_block(System::block_number())); - - assert_eq!(Council::active_council(), vec![(6, 27), (5, 27)]); - assert_eq!( - Council::voter_info(6).unwrap(), - VoterInfo { last_win: 3, last_active: 2, stake: 600, pot: 0} - ); - assert_eq!(Council::voter_info(5).unwrap(), VoterInfo { last_win: 3, last_active: 2, stake: 500, pot: 0}); - assert_eq!(Council::voter_info(1).unwrap(), VoterInfo { last_win: 0, last_active: 0, stake: 100, pot: 0}); - - - System::set_block_number(28); - assert_ok!(Council::retract_voter(Origin::signed(6), 0)); - assert_ok!(Council::retract_voter(Origin::signed(5), 1)); - assert_ok!(Council::submit_candidacy(Origin::signed(6), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 1)); - assert_ok!(Council::set_approvals(Origin::signed(6), vec![true, false, false], 3, 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, true, false], 3, 1)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(30); - assert!(Council::presentation_active()); - assert_eq!(Council::present_winner(Origin::signed(6), 6, 600, 3), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(5), 5, 500, 3), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(1), 1, 100 + Council::get_offset(100, 3), 3), Ok(())); - assert_eq!(Council::leaderboard(), Some(vec![(0, 0), (100 + 96 + 93 + 90, 1), (500, 5), (600, 6)])); - assert_ok!(Council::end_block(System::block_number())); - - assert_eq!(Council::active_council(), vec![(6, 35), (5, 35)]); - assert_eq!( - Council::voter_info(6).unwrap(), - VoterInfo { last_win: 4, last_active: 3, stake: 600, pot: 0} - ); - assert_eq!(Council::voter_info(5).unwrap(), VoterInfo { last_win: 4, last_active: 3, stake: 500, pot: 0}); - assert_eq!(Council::voter_info(1).unwrap(), VoterInfo { last_win: 0, last_active: 0, stake: 100, pot: 0}); - }) - } - - #[test] - fn winning_resets_accumulated_pot() { - with_externalities(&mut ExtBuilder::default().balance_factor(10).build(), || { - System::set_block_number(4); - assert!(!Council::presentation_active()); - - assert_ok!(Council::submit_candidacy(Origin::signed(6), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(4), 1)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 2)); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 3)); - - assert_ok!(Council::set_approvals(Origin::signed(6), vec![true, false, false, false], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![false, true, false, false], 0, 1)); - assert_ok!(Council::set_approvals(Origin::signed(3), vec![false, false, true, true], 0, 2)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert!(Council::presentation_active()); - assert_eq!(Council::present_winner(Origin::signed(6), 6, 600, 0), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(4), 4, 400, 0), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(3), 3, 300, 0), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(2), 2, 300, 0), Ok(())); - assert_eq!(Council::leaderboard(), Some(vec![(300, 2), (300, 3), (400, 4), (600, 6)])); - assert_ok!(Council::end_block(System::block_number())); - - assert_eq!(Council::active_council(), vec![(6, 11), (4, 11)]); - - System::set_block_number(12); - assert_ok!(Council::retract_voter(Origin::signed(6), 0)); - assert_ok!(Council::retract_voter(Origin::signed(4), 1)); - assert_ok!(Council::submit_candidacy(Origin::signed(6), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(4), 1)); - assert_ok!(Council::set_approvals(Origin::signed(6), vec![true, false, false, false], 1, 0)); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![false, true, false, false], 1, 1)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(14); - assert!(Council::presentation_active()); - assert_eq!(Council::present_winner(Origin::signed(6), 6, 600, 1), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(4), 4, 400, 1), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(3), 3, 300 + Council::get_offset(300, 1), 1), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(2), 2, 300 + Council::get_offset(300, 1), 1), Ok(())); - assert_eq!(Council::leaderboard(), Some(vec![(400, 4), (588, 2), (588, 3), (600, 6)])); - assert_ok!(Council::end_block(System::block_number())); - - assert_eq!(Council::active_council(), vec![(6, 19), (3, 19)]); - - System::set_block_number(20); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(22); - // 2 will not get re-elected with 300 + 288, instead just 300. - // because one of 3's candidates (3) won in previous round - // 4 on the other hand will get extra weight since it was unlucky. - assert_eq!(Council::present_winner(Origin::signed(3), 2, 300, 2), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(4), 4, 400 + Council::get_offset(400, 1), 2), Ok(())); - assert_ok!(Council::end_block(System::block_number())); - - assert_eq!(Council::active_council(), vec![(4, 27), (2, 27)]); - }) - } - - #[test] - fn resubmitting_approvals_stores_pot() { - with_externalities(&mut ExtBuilder::default() - .voter_bond(0) - .voting_fee(0) - .balance_factor(10) - .build(), - || { System::set_block_number(4); - assert!(!Council::presentation_active()); - - assert_ok!(Council::submit_candidacy(Origin::signed(6), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 1)); - assert_ok!(Council::submit_candidacy(Origin::signed(1), 2)); - - assert_ok!(Council::set_approvals(Origin::signed(6), vec![true, false, false], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, true, false], 0, 1)); - assert_ok!(Council::set_approvals(Origin::signed(1), vec![false, false, true], 0, 2)); - - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert!(Council::presentation_active()); - - assert_eq!(Council::present_winner(Origin::signed(6), 6, 600, 0), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(5), 5, 500, 0), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(1), 1, 100, 0), Ok(())); - assert_eq!(Council::leaderboard(), Some(vec![(0, 0), (100, 1), (500, 5), (600, 6)])); - assert_ok!(Council::end_block(System::block_number())); - - assert_eq!(Council::active_council(), vec![(6, 11), (5, 11)]); - - System::set_block_number(12); - assert_ok!(Council::retract_voter(Origin::signed(6), 0)); - assert_ok!(Council::retract_voter(Origin::signed(5), 1)); - assert_ok!(Council::submit_candidacy(Origin::signed(6), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 1)); - assert_ok!(Council::set_approvals(Origin::signed(6), vec![true, false, false], 1, 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, true, false], 1, 1)); - // give 1 some new high balance - let _ = Balances::make_free_balance_be(&1, 997); - assert_ok!(Council::set_approvals(Origin::signed(1), vec![false, false, true], 1, 2)); - assert_eq!(Council::voter_info(1).unwrap(), - VoterInfo { - stake: 1000, // 997 + 3 which is candidacy bond. - pot: Council::get_offset(100, 1), - last_active: 1, - last_win: 1, - } - ); - assert_ok!(Council::end_block(System::block_number())); - - assert_eq!(Council::active_council(), vec![(6, 11), (5, 11)]); - - System::set_block_number(14); - assert!(Council::presentation_active()); - assert_eq!(Council::present_winner(Origin::signed(6), 6, 600, 1), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(5), 5, 500, 1), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(1), 1, 1000 + 96 /* pot */, 1), Ok(())); - assert_eq!(Council::leaderboard(), Some(vec![(0, 0), (500, 5), (600, 6), (1096, 1)])); - assert_ok!(Council::end_block(System::block_number())); - - assert_eq!(Council::active_council(), vec![(1, 19), (6, 19)]); - }) - } - - #[test] - fn get_offset_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { - assert_eq!(Council::get_offset(100, 0), 0); - assert_eq!(Council::get_offset(100, 1), 96); - assert_eq!(Council::get_offset(100, 2), 96 + 93); - assert_eq!(Council::get_offset(100, 3), 96 + 93 + 90); - assert_eq!(Council::get_offset(100, 4), 96 + 93 + 90 + 87); - // limit - assert_eq!(Council::get_offset(100, 1000), 100 * 24); - - assert_eq!(Council::get_offset(50_000_000_000, 0), 0); - assert_eq!(Council::get_offset(50_000_000_000, 1), 48_000_000_000); - assert_eq!(Council::get_offset(50_000_000_000, 2), 48_000_000_000 + 46_080_000_000); - assert_eq!(Council::get_offset(50_000_000_000, 3), 48_000_000_000 + 46_080_000_000 + 44_236_800_000); - assert_eq!( - Council::get_offset(50_000_000_000, 4), - 48_000_000_000 + 46_080_000_000 + 44_236_800_000 + 42_467_328_000 - ); - // limit - assert_eq!(Council::get_offset(50_000_000_000, 1000), 50_000_000_000 * 24); - }) - } - - #[test] - fn get_offset_with_zero_decay() { - with_externalities(&mut ExtBuilder::default().decay_ratio(0).build(), || { - assert_eq!(Council::get_offset(100, 0), 0); - assert_eq!(Council::get_offset(100, 1), 0); - assert_eq!(Council::get_offset(100, 2), 0); - assert_eq!(Council::get_offset(100, 3), 0); - // limit - assert_eq!(Council::get_offset(100, 1000), 0); - }) - } - - #[test] - fn voting_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { - System::set_block_number(1); - - assert_ok!(Council::submit_candidacy(Origin::signed(5), 0)); - - assert_ok!(Council::set_approvals(Origin::signed(1), vec![true], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![true], 0, 1)); - - assert_eq!(Council::all_approvals_of(&1), vec![true]); - assert_eq!(Council::all_approvals_of(&4), vec![true]); - assert_eq!(voter_ids(), vec![1, 4]); - - assert_ok!(Council::submit_candidacy(Origin::signed(2), 1)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 2)); - - assert_ok!(Council::set_approvals(Origin::signed(2), vec![false, true, true], 0, 2)); - assert_ok!(Council::set_approvals(Origin::signed(3), vec![false, true, true], 0, 3)); - - assert_eq!(Council::all_approvals_of(&1), vec![true]); - assert_eq!(Council::all_approvals_of(&4), vec![true]); - assert_eq!(Council::all_approvals_of(&2), vec![false, true, true]); - assert_eq!(Council::all_approvals_of(&3), vec![false, true, true]); - - assert_eq!(voter_ids(), vec![1, 4, 2, 3]); - }); - } - - #[test] - fn proxy_voting_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { - System::set_block_number(1); - - assert_ok!(Council::submit_candidacy(Origin::signed(5), 0)); - - Democracy::force_proxy(1, 11); - Democracy::force_proxy(2, 12); - Democracy::force_proxy(3, 13); - Democracy::force_proxy(4, 14); - assert_ok!(Council::proxy_set_approvals(Origin::signed(11), vec![true], 0, 0)); - assert_ok!(Council::proxy_set_approvals(Origin::signed(14), vec![true], 0, 1)); - - assert_eq!(Council::all_approvals_of(&1), vec![true]); - assert_eq!(Council::all_approvals_of(&4), vec![true]); - assert_eq!(voter_ids(), vec![1, 4]); - - assert_ok!(Council::submit_candidacy(Origin::signed(2), 1)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 2)); - - assert_ok!(Council::proxy_set_approvals(Origin::signed(12), vec![false, true, true], 0, 2)); - assert_ok!(Council::proxy_set_approvals(Origin::signed(13), vec![false, true, true], 0, 3)); - - assert_eq!(Council::all_approvals_of(&1), vec![true]); - assert_eq!(Council::all_approvals_of(&4), vec![true]); - assert_eq!(Council::all_approvals_of(&2), vec![false, true, true]); - assert_eq!(Council::all_approvals_of(&3), vec![false, true, true]); - - assert_eq!(voter_ids(), vec![1, 4, 2, 3]); - }); - } - - #[test] - fn setting_any_approval_vote_count_without_any_candidate_count_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { - System::set_block_number(1); - - assert_eq!(Council::candidates().len(), 0); - - assert_noop!( - Council::set_approvals(Origin::signed(4), vec![], 0, 0), - "amount of candidates to receive approval votes should be non-zero" - ); - }); - } - - #[test] - fn setting_an_approval_vote_count_more_than_candidate_count_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { - System::set_block_number(1); - - assert_ok!(Council::submit_candidacy(Origin::signed(5), 0)); - assert_eq!(Council::candidates().len(), 1); - - assert_noop!( - Council::set_approvals(Origin::signed(4),vec![true, true], 0, 0), - "amount of candidate votes cannot exceed amount of candidates" - ); - }); - } - - #[test] - fn resubmitting_voting_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { - System::set_block_number(1); - - assert_ok!(Council::submit_candidacy(Origin::signed(5), 0)); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![true], 0, 0)); - - assert_eq!(Council::all_approvals_of(&4), vec![true]); - - assert_ok!(Council::submit_candidacy(Origin::signed(2), 1)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 2)); - assert_eq!(Council::candidates().len(), 3); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![true, false, true], 0, 0)); - - assert_eq!(Council::all_approvals_of(&4), vec![true, false, true]); - }); - } - - #[test] - fn retracting_voter_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { - System::set_block_number(1); - - assert_ok!(Council::submit_candidacy(Origin::signed(5), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 1)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 2)); - assert_eq!(Council::candidates().len(), 3); - - assert_ok!(Council::set_approvals(Origin::signed(1), vec![true], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![false, true, true], 0, 1)); - assert_ok!(Council::set_approvals(Origin::signed(3), vec![false, true, true], 0, 2)); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![true, false, true], 0, 3)); - - assert_eq!(voter_ids(), vec![1, 2, 3, 4]); - assert_eq!(Council::all_approvals_of(&1), vec![true]); - assert_eq!(Council::all_approvals_of(&2), vec![false, true, true]); - assert_eq!(Council::all_approvals_of(&3), vec![false, true, true]); - assert_eq!(Council::all_approvals_of(&4), vec![true, false, true]); - - assert_ok!(Council::retract_voter(Origin::signed(1), 0)); - - assert_eq!(voter_ids(), vec![0, 2, 3, 4]); - assert_eq!(Council::all_approvals_of(&1), Vec::::new()); - assert_eq!(Council::all_approvals_of(&2), vec![false, true, true]); - assert_eq!(Council::all_approvals_of(&3), vec![false, true, true]); - assert_eq!(Council::all_approvals_of(&4), vec![true, false, true]); - - assert_ok!(Council::retract_voter(Origin::signed(2), 1)); - - assert_eq!(voter_ids(), vec![0, 0, 3, 4]); - assert_eq!(Council::all_approvals_of(&1), Vec::::new()); - assert_eq!(Council::all_approvals_of(&2), Vec::::new()); - assert_eq!(Council::all_approvals_of(&3), vec![false, true, true]); - assert_eq!(Council::all_approvals_of(&4), vec![true, false, true]); - - assert_ok!(Council::retract_voter(Origin::signed(3), 2)); - - assert_eq!(voter_ids(), vec![0, 0, 0, 4]); - assert_eq!(Council::all_approvals_of(&1), Vec::::new()); - assert_eq!(Council::all_approvals_of(&2), Vec::::new()); - assert_eq!(Council::all_approvals_of(&3), Vec::::new()); - assert_eq!(Council::all_approvals_of(&4), vec![true, false, true]); - }); - } - - #[test] - fn invalid_retraction_index_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { - System::set_block_number(1); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 0)); - assert_ok!(Council::set_approvals(Origin::signed(1), vec![true], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true], 0, 0)); - assert_eq!(voter_ids(), vec![1, 2]); - assert_noop!(Council::retract_voter(Origin::signed(1), 1), "retraction index mismatch"); - }); - } - - #[test] - fn overflow_retraction_index_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { - System::set_block_number(1); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 0)); - assert_ok!(Council::set_approvals(Origin::signed(1), vec![true], 0, 0)); - assert_noop!(Council::retract_voter(Origin::signed(1), 1), "retraction index invalid"); - }); - } - - #[test] - fn non_voter_retraction_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { - System::set_block_number(1); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 0)); - assert_ok!(Council::set_approvals(Origin::signed(1), vec![true], 0, 0)); - assert_noop!(Council::retract_voter(Origin::signed(2), 0), "cannot retract non-voter"); - }); - } - - #[test] - fn approval_storage_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 1)); - - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true, false], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(3), vec![false, false], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![], 0, 0)); - - assert_eq!(Council::all_approvals_of(&2), vec![true]); - // NOTE: these two are stored in mem differently though. - assert_eq!(Council::all_approvals_of(&3), vec![]); - assert_eq!(Council::all_approvals_of(&4), vec![]); - - assert_eq!(Council::approvals_of((3, 0)), vec![0]); - assert_eq!(Council::approvals_of((4, 0)), vec![]); - }); - } - - #[test] - fn simple_tally_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { - System::set_block_number(4); - assert!(!Council::presentation_active()); - - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 1)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, true], 0, 0)); - assert_eq!(voter_ids(), vec![2, 5]); - assert_eq!(Council::all_approvals_of(&2), vec![true]); - assert_eq!(Council::all_approvals_of(&5), vec![false, true]); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert!(Council::presentation_active()); - assert_eq!(Council::present_winner(Origin::signed(4), 2, 20, 0), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(4), 5, 50, 0), Ok(())); - assert_eq!(Council::leaderboard(), Some(vec![(0, 0), (0, 0), (20, 2), (50, 5)])); - - assert_ok!(Council::end_block(System::block_number())); - - assert!(!Council::presentation_active()); - assert_eq!(Council::active_council(), vec![(5, 11), (2, 11)]); - - assert!(!Council::is_a_candidate(&2)); - assert!(!Council::is_a_candidate(&5)); - assert_eq!(Council::vote_index(), 1); - assert_eq!(Council::voter_info(2), Some(VoterInfo { last_win: 1, last_active: 0, stake: 20, pot: 0 })); - assert_eq!(Council::voter_info(5), Some(VoterInfo { last_win: 1, last_active: 0, stake: 50, pot: 0 })); - }); - } - - #[test] - fn seats_should_be_released() { - with_externalities(&mut ExtBuilder::default().build(), || { - System::set_block_number(4); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 1)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true, false], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, true], 0, 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert!(Council::presentation_active()); - assert_eq!(Council::present_winner(Origin::signed(4), 2, 20, 0), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(4), 5, 50, 0), Ok(())); - assert_eq!(Council::leaderboard(), Some(vec![(0, 0), (0, 0), (20, 2), (50, 5)])); - assert_ok!(Council::end_block(System::block_number())); - - assert_eq!(Council::active_council(), vec![(5, 11), (2, 11)]); - let mut current = System::block_number(); - let free_block; - loop { - current += 1; - System::set_block_number(current); - assert_ok!(Council::end_block(System::block_number())); - if Council::active_council().len() == 0 { - free_block = current; - break; - } - } - // 11 + 2 which is the next voting period. - assert_eq!(free_block, 14); - }); - } - - #[test] - fn presentations_with_zero_staked_deposit_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { - System::set_block_number(4); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true], 0, 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert_noop!( - Council::present_winner(Origin::signed(4), 2, 0, 0), - "stake deposited to present winner and be added to leaderboard should be non-zero" - ); - }); - } - - #[test] - fn double_presentations_should_be_punished() { - with_externalities(&mut ExtBuilder::default().build(), || { - assert!(Balances::can_slash(&4, 10)); - - System::set_block_number(4); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 1)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true, false], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, true], 0, 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert_ok!(Council::present_winner(Origin::signed(4), 2, 20, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 5, 50, 0)); - assert_eq!(Council::present_winner(Origin::signed(4), 5, 50, 0), Err("duplicate presentation")); - assert_ok!(Council::end_block(System::block_number())); - - assert_eq!(Council::active_council(), vec![(5, 11), (2, 11)]); - assert_eq!(Balances::total_balance(&4), 38); - }); - } - - #[test] - fn retracting_inactive_voter_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { - System::set_block_number(4); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true], 0, 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert_ok!(Council::present_winner(Origin::signed(4), 2, 20, 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(8); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![true], 1, 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(10); - assert_ok!(Council::present_winner(Origin::signed(4), 5, 50, 1)); - assert_ok!(Council::end_block(System::block_number())); - - assert_ok!(Council::reap_inactive_voter(Origin::signed(5), - (voter_ids().iter().position(|&i| i == 5).unwrap() as u32).into(), - 2, (voter_ids().iter().position(|&i| i == 2).unwrap() as u32).into(), - 2 - )); - - assert_eq!(voter_ids(), vec![0, 5]); - assert_eq!(Council::all_approvals_of(&2).len(), 0); - assert_eq!(Balances::total_balance(&2), 20); - assert_eq!(Balances::total_balance(&5), 50); - }); - } - - #[test] - fn presenting_for_double_election_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { - System::set_block_number(4); - assert_eq!(Council::submit_candidacy(Origin::signed(2), 0), Ok(())); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true], 0, 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert_ok!(Council::present_winner(Origin::signed(4), 2, 20, 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(8); - // NOTE: This is now mandatory to disable the lock - assert_ok!(Council::retract_voter(Origin::signed(2), 0)); - assert_eq!(Council::submit_candidacy(Origin::signed(2), 0), Ok(())); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true], 1, 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(10); - assert_noop!( - Council::present_winner(Origin::signed(4), 2, 20, 1), - "candidate must not form a duplicated member if elected" - ); - }); - } - - #[test] - fn retracting_inactive_voter_with_other_candidates_in_slots_should_work() { - with_externalities(&mut ExtBuilder::default().voter_bond(2).build(), || { - System::set_block_number(4); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true], 0, 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert_ok!(Council::present_winner(Origin::signed(4), 2, 20, 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(8); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![true], 1, 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(10); - assert_ok!(Council::present_winner(Origin::signed(4), 5, 50, 1)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(11); - assert_ok!(Council::submit_candidacy(Origin::signed(1), 0)); - - assert_ok!(Council::reap_inactive_voter(Origin::signed(5), - (voter_ids().iter().position(|&i| i == 5).unwrap() as u32).into(), - 2, (voter_ids().iter().position(|&i| i == 2).unwrap() as u32).into(), - 2 - )); - - assert_eq!(voter_ids(), vec![0, 5]); - assert_eq!(Council::all_approvals_of(&2).len(), 0); - assert_eq!(Balances::total_balance(&2), 18); - assert_eq!(Balances::total_balance(&5), 52); - }); - } - - #[test] - fn retracting_inactive_voter_with_bad_reporter_index_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { - System::set_block_number(4); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true], 0, 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert_ok!(Council::present_winner(Origin::signed(4), 2, 20, 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(8); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![true], 1, 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(10); - assert_ok!(Council::present_winner(Origin::signed(4), 5, 50, 1)); - assert_ok!(Council::end_block(System::block_number())); - - assert_noop!(Council::reap_inactive_voter(Origin::signed(2), - 42, - 2, (voter_ids().iter().position(|&i| i == 2).unwrap() as u32).into(), - 2 - ), "invalid reporter index"); - }); - } - - #[test] - fn retracting_inactive_voter_with_bad_target_index_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { - System::set_block_number(4); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true], 0, 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert_ok!(Council::present_winner(Origin::signed(4), 2, 20, 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(8); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![true], 1, 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(10); - assert_ok!(Council::present_winner(Origin::signed(4), 5, 50, 1)); - assert_ok!(Council::end_block(System::block_number())); - - assert_noop!(Council::reap_inactive_voter(Origin::signed(2), - (voter_ids().iter().position(|&i| i == 2).unwrap() as u32).into(), - 2, 42, - 2 - ), "invalid target index"); - }); - } - - #[test] - fn attempting_to_retract_active_voter_should_slash_reporter() { - with_externalities(&mut ExtBuilder::default().build(), || { - System::set_block_number(4); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 1)); - assert_ok!(Council::submit_candidacy(Origin::signed(4), 2)); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 3)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true, false, false, false], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(3), vec![false, true, false, false], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![false, false, true, false], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, false, false, true], 0, 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert_ok!(Council::present_winner(Origin::signed(4), 2, 20, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 3, 30, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 4, 40, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 5, 50, 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(8); - assert_ok!(Council::set_desired_seats(3)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(10); - assert_ok!(Council::present_winner(Origin::signed(4), 2, 20 + Council::get_offset(20, 1), 1)); - assert_ok!(Council::present_winner(Origin::signed(4), 3, 30 + Council::get_offset(30, 1), 1)); - assert_ok!(Council::end_block(System::block_number())); - - assert_eq!(Council::vote_index(), 2); - assert_eq!(Council::inactivity_grace_period(), 1); - assert_eq!(Council::voting_period(), 4); - assert_eq!(Council::voter_info(4), Some(VoterInfo { last_win: 1, last_active: 0, stake: 40, pot: 0 })); - - assert_ok!(Council::reap_inactive_voter(Origin::signed(4), - (voter_ids().iter().position(|&i| i == 4).unwrap() as u32).into(), - 2, - (voter_ids().iter().position(|&i| i == 2).unwrap() as u32).into(), - 2 - )); - - assert_eq!(voter_ids(), vec![2, 3, 0, 5]); - assert_eq!(Council::all_approvals_of(&4).len(), 0); - assert_eq!(Balances::total_balance(&4), 40); - }); - } - - #[test] - fn attempting_to_retract_inactive_voter_by_nonvoter_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { - System::set_block_number(4); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true], 0, 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert_ok!(Council::present_winner(Origin::signed(4), 2, 20, 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(8); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![true], 1, 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(10); - assert_ok!(Council::present_winner(Origin::signed(4), 5, 50, 1)); - assert_ok!(Council::end_block(System::block_number())); - - assert_noop!(Council::reap_inactive_voter(Origin::signed(4), - 0, - 2, (voter_ids().iter().position(|&i| i == 2).unwrap() as u32).into(), - 2 - ), "reporter must be a voter"); - }); - } - - #[test] - fn presenting_loser_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { - System::set_block_number(4); - assert_ok!(Council::submit_candidacy(Origin::signed(1), 0)); - assert_ok!(Council::set_approvals(Origin::signed(6), vec![true], 0, 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 1)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![false, true], 0, 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 2)); - assert_ok!(Council::set_approvals(Origin::signed(3), vec![false, false, true], 0, 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(4), 3)); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![false, false, false, true], 0, 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 4)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, false, false, false, true], 0, 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert_ok!(Council::present_winner(Origin::signed(4), 1, 60, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 3, 30, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 4, 40, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 5, 50, 0)); - - assert_eq!(Council::leaderboard(), Some(vec![ - (30, 3), - (40, 4), - (50, 5), - (60, 1) - ])); - - assert_noop!(Council::present_winner(Origin::signed(4), 2, 20, 0), "candidate not worthy of leaderboard"); - }); - } - - #[test] - fn presenting_loser_first_should_not_matter() { - with_externalities(&mut ExtBuilder::default().build(), || { - System::set_block_number(4); - assert_ok!(Council::submit_candidacy(Origin::signed(1), 0)); - assert_ok!(Council::set_approvals(Origin::signed(6), vec![true], 0, 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 1)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![false, true], 0, 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 2)); - assert_ok!(Council::set_approvals(Origin::signed(3), vec![false, false, true], 0, 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(4), 3)); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![false, false, false, true], 0, 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 4)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, false, false, false, true], 0, 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert_ok!(Council::present_winner(Origin::signed(4), 2, 20, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 1, 60, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 3, 30, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 4, 40, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 5, 50, 0)); - - assert_eq!(Council::leaderboard(), Some(vec![ - (30, 3), - (40, 4), - (50, 5), - (60, 1) - ])); - }); - } - - #[test] - fn present_outside_of_presentation_period_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { - System::set_block_number(4); - assert!(!Council::presentation_active()); - assert_noop!( - Council::present_winner(Origin::signed(5), 5, 1, 0), - "cannot present outside of presentation period" - ); - }); - } - - #[test] - fn present_with_invalid_vote_index_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { - System::set_block_number(4); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 1)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true, false], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, true], 0, 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert_noop!(Council::present_winner(Origin::signed(4), 2, 20, 1), "index not current"); - }); - } - - #[test] - fn present_when_presenter_is_poor_should_not_work() { - let test_present = |p| { - with_externalities(&mut 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!(!Council::presentation_active()); - - assert_ok!(Council::submit_candidacy(Origin::signed(1), 0)); // -3 - assert_eq!(Balances::free_balance(&1), 12); - assert_ok!(Council::set_approvals(Origin::signed(1), vec![true], 0, 0)); // -2 -5 - assert_ok!(Council::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!(Council::present_winner( - Origin::signed(1), 1, 10, 0), - "presenter must have sufficient slashable funds" - ); - } else { - assert_ok!(Council::present_winner(Origin::signed(1), 1, 10, 0)); - } - }); - }; - test_present(4); - test_present(6); - } - - #[test] - fn invalid_present_tally_should_slash() { - with_externalities(&mut ExtBuilder::default().build(), || { - System::set_block_number(4); - assert!(!Council::presentation_active()); - assert_eq!(Balances::total_balance(&4), 40); - - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 1)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true, false], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, true], 0, 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert_err!(Council::present_winner(Origin::signed(4), 2, 80, 0), "incorrect total"); - - assert_eq!(Balances::total_balance(&4), 38); - }); - } - - #[test] - fn runners_up_should_be_kept() { - with_externalities(&mut ExtBuilder::default().build(), || { - System::set_block_number(4); - assert!(!Council::presentation_active()); - - assert_ok!(Council::submit_candidacy(Origin::signed(1), 0)); - assert_ok!(Council::set_approvals(Origin::signed(6), vec![true], 0, 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 1)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![false, true], 0, 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 2)); - assert_ok!(Council::set_approvals(Origin::signed(3), vec![false, false, true], 0, 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(4), 3)); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![false, false, false, true], 0, 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 4)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, false, false, false, true], 0, 0)); - - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert!(Council::presentation_active()); - assert_ok!(Council::present_winner(Origin::signed(4), 1, 60, 0)); - // leaderboard length is the empty seats plus the carry count (i.e. 5 + 2), where those - // to be carried are the lowest and stored in lowest indices - assert_eq!(Council::leaderboard(), Some(vec![ - (0, 0), - (0, 0), - (0, 0), - (60, 1) - ])); - assert_ok!(Council::present_winner(Origin::signed(4), 3, 30, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 4, 40, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 5, 50, 0)); - assert_eq!(Council::leaderboard(), Some(vec![ - (30, 3), - (40, 4), - (50, 5), - (60, 1) - ])); - - assert_ok!(Council::end_block(System::block_number())); - - assert!(!Council::presentation_active()); - assert_eq!(Council::active_council(), vec![(1, 11), (5, 11)]); - - assert!(!Council::is_a_candidate(&1)); - assert!(!Council::is_a_candidate(&5)); - assert!(!Council::is_a_candidate(&2)); - assert!(Council::is_a_candidate(&3)); - assert!(Council::is_a_candidate(&4)); - assert_eq!(Council::vote_index(), 1); - assert_eq!(Council::voter_info(2), Some(VoterInfo { last_win: 0, last_active: 0, stake: 20, pot: 0 })); - assert_eq!(Council::voter_info(3), Some(VoterInfo { last_win: 0, last_active: 0, stake: 30, pot: 0 })); - assert_eq!(Council::voter_info(4), Some(VoterInfo { last_win: 0, last_active: 0, stake: 40, pot: 0 })); - assert_eq!(Council::voter_info(5), Some(VoterInfo { last_win: 1, last_active: 0, stake: 50, pot: 0 })); - assert_eq!(Council::voter_info(6), Some(VoterInfo { last_win: 1, last_active: 0, stake: 60, pot: 0 })); - assert_eq!(Council::candidate_reg_info(3), Some((0, 2))); - assert_eq!(Council::candidate_reg_info(4), Some((0, 3))); - }); - } - - #[test] - fn second_tally_should_use_runners_up() { - with_externalities(&mut ExtBuilder::default().build(), || { - System::set_block_number(4); - assert_ok!(Council::submit_candidacy(Origin::signed(1), 0)); - assert_ok!(Council::set_approvals(Origin::signed(6), vec![true], 0, 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 1)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![false, true], 0, 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 2)); - assert_ok!(Council::set_approvals(Origin::signed(3), vec![false, false, true], 0, 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(4), 3)); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![false, false, false, true], 0, 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 4)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, false, false, false, true], 0, 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert_ok!(Council::present_winner(Origin::signed(4), 1, 60, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 3, 30, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 4, 40, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 5, 50, 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(8); - assert_ok!(Council::set_approvals(Origin::signed(6), vec![false, false, true, false], 1, 0)); - assert_ok!(Council::set_desired_seats(3)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(10); - assert_ok!(Council::present_winner(Origin::signed(4), 3, 30 + Council::get_offset(30, 1) + 60, 1)); - assert_ok!(Council::present_winner(Origin::signed(4), 4, 40 + Council::get_offset(40, 1), 1)); - assert_ok!(Council::end_block(System::block_number())); - - assert!(!Council::presentation_active()); - assert_eq!(Council::active_council(), vec![(1, 11), (5, 11), (3, 15)]); - - assert!(!Council::is_a_candidate(&1)); - assert!(!Council::is_a_candidate(&2)); - assert!(!Council::is_a_candidate(&3)); - assert!(!Council::is_a_candidate(&5)); - assert!(Council::is_a_candidate(&4)); - assert_eq!(Council::vote_index(), 2); - assert_eq!(Council::voter_info(2), Some( VoterInfo { last_win: 0, last_active: 0, stake: 20, pot: 0})); - assert_eq!(Council::voter_info(3), Some( VoterInfo { last_win: 2, last_active: 0, stake: 30, pot: 0})); - assert_eq!(Council::voter_info(4), Some( VoterInfo { last_win: 0, last_active: 0, stake: 40, pot: 0})); - assert_eq!(Council::voter_info(5), Some( VoterInfo { last_win: 1, last_active: 0, stake: 50, pot: 0})); - assert_eq!( - Council::voter_info(6), - Some(VoterInfo { last_win: 2, last_active: 1, stake: 60, pot: 0}) - ); - - assert_eq!(Council::candidate_reg_info(4), Some((0, 3))); - }); - } -} diff --git a/srml/democracy/Cargo.toml b/srml/democracy/Cargo.toml index 88d1da1a8cb24008e61a87d3b39323b0f1bd4460..e7b06ca5975c16c230fe5c2d5fdcfbba4b6f58d5 100644 --- a/srml/democracy/Cargo.toml +++ b/srml/democracy/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" [dependencies] serde = { version = "1.0", optional = true, features = ["derive"] } safe-mix = { version = "1.0", default-features = false} -parity-codec = { version = "3.3", default-features = false, features = ["derive"] } +parity-codec = { version = "4.1.1", 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 } primitives = { package = "sr-primitives", path = "../../core/sr-primitives", default-features = false } diff --git a/srml/democracy/src/lib.rs b/srml/democracy/src/lib.rs index 0fead411c8796d6c460c8f2a0c8f91557ff6e8a3..cb7f665b30cc5f2547b65b2896134e0f35bf5662 100644 --- a/srml/democracy/src/lib.rs +++ b/srml/democracy/src/lib.rs @@ -165,8 +165,15 @@ impl Decode for Vote { type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; +pub const DEFAULT_ENACTMENT_PERIOD: u32 = 0; +pub const DEFAULT_LAUNCH_PERIOD: u32 = 0; +pub const DEFAULT_VOTING_PERIOD: u32 = 0; +pub const DEFAULT_MINIMUM_DEPOSIT: u32 = 0; +pub const DEFAULT_EMERGENCY_VOTING_PERIOD: u32 = 0; +pub const DEFAULT_COOLOFF_PERIOD: u32 = 0; + pub trait Trait: system::Trait + Sized { - type Proposal: Parameter + Dispatchable + IsSubType>; + type Proposal: Parameter + Dispatchable + IsSubType, Self>; type Event: From> + Into<::Event>; /// Currency type for this module. @@ -197,6 +204,11 @@ pub trait Trait: system::Trait + Sized { /// a majority-carries referendum. type ExternalMajorityOrigin: EnsureOrigin; + /// Origin from which the next referendum proposed by the external majority may be immediately + /// tabled to vote asynchronously in a similar manner to the emergency origin. It remains a + /// majority-carries vote. + type ExternalPushOrigin: EnsureOrigin; + /// Origin from which emergency referenda may be scheduled. type EmergencyOrigin: EnsureOrigin; @@ -241,7 +253,6 @@ 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; /// The public proposals. Unsorted. @@ -318,6 +329,28 @@ decl_event!( decl_module! { pub struct Module for enum Call where origin: T::Origin { + /// The minimum period of locking and the period between a proposal being approved and enacted. + /// + /// It should generally be a little more than the unstake period to ensure that + /// voting stakers have an opportunity to remove themselves from the system in the case where + /// they are on the losing side of a vote. + const EnactmentPeriod: T::BlockNumber = T::EnactmentPeriod::get(); + + /// How often (in blocks) new public referenda are launched. + const LaunchPeriod: T::BlockNumber = T::LaunchPeriod::get(); + + /// How often (in blocks) to check for new votes. + const VotingPeriod: T::BlockNumber = T::VotingPeriod::get(); + + /// The minimum amount to be used as a deposit for a public referendum proposal. + const MinimumDeposit: BalanceOf = T::MinimumDeposit::get(); + + /// Minimum voting period allowed for an emergency referendum. + const EmergencyVotingPeriod: T::BlockNumber = T::EmergencyVotingPeriod::get(); + + /// Period in blocks where an external proposal may not be re-submitted after being vetoed. + const CooloffPeriod: T::BlockNumber = T::CooloffPeriod::get(); + fn deposit_event() = default; /// Propose a sensitive action to be taken. @@ -337,7 +370,7 @@ decl_module! { .map_err(|_| "proposer's balance too low")?; let index = Self::public_prop_count(); - >::put(index + 1); + PublicPropCount::put(index + 1); >::insert(index, (value, vec![who.clone()])); let mut props = Self::public_props(); @@ -415,12 +448,7 @@ decl_module! { // resubmission in the case of a mistakenly low `vote_period`; better to just let the // referendum take place with the lowest valid value. let period = voting_period.max(T::EmergencyVotingPeriod::get()); - Self::inject_referendum( - now + period, - *proposal, - threshold, - delay, - ).map(|_| ())?; + Self::inject_referendum(now + period, *proposal, threshold, delay).map(|_| ())?; } /// Schedule an emergency cancellation of a referendum. Cannot happen twice to the same @@ -460,6 +488,31 @@ decl_module! { >::put((*proposal, VoteThreshold::SimpleMajority)); } + /// Schedule the currently externally-proposed majority-carries referendum to be tabled + /// immediately. If there is no externally-proposed referendum currently, or if there is one + /// 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. + /// - `delay`: The number of block after voting has ended in approval and this should be + /// enacted. Increased to `EmergencyVotingPeriod` if too low. + fn external_push(origin, + proposal_hash: T::Hash, + voting_period: T::BlockNumber, + delay: T::BlockNumber + ) { + T::ExternalPushOrigin::ensure_origin(origin)?; + let (proposal, threshold) = >::get().ok_or("no proposal made")?; + ensure!(threshold == VoteThreshold::SimpleMajority, "next external proposal not simple majority"); + ensure!(proposal_hash == T::Hashing::hash_of(&proposal), "invalid hash"); + + >::kill(); + let now = >::block_number(); + // We don't consider it an error if `vote_period` is too low, like `emergency_propose`. + let period = voting_period.max(T::EmergencyVotingPeriod::get()); + Self::inject_referendum(now + period, proposal, threshold, delay).map(|_| ())?; + } + /// Veto and blacklist the external proposal hash. fn veto_external(origin, proposal_hash: T::Hash) { let who = T::VetoOrigin::ensure_origin(origin)?; @@ -485,16 +538,19 @@ decl_module! { } /// Remove a referendum. - fn cancel_referendum(#[compact] ref_index: ReferendumIndex) { + fn cancel_referendum(origin, #[compact] ref_index: ReferendumIndex) { + ensure_root(origin)?; Self::clear_referendum(ref_index); } /// Cancel a proposal queued for enactment. fn cancel_queued( + origin, #[compact] when: T::BlockNumber, #[compact] which: u32, #[compact] what: ReferendumIndex ) { + ensure_root(origin)?; let which = which as usize; let mut items = >::get(when); if items.get(which).and_then(Option::as_ref).map_or(false, |x| x.1 == what) { @@ -703,7 +759,7 @@ impl Module { >::insert(proxy, stash) } - /// Start a referendum. Can be called directly by the council. + /// Start a referendum. pub fn internal_start_referendum( proposal: T::Proposal, threshold: VoteThreshold, @@ -717,7 +773,7 @@ impl Module { ) } - /// Remove a referendum. Can be called directly by the council. + /// Remove a referendum. pub fn internal_cancel_referendum(ref_index: ReferendumIndex) { Self::deposit_event(RawEvent::Cancelled(ref_index)); >::clear_referendum(ref_index); @@ -751,7 +807,7 @@ impl Module { Err("Cannot inject a referendum that ends earlier than preceeding referendum")? } - >::put(ref_index + 1); + ReferendumCount::put(ref_index + 1); let item = ReferendumInfo { end, proposal, threshold, delay }; >::insert(ref_index, item); Self::deposit_event(RawEvent::Started(ref_index, threshold)); @@ -775,7 +831,7 @@ impl Module { /// Table the next waiting proposal for a vote. fn launch_next(now: T::BlockNumber) -> Result { - if >::take() { + if LastTabledWasExternal::take() { Self::launch_public(now).or_else(|_| Self::launch_external(now)) } else { Self::launch_external(now).or_else(|_| Self::launch_public(now)) @@ -785,7 +841,7 @@ impl Module { /// Table the waiting external proposal for a vote, if there is one. fn launch_external(now: T::BlockNumber) -> Result { if let Some((proposal, threshold)) = >::take() { - >::put(true); + LastTabledWasExternal::put(true); Self::deposit_event(RawEvent::ExternalTabled); Self::inject_referendum( now + T::VotingPeriod::get(), @@ -875,7 +931,7 @@ impl Module { } else { Self::deposit_event(RawEvent::NotPassed(index)); } - >::put(index + 1); + NextTally::put(index + 1); Ok(()) } @@ -917,9 +973,7 @@ mod tests { traits::Contains }; use substrate_primitives::{H256, Blake2Hasher}; - use primitives::BuildStorage; - use primitives::traits::{BlakeTwo256, IdentityLookup, Bounded}; - use primitives::testing::Header; + use primitives::{traits::{BlakeTwo256, IdentityLookup, Bounded}, testing::Header}; use balances::BalanceLock; use system::EnsureSignedBy; @@ -942,6 +996,11 @@ mod tests { // 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; + } impl system::Trait for Test { type Origin = Origin; type Index = u64; @@ -951,7 +1010,18 @@ mod tests { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; + type WeightMultiplierUpdate = (); type Event = (); + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; + } + 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; @@ -961,6 +1031,11 @@ mod tests { type TransactionPayment = (); type TransferPayment = (); type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type TransferFee = TransferFee; + type CreationFee = CreationFee; + type TransactionBaseFee = TransactionBaseFee; + type TransactionByteFee = TransactionByteFee; } parameter_types! { pub const LaunchPeriod: u64 = 2; @@ -993,24 +1068,20 @@ mod tests { type EmergencyOrigin = EnsureSignedBy; type ExternalOrigin = EnsureSignedBy; type ExternalMajorityOrigin = EnsureSignedBy; + type ExternalPushOrigin = EnsureSignedBy; type CancellationOrigin = EnsureSignedBy; type VetoOrigin = EnsureSignedBy; type CooloffPeriod = CooloffPeriod; } fn new_test_ext() -> runtime_io::TestExternalities { - let mut t = system::GenesisConfig::::default().build_storage().unwrap().0; - t.extend(balances::GenesisConfig::{ - transaction_base_fee: 0, - transaction_byte_fee: 0, + 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)], - existential_deposit: 0, - transfer_fee: 0, - creation_fee: 0, vesting: vec![], - }.build_storage().unwrap().0); - t.extend(GenesisConfig::::default().build_storage().unwrap().0); - runtime_io::TestExternalities::new(t) + }.assimilate_storage(&mut t.0, &mut t.1).unwrap(); + GenesisConfig::default().assimilate_storage(&mut t.0, &mut t.1).unwrap(); + runtime_io::TestExternalities::new_with_children(t) } type System = system::Module; @@ -1363,6 +1434,46 @@ mod tests { }); } + #[test] + fn external_push_referendum_works() { + with_externalities(&mut new_test_ext(), || { + System::set_block_number(0); + let h = BlakeTwo256::hash_of(&set_balance_proposal(2)); + assert_noop!(Democracy::external_push(Origin::signed(5), h, 3, 2), "no proposal made"); + assert_ok!(Democracy::external_propose_majority( + Origin::signed(3), + Box::new(set_balance_proposal(2)) + )); + assert_noop!(Democracy::external_push(Origin::signed(1), h, 3, 2), "Invalid origin"); + assert_ok!(Democracy::external_push(Origin::signed(5), h, 0, 0)); + assert_eq!( + Democracy::referendum_info(0), + Some(ReferendumInfo { + end: 1, + proposal: set_balance_proposal(2), + threshold: VoteThreshold::SimpleMajority, + delay: 0, + }) + ); + }); + } + + #[test] + fn external_push_referendum_fails_when_no_simple_majority() { + with_externalities(&mut new_test_ext(), || { + System::set_block_number(0); + let h = BlakeTwo256::hash_of(&set_balance_proposal(2)); + assert_ok!(Democracy::external_propose( + Origin::signed(2), + Box::new(set_balance_proposal(2)) + )); + assert_noop!( + Democracy::external_push(Origin::signed(5), h, 3, 2), + "next external proposal not simple majority" + ); + }); + } + #[test] fn locked_for_should_work() { with_externalities(&mut new_test_ext(), || { @@ -1440,9 +1551,9 @@ mod tests { Some((set_balance_proposal(2), 0)) ]); - assert_noop!(Democracy::cancel_queued(3, 0, 0), "proposal not found"); - assert_noop!(Democracy::cancel_queued(4, 1, 0), "proposal not found"); - assert_ok!(Democracy::cancel_queued(4, 0, 0)); + assert_noop!(Democracy::cancel_queued(Origin::ROOT, 3, 0, 0), "proposal not found"); + assert_noop!(Democracy::cancel_queued(Origin::ROOT, 4, 1, 0), "proposal not found"); + assert_ok!(Democracy::cancel_queued(Origin::ROOT, 4, 0, 0)); assert_eq!(Democracy::dispatch_queue(4), vec![None]); }); } @@ -1741,7 +1852,7 @@ mod tests { 0 ).unwrap(); assert_ok!(Democracy::vote(Origin::signed(1), r, AYE)); - assert_ok!(Democracy::cancel_referendum(r.into())); + assert_ok!(Democracy::cancel_referendum(Origin::ROOT, r.into())); next_block(); next_block(); diff --git a/srml/elections/Cargo.toml b/srml/elections/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..cd0a43aeb53945958fc7f860f2da54632dc817e8 --- /dev/null +++ b/srml/elections/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "srml-elections" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +serde = { version = "1.0", optional = true } +safe-mix = { version = "1.0", default-features = false} +parity-codec = { version = "4.1.1", default-features = false, features = ["derive"] } +substrate-primitives = { path = "../../core/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 } +primitives = { package = "sr-primitives", path = "../../core/sr-primitives", default-features = false } +srml-support = { path = "../support", default-features = false } +system = { package = "srml-system", path = "../system", default-features = false } + +[dev-dependencies] +hex-literal = "0.2.0" +balances = { package = "srml-balances", path = "../balances" } + +[features] +default = ["std"] +std = [ + "safe-mix/std", + "parity-codec/std", + "substrate-primitives/std", + "rstd/std", + "serde", + "runtime_io/std", + "srml-support/std", + "primitives/std", + "system/std", +] diff --git a/srml/elections/src/lib.rs b/srml/elections/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..9960ef22b70abd07ac358d1644f34303ad0246da --- /dev/null +++ b/srml/elections/src/lib.rs @@ -0,0 +1,2861 @@ +// 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 . + +//! Election module for stake-weighted membership selection of a collective. +//! +//! The composition of a set of account IDs works according to one or more approval votes +//! weighted by stake. There is a partial carry-over facility to give greater weight to those +//! whose voting is serially unsuccessful. + +#![cfg_attr(not(feature = "std"), no_std)] +#![recursion_limit="128"] + +use rstd::prelude::*; +use primitives::traits::{Zero, One, StaticLookup, Bounded, Saturating}; +use runtime_io::print; +use srml_support::{ + StorageValue, StorageMap, + dispatch::Result, decl_storage, decl_event, ensure, decl_module, + traits::{ + Currency, ExistenceRequirement, Get, LockableCurrency, LockIdentifier, + OnUnbalanced, ReservableCurrency, WithdrawReason, WithdrawReasons, ChangeMembers + } +}; +use parity_codec::{Encode, Decode}; +use system::{self, ensure_signed, ensure_root}; + +// no polynomial attacks: +// +// all unbonded public operations should be constant time. +// all other public operations must be linear time in terms of prior public operations and: +// - those "valid" ones that cost nothing be limited to a constant number per single protected +// operation +// - the rest costing the same order as the computational complexity +// all protected operations must complete in at most O(public operations) +// +// we assume "beneficial" transactions will have the same access as attack transactions. +// +// any storage requirements should be bonded by the same order as the volume. + +// public operations: +// - express approvals (you pay in a "voter" bond the first time you do this; O(1); one extra DB +// entry, one DB change) +// - remove active voter (you get your "voter" bond back; O(1); one fewer DB entry, one DB change) +// - remove inactive voter (either you or the target is removed; if the target, you get their +// "voter" bond back; O(1); one fewer DB entry, one DB change) +// - submit candidacy (you pay a "candidate" bond; O(1); one extra DB entry, two DB changes) +// - present winner/runner-up (you may pay a "presentation" bond of O(voters) if the presentation +// is invalid; O(voters) compute; ) protected operations: +// - remove candidacy (remove all votes for a candidate) (one fewer DB entry, two DB changes) + +// to avoid a potentially problematic case of not-enough approvals prior to voting causing a +// back-to-back votes that have no way of ending, then there's a forced grace period between votes. +// to keep the system as stateless as possible (making it a bit easier to reason about), we just +// restrict when votes can begin to blocks that lie on boundaries (`voting_period`). + +// for an approval vote of C members: + +// top K runners-up are maintained between votes. all others are discarded. +// - candidate removed & bond returned when elected. +// - candidate removed & bond burned when discarded. + +// at the point that the vote ends (), all voters' balances are snapshotted. + +// for B blocks following, there's a counting period whereby each of the candidates that believe +// they fall in the top K+C voted can present themselves. they get the total stake +// recorded (based on the snapshot); an ordered list is maintained (the leaderboard). Noone may +// present themselves that, if elected, would result in being included twice in the collective +// (important since existing members will have their approval votes as it may be that they +// don't get removed), nor if existing presenters would mean they're not in the top K+C. + +// following B blocks, the top C candidates are elected and have their bond returned. the top C +// candidates and all other candidates beyond the top C+K are cleared. + +// vote-clearing happens lazily; for an approval to count, the most recent vote at the time of the +// voter's most recent vote must be no later than the most recent vote at the time that the +// candidate in the approval position was registered there. as candidates are removed from the +// register and others join in their place, this prevents an approval meant for an earlier candidate +// being used to elect a new candidate. + +// the candidate list increases as needed, but the contents (though not really the capacity) reduce +// after each vote as all but K entries are cleared. newly registering candidates must use cleared +// 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))] +pub struct VoterInfo { + /// Last VoteIndex in which this voter assigned (or initialized) approvals. + last_active: VoteIndex, + /// Last VoteIndex in which one of this voter's approvals won. + /// Note that `last_win = N` indicates a last win at index `N-1`, hence `last_win = 0` means no + /// win ever. + last_win: VoteIndex, + /// The amount of stored weight as a result of not winning but changing approvals. + pot: Balance, + /// Current staked amount. A lock equal to this value always exists. + stake: Balance, +} + +/// Used to demonstrate the status of a particular index in the global voter list. +#[derive(PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(Debug))] +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. + Head, + /// Already occupied by another voter. Voting fee is applied. + Occupied, + /// Empty hole which should be filled. No fee will be applied. + Hole, +} + +const MODULE_ID: LockIdentifier = *b"py/elect"; + +pub const VOTER_SET_SIZE: usize = 64; +pub const APPROVAL_SET_SIZE: usize = 8; + +type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; +type NegativeImbalanceOf = + <::Currency as Currency<::AccountId>>::NegativeImbalance; + +type SetIndex = u32; +pub type VoteIndex = u32; + +// all three must be in sync. +type ApprovalFlag = u32; +pub const APPROVAL_FLAG_MASK: ApprovalFlag = 0x8000_0000; +pub const APPROVAL_FLAG_LEN: usize = 32; + +pub const DEFAULT_CANDIDACY_BOND: u32 = 9; +pub const DEFAULT_VOTING_BOND: u32 = 0; +pub const DEFAULT_VOTING_FEE: u32 = 0; +pub const DEFAULT_PRESENT_SLASH_PER_VOTER: u32 = 1; +pub const DEFAULT_CARRY_COUNT: u32 = 2; +pub const DEFAULT_INACTIVE_GRACE_PERIOD: u32 = 1; +pub const DEFAULT_VOTING_PERIOD: u32 = 1000; +pub const DEFAULT_DECAY_RATIO: u32 = 24; + +pub trait Trait: system::Trait { + type Event: From> + Into<::Event>; + + /// The currency that people are electing with. + type Currency: + LockableCurrency + + ReservableCurrency; + + /// Handler for the unbalanced reduction when slashing a validator. + type BadPresentation: OnUnbalanced>; + + /// Handler for the unbalanced reduction when slashing an invalid reaping attempt. + type BadReaper: OnUnbalanced>; + + /// Handler for the unbalanced reduction when submitting a bad `voter_index`. + type BadVoterIndex: OnUnbalanced>; + + /// Handler for the unbalanced reduction when a candidate has lost (and is not a runner up) + type LoserCandidate: OnUnbalanced>; + + /// What to do when the members change. + type ChangeMembers: ChangeMembers; + + /// How much should be locked up in order to submit one's candidacy. A reasonable + /// default value is 9. + type CandidacyBond: Get>; + + /// How much should be locked up in order to be able to submit votes. + type VotingBond: Get>; + + /// The amount of fee paid upon each vote submission, unless if they submit a + /// _hole_ index and replace it. + type VotingFee: Get>; + + /// The punishment, per voter, if you provide an invalid presentation. A + /// reasonable default value is 1. + type PresentSlashPerVoter: Get>; + + /// How many runners-up should have their approvals persist until the next + /// vote. A reasonable default value is 2. + type CarryCount: Get; + + /// How many vote indices need to go by after a target voter's last vote before + /// they can be reaped if their approvals are moot. A reasonable default value + /// is 1. + type InactiveGracePeriod: Get; + + /// How often (in blocks) to check for new votes. A reasonable default value + /// is 1000. + type VotingPeriod: Get; + + /// Decay factor of weight when being accumulated. It should typically be set to + /// __at least__ `membership_size -1` to keep the collective secure. + /// When set to `N`, it indicates `(1/N)^t` of staked is decayed at weight + /// increment step `t`. 0 will result in no weight being added at all (normal + /// approval voting). A reasonable default value is 24. + type DecayRatio: Get; +} + +decl_storage! { + trait Store for Module as Council { + // ---- parameters + /// How long to give each top candidate to present themselves after the vote ends. + pub PresentationDuration get(presentation_duration) config(): T::BlockNumber; + /// How long each position is active for. + pub TermDuration get(term_duration) config(): T::BlockNumber; + /// Number of accounts that should constitute the collective. + pub DesiredSeats get(desired_seats) config(): u32; + + // ---- permanent state (always relevant, changes only at the finalization of voting) + /// The current membership. When there's a vote going on, this should still be used for 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)>; + /// The total number of vote rounds that have happened or are in progress. + pub VoteCount get(vote_index): VoteIndex; + + // ---- persistent state (always relevant, changes constantly) + /// A list of votes for each voter. The votes are stored as numeric values and parsed in a 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; + /// 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)>; + /// Basic information about a voter. + pub VoterInfoOf get(voter_info): map T::AccountId => Option>>; + /// The present voter list (chunked and capped at [`VOTER_SET_SIZE`]). + pub Voters get(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; + /// Current number of Voters. + pub VoterCount get(voter_count): SetIndex = 0; + /// The present candidate list. + pub Candidates get(candidates): Vec; // has holes + /// Current number of active candidates + pub CandidateCount get(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)>; + /// 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. + pub Leaderboard get(leaderboard): Option, T::AccountId)> >; // ORDERED low -> high + + /// 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; + } +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + /// How much should be locked up in order to submit one's candidacy. A reasonable + /// default value is 9. + const CandidacyBond: BalanceOf = T::CandidacyBond::get(); + + /// How much should be locked up in order to be able to submit votes. + const VotingBond: BalanceOf = T::VotingBond::get(); + + /// The amount of fee paid upon each vote submission, unless if they submit a + /// _hole_ index and replace it. + const VotingFee: BalanceOf = T::VotingFee::get(); + + /// The punishment, per voter, if you provide an invalid presentation. A + /// reasonable default value is 1. + const PresentSlashPerVoter: BalanceOf = T::PresentSlashPerVoter::get(); + + /// How many runners-up should have their approvals persist until the next + /// vote. A reasonable default value is 2. + const CarryCount: u32 = T::CarryCount::get(); + + /// How many vote indices need to go by after a target voter's last vote before + /// they can be reaped if their approvals are moot. A reasonable default value + /// is 1. + const InactiveGracePeriod: VoteIndex = T::InactiveGracePeriod::get(); + + /// How often (in blocks) to check for new votes. A reasonable default value + /// is 1000. + const VotingPeriod: T::BlockNumber = T::VotingPeriod::get(); + + /// Decay factor of weight when being accumulated. It should typically be set to + /// __at least__ `membership_size -1` to keep the collective secure. + /// When set to `N`, it indicates `(1/N)^t` of staked is decayed at weight + /// increment step `t`. 0 will result in no weight being added at all (normal + /// approval voting). A reasonable default value is 24. + const DecayRatio: u32 = T::DecayRatio::get(); + + /// The chunk size of the voter vector. + const VOTER_SET_SIZE: u32 = VOTER_SET_SIZE as u32; + /// The chunk size of the approval vector. + const APPROVAL_SET_SIZE: u32 = APPROVAL_SET_SIZE as u32; + + fn deposit_event() = default; + + /// Set candidate approvals. Approval slots stay valid as long as candidates in those slots + /// are registered. + /// + /// Locks the total balance of caller indefinitely. + /// Only [`retract_voter`] or [`reap_inactive_voter`] can unlock the balance. + /// + /// `hint` argument is interpreted differently based on: + /// - if `origin` is setting approvals for the first time: The index will be checked + /// for being a valid _hole_ in the voter list. + /// - if the hint is correctly pointing to a hole, no fee is deducted from `origin`. + /// - Otherwise, the call will succeed but the index is ignored and simply a push to the last chunk + /// with free space happens. If the new push causes a new chunk to be created, a fee indicated by + /// [`VotingFee`] is deducted. + /// - if `origin` is already a voter: the index __must__ be valid and point to the correct + /// position of the `origin` in the current voters list. + /// + /// Note that any trailing `false` votes in `votes` is ignored; In approval voting, not voting for a candidate + /// and voting false, are equal. + /// + /// # + /// - O(1). + /// - Two extra DB entries, one DB change. + /// - Argument `votes` is limited in length to number of candidates. + /// # + fn set_approvals(origin, votes: Vec, #[compact] index: VoteIndex, hint: SetIndex) -> Result { + let who = ensure_signed(origin)?; + Self::do_set_approvals(who, votes, index, hint) + } + + /// Set candidate approvals from a proxy. Approval slots stay valid as long as candidates in those slots + /// are registered. + /// + /// # + /// - Same as `set_approvals` with one additional storage read. + /// # + fn proxy_set_approvals(origin, + votes: Vec, + #[compact] index: VoteIndex, + hint: SetIndex + ) -> Result { + let who = Self::proxy(ensure_signed(origin)?).ok_or("not a proxy")?; + Self::do_set_approvals(who, votes, index, hint) + } + + /// Remove a voter. For it not to be a bond-consuming no-op, all approved candidate indices + /// must now be either unregistered or registered to a candidate that registered the slot after + /// the voter gave their last approval set. + /// + /// Both indices must be provided as explained in [`voter_at`] function. + /// + /// May be called by anyone. Returns the voter deposit to `signed`. + /// + /// # + /// - O(1). + /// - Two fewer DB entries, one DB change. + /// # + fn reap_inactive_voter( + origin, + #[compact] reporter_index: u32, + who: ::Source, + #[compact] who_index: u32, + #[compact] assumed_vote_index: VoteIndex + ) { + let reporter = ensure_signed(origin)?; + let who = T::Lookup::lookup(who)?; + + ensure!(!Self::presentation_active(), "cannot reap during presentation period"); + ensure!(Self::voter_info(&reporter).is_some(), "reporter must be a voter"); + + let info = Self::voter_info(&who).ok_or("target for inactivity cleanup must be active")?; + let last_active = info.last_active; + + ensure!(assumed_vote_index == Self::vote_index(), "vote index not current"); + ensure!( + assumed_vote_index > last_active + T::InactiveGracePeriod::get(), + "cannot reap during grace period" + ); + + let reporter_index = reporter_index as usize; + let who_index = who_index as usize; + let assumed_reporter = Self::voter_at(reporter_index).ok_or("invalid reporter index")?; + let assumed_who = Self::voter_at(who_index).ok_or("invalid target index")?; + + ensure!(assumed_reporter == reporter, "bad reporter index"); + ensure!(assumed_who == who, "bad target index"); + + // will definitely kill one of reporter or who now. + + let valid = !Self::all_approvals_of(&who).iter() + .zip(Self::candidates().iter()) + .any(|(&appr, addr)| + appr && + *addr != T::AccountId::default() && + // defensive only: all items in candidates list are registered + Self::candidate_reg_info(addr).map_or(false, |x| x.0 <= last_active) + ); + + Self::remove_voter( + if valid { &who } else { &reporter }, + if valid { who_index } else { reporter_index } + ); + + T::Currency::remove_lock( + MODULE_ID, + if valid { &who } else { &reporter } + ); + + if valid { + // This only fails if `reporter` doesn't exist, which it clearly must do since its the origin. + // Still, it's no more harmful to propagate any error at this point. + T::Currency::repatriate_reserved(&who, &reporter, T::VotingBond::get())?; + Self::deposit_event(RawEvent::VoterReaped(who, reporter)); + } else { + let imbalance = T::Currency::slash_reserved(&reporter, T::VotingBond::get()).0; + T::BadReaper::on_unbalanced(imbalance); + Self::deposit_event(RawEvent::BadReaperSlashed(reporter)); + } + } + + /// Remove a voter. All votes are cancelled and the voter deposit is returned. + /// + /// The index must be provided as explained in [`voter_at`] function. + /// + /// Also removes the lock on the balance of the voter. See [`do_set_approvals()`]. + /// + /// # + /// - O(1). + /// - Two fewer DB entries, one DB change. + /// # + fn retract_voter(origin, #[compact] index: u32) { + let who = ensure_signed(origin)?; + + ensure!(!Self::presentation_active(), "cannot retract when presenting"); + ensure!(>::exists(&who), "cannot retract non-voter"); + let index = index as usize; + let voter = Self::voter_at(index).ok_or("retraction index invalid")?; + ensure!(voter == who, "retraction index mismatch"); + + Self::remove_voter(&who, index); + T::Currency::unreserve(&who, T::VotingBond::get()); + T::Currency::remove_lock(MODULE_ID, &who); + } + + /// Submit oneself for candidacy. + /// + /// Account must have enough transferrable funds in it to pay the bond. + /// + /// NOTE: if `origin` has already assigned approvals via [`set_approvals`], + /// it will NOT have any usable funds to pass candidacy bond and must first retract. + /// Note that setting approvals will lock the entire balance of the voter until + /// retraction or being reported. + /// + /// # + /// - Independent of input. + /// - Three DB changes. + /// # + fn submit_candidacy(origin, #[compact] slot: u32) { + let who = ensure_signed(origin)?; + + ensure!(!Self::is_a_candidate(&who), "duplicate candidate submission"); + let slot = slot as usize; + let count = Self::candidate_count() as usize; + let candidates = Self::candidates(); + ensure!( + (slot == count && count == candidates.len()) || + (slot < candidates.len() && candidates[slot] == T::AccountId::default()), + "invalid candidate slot" + ); + // NOTE: This must be last as it has side-effects. + T::Currency::reserve(&who, T::CandidacyBond::get()) + .map_err(|_| "candidate has not enough funds")?; + + >::insert(&who, (Self::vote_index(), slot as u32)); + let mut candidates = candidates; + if slot == candidates.len() { + candidates.push(who); + } else { + candidates[slot] = who; + } + >::put(candidates); + CandidateCount::put(count as u32 + 1); + } + + /// Claim that `signed` is one of the top Self::carry_count() + current_vote().1 candidates. + /// Only works if the `block_number >= current_vote().0` and `< current_vote().0 + presentation_duration()` + /// `signed` should have at least + /// + /// # + /// - O(voters) compute. + /// - One DB change. + /// # + fn present_winner( + origin, + candidate: ::Source, + #[compact] total: BalanceOf, + #[compact] index: VoteIndex + ) -> Result { + let who = ensure_signed(origin)?; + ensure!( + !total.is_zero(), + "stake deposited to present winner and be added to leaderboard should be non-zero" + ); + + let candidate = T::Lookup::lookup(candidate)?; + ensure!(index == Self::vote_index(), "index not current"); + let (_, _, expiring) = Self::next_finalize().ok_or("cannot present outside of presentation period")?; + let bad_presentation_punishment = + T::PresentSlashPerVoter::get() + * BalanceOf::::from(Self::voter_count() as u32); + ensure!( + T::Currency::can_slash(&who, bad_presentation_punishment), + "presenter must have sufficient slashable funds" + ); + + let mut leaderboard = Self::leaderboard().ok_or("leaderboard must exist while present phase active")?; + ensure!(total > leaderboard[0].0, "candidate not worthy of leaderboard"); + + if let Some(p) = Self::members().iter().position(|&(ref c, _)| c == &candidate) { + ensure!(p < expiring.len(), "candidate must not form a duplicated member if elected"); + } + + let voters = Self::all_voters(); + let (registered_since, candidate_index): (VoteIndex, u32) = + Self::candidate_reg_info(&candidate).ok_or("presented candidate must be current")?; + let actual_total = voters.iter() + .filter_map(|maybe_voter| maybe_voter.as_ref()) + .filter_map(|voter| match Self::voter_info(voter) { + Some(b) if b.last_active >= registered_since => { + let last_win = b.last_win; + let now = Self::vote_index(); + let stake = b.stake; + let offset = Self::get_offset(stake, now - last_win); + let weight = stake + offset + b.pot; + if Self::approvals_of_at(voter, candidate_index as usize) { + Some(weight) + } else { None } + }, + _ => None, + }) + .fold(Zero::zero(), |acc, n| acc + n); + let dupe = leaderboard.iter().find(|&&(_, ref c)| c == &candidate).is_some(); + if total == actual_total && !dupe { + // insert into leaderboard + leaderboard[0] = (total, candidate); + leaderboard.sort_by_key(|&(t, _)| t); + >::put(leaderboard); + Ok(()) + } else { + // we can rest assured it will be Ok since we checked `can_slash` earlier; still + // better safe than sorry. + let imbalance = T::Currency::slash(&who, bad_presentation_punishment).0; + T::BadPresentation::on_unbalanced(imbalance); + Err(if dupe { "duplicate presentation" } else { "incorrect total" }) + } + } + + /// Set the desired member count; if lower than the current count, then seats will not be up + /// election when they expire. If more, then a new vote will be started if one is not + /// already in progress. + fn set_desired_seats(origin, #[compact] count: u32) { + ensure_root(origin)?; + DesiredSeats::put(count); + } + + /// Remove a particular member from the set. This is effective immediately. + /// + /// Note: A tally should happen instantly (if not already in a presentation + /// period) to fill the seat if removal means that the desired members are not met. + fn remove_member(origin, who: ::Source) { + ensure_root(origin)?; + let who = T::Lookup::lookup(who)?; + let new_set: Vec<(T::AccountId, T::BlockNumber)> = Self::members() + .into_iter() + .filter(|i| i.0 != who) + .collect(); + >::put(&new_set); + let new_set = new_set.into_iter().map(|x| x.0).collect::>(); + T::ChangeMembers::change_members(&[], &[who], &new_set[..]); + } + + /// Set the presentation duration. If there is currently a vote being presented for, will + /// invoke `finalize_vote`. + fn set_presentation_duration(origin, #[compact] count: T::BlockNumber) { + ensure_root(origin)?; + >::put(count); + } + + /// Set the presentation duration. If there is current a vote being presented for, will + /// invoke `finalize_vote`. + fn set_term_duration(origin, #[compact] count: T::BlockNumber) { + ensure_root(origin)?; + >::put(count); + } + + fn on_initialize(n: T::BlockNumber) { + if let Err(e) = Self::end_block(n) { + print("Guru meditation"); + print(e); + } + } + } +} + +decl_event!( + pub enum Event where ::AccountId { + /// reaped voter, reaper + VoterReaped(AccountId, AccountId), + /// slashed reaper + BadReaperSlashed(AccountId), + /// A tally (for approval votes of seat(s)) has started. + TallyStarted(u32), + /// A tally (for approval votes of seat(s)) has ended (with one or more new members). + TallyFinalized(Vec, Vec), + } +); + +impl Module { + // exposed immutables. + + /// True if we're currently in a presentation period. + pub fn presentation_active() -> bool { + >::exists() + } + + /// If `who` a candidate at the moment? + pub fn is_a_candidate(who: &T::AccountId) -> bool { + >::exists(who) + } + + /// Iff the member `who` still has a seat at blocknumber `n` returns `true`. + pub fn will_still_be_member_at(who: &T::AccountId, n: T::BlockNumber) -> bool { + Self::members().iter() + .find(|&&(ref a, _)| a == who) + .map(|&(_, expires)| expires > n) + .unwrap_or(false) + } + + /// Determine the block that a vote can happen on which is no less than `n`. + pub fn next_vote_from(n: T::BlockNumber) -> T::BlockNumber { + let voting_period = T::VotingPeriod::get(); + (n + voting_period - One::one()) / voting_period * voting_period + } + + /// The block number on which the tally for the next election will happen. `None` only if the + /// desired seats of the set is zero. + pub fn next_tally() -> Option { + let desired_seats = Self::desired_seats(); + if desired_seats == 0 { + None + } else { + let c = Self::members(); + let (next_possible, count, coming) = + if let Some((tally_end, comers, leavers)) = Self::next_finalize() { + // if there's a tally in progress, then next tally can begin immediately afterwards + (tally_end, c.len() - leavers.len() + comers as usize, comers) + } else { + (>::block_number(), c.len(), 0) + }; + if count < desired_seats as usize { + Some(next_possible) + } else { + // next tally begins once enough members expire to bring members below desired. + if desired_seats <= coming { + // the entire amount of desired seats is less than those new members - we'll have + // to wait until they expire. + Some(next_possible + Self::term_duration()) + } else { + Some(c[c.len() - (desired_seats - coming) as usize].1) + } + }.map(Self::next_vote_from) + } + } + + // Private + /// Check there's nothing to do this block + fn end_block(block_number: T::BlockNumber) -> Result { + if (block_number % T::VotingPeriod::get()).is_zero() { + if let Some(number) = Self::next_tally() { + if block_number == number { + Self::start_tally(); + } + } + } + if let Some((number, _, _)) = Self::next_finalize() { + if block_number == number { + Self::finalize_tally()? + } + } + Ok(()) + } + + /// Remove a voter at a specified index from the system. + fn remove_voter(voter: &T::AccountId, index: usize) { + let (set_index, vec_index) = Self::split_index(index, VOTER_SET_SIZE); + let mut set = Self::voters(set_index); + set[vec_index] = None; + >::insert(set_index, set); + VoterCount::mutate(|c| *c = *c - 1); + Self::remove_all_approvals_of(voter); + >::remove(voter); + } + + /// Actually do the voting. + /// + /// The voter index must be provided as explained in [`voter_at`] function. + fn do_set_approvals(who: T::AccountId, votes: Vec, index: VoteIndex, hint: SetIndex) -> Result { + let candidates = Self::candidates(); + + ensure!(!Self::presentation_active(), "no approval changes during presentation period"); + ensure!(index == Self::vote_index(), "incorrect vote index"); + ensure!(!candidates.is_empty(), "amount of candidates to receive approval votes should be non-zero"); + // Prevent a vote from voters that provide a list of votes that exceeds the candidates length + // since otherwise an attacker may be able to submit a very long list of `votes` that far exceeds + // the amount of candidates and waste more computation than a reasonable voting bond would cover. + ensure!(candidates.len() >= votes.len(), "amount of candidate votes cannot exceed amount of candidates"); + + // Amount to be locked up. + let mut locked_balance = T::Currency::total_balance(&who); + let mut pot_to_set = Zero::zero(); + let hint = hint as usize; + + if let Some(info) = Self::voter_info(&who) { + // already a voter. Index must be valid. No fee. update pot. O(1) + let voter = Self::voter_at(hint).ok_or("invalid voter index")?; + ensure!(voter == who, "wrong voter index"); + + // write new accumulated offset. + let last_win = info.last_win; + let now = index; + let offset = Self::get_offset(info.stake, now - last_win); + pot_to_set = info.pot + offset; + } else { + // not yet a voter. Index _could be valid_. Fee might apply. Bond will be reserved O(1). + ensure!( + T::Currency::free_balance(&who) > T::VotingBond::get(), + "new voter must have sufficient funds to pay the bond" + ); + + let (set_index, vec_index) = Self::split_index(hint, VOTER_SET_SIZE); + match Self::cell_status(set_index, vec_index) { + CellStatus::Hole => { + // requested cell was a valid hole. + >::mutate(set_index, |set| set[vec_index] = Some(who.clone())); + }, + CellStatus::Head | CellStatus::Occupied => { + // Either occupied or out-of-range. + let next = Self::next_nonfull_voter_set(); + let mut set = Self::voters(next); + // Caused a new set to be created. Pay for it. + // This is the last potential error. Writes will begin afterwards. + if set.is_empty() { + let imbalance = T::Currency::withdraw( + &who, + T::VotingFee::get(), + WithdrawReason::Fee, + ExistenceRequirement::KeepAlive, + )?; + T::BadVoterIndex::on_unbalanced(imbalance); + // NOTE: this is safe since the `withdraw()` will check this. + locked_balance -= T::VotingFee::get(); + } + Self::checked_push_voter(&mut set, who.clone(), next); + >::insert(next, set); + } + } + + T::Currency::reserve(&who, T::VotingBond::get())?; + VoterCount::mutate(|c| *c = *c + 1); + } + + T::Currency::set_lock( + MODULE_ID, + &who, + locked_balance, + T::BlockNumber::max_value(), + WithdrawReasons::except(WithdrawReason::TransactionPayment), + ); + + >::insert( + &who, + VoterInfo::> { + last_active: index, + last_win: index, + stake: locked_balance, + pot: pot_to_set, + } + ); + Self::set_approvals_chunked(&who, votes); + + Ok(()) + } + + /// Close the voting, record the number of seats that are actually up for grabs. + fn start_tally() { + let members = Self::members(); + let desired_seats = Self::desired_seats() as usize; + let number = >::block_number(); + let expiring = members.iter().take_while(|i| i.1 <= number).map(|i| i.0.clone()).collect::>(); + let retaining_seats = members.len() - expiring.len(); + if retaining_seats < desired_seats { + let empty_seats = desired_seats - retaining_seats; + >::put((number + Self::presentation_duration(), empty_seats as u32, expiring)); + + // initialize leaderboard. + let leaderboard_size = empty_seats + T::CarryCount::get() as usize; + >::put(vec![(Zero::zero(), T::AccountId::default()); leaderboard_size]); + + Self::deposit_event(RawEvent::TallyStarted(empty_seats as u32)); + } + } + + /// Finalize the vote, removing each of the `removals` and inserting `seats` of the most approved + /// candidates in their place. If the total number of members is less than the desired membership + /// a new vote is started. + /// Clears all presented candidates, returning the bond of the elected ones. + fn finalize_tally() -> Result { + let (_, coming, expiring): (T::BlockNumber, u32, Vec) = + >::take().ok_or("finalize can only be called after a tally is started.")?; + let leaderboard: Vec<(BalanceOf, T::AccountId)> = >::take().unwrap_or_default(); + let new_expiry = >::block_number() + Self::term_duration(); + + // return bond to winners. + let candidacy_bond = T::CandidacyBond::get(); + let incoming: Vec<_> = leaderboard.iter() + .rev() + .take_while(|&&(b, _)| !b.is_zero()) + .take(coming as usize) + .map(|(_, a)| a) + .cloned() + .inspect(|a| { T::Currency::unreserve(a, candidacy_bond); }) + .collect(); + + // Update last win index for anyone voted for any of the incomings. + incoming.iter().filter_map(|i| Self::candidate_reg_info(i)).for_each(|r| { + let index = r.1 as usize; + Self::all_voters() + .iter() + .filter_map(|mv| mv.as_ref()) + .filter(|v| Self::approvals_of_at(*v, index)) + .for_each(|v| >::mutate(v, |a| { + if let Some(activity) = a { activity.last_win = Self::vote_index() + 1; } + })); + }); + let members = Self::members(); + let outgoing: Vec<_> = members.iter() + .take(expiring.len()) + .map(|a| a.0.clone()).collect(); + + // set the new membership set. + let mut new_set: Vec<_> = members + .into_iter() + .skip(expiring.len()) + .chain(incoming.iter().cloned().map(|a| (a, new_expiry))) + .collect(); + new_set.sort_by_key(|&(_, expiry)| expiry); + >::put(&new_set); + + let new_set = new_set.into_iter().map(|x| x.0).collect::>(); + T::ChangeMembers::change_members(&incoming, &outgoing, &new_set[..]); + + // clear all except runners-up from candidate list. + let candidates = Self::candidates(); + let mut new_candidates = vec![T::AccountId::default(); candidates.len()]; // shrink later. + let runners_up = leaderboard.into_iter() + .rev() + .take_while(|&(b, _)| !b.is_zero()) + .skip(coming as usize) + .filter_map(|(_, a)| Self::candidate_reg_info(&a).map(|i| (a, i.1))); + let mut count = 0u32; + for (address, slot) in runners_up { + new_candidates[slot as usize] = address; + count += 1; + } + for (old, new) in candidates.iter().zip(new_candidates.iter()) { + if old != new { + // removed - kill it + >::remove(old); + } + } + // discard any superfluous slots. + if let Some(last_index) = new_candidates.iter().rposition(|c| *c != T::AccountId::default()) { + new_candidates.truncate(last_index + 1); + } + + Self::deposit_event(RawEvent::TallyFinalized(incoming, outgoing)); + + >::put(new_candidates); + CandidateCount::put(count); + VoteCount::put(Self::vote_index() + 1); + Ok(()) + } + + fn checked_push_voter(set: &mut Vec>, who: T::AccountId, index: u32) { + let len = set.len(); + + // Defensive only: this should never happen. Don't push since it will break more things. + if len == VOTER_SET_SIZE { return; } + + set.push(Some(who)); + if len + 1 == VOTER_SET_SIZE { + NextVoterSet::put(index + 1); + } + } + + /// Get the set and vector index of a global voter index. + /// + /// Note that this function does not take holes into account. + /// See [`voter_at`]. + fn split_index(index: usize, scale: usize) -> (SetIndex, usize) { + let set_index = (index / scale) as u32; + let vec_index = index % scale; + (set_index, vec_index) + } + + /// Return a concatenated vector over all voter sets. + fn all_voters() -> Vec> { + let mut all = >::get(0); + let mut index = 1; + // NOTE: we could also use `Self::next_nonfull_voter_set()` here but that might change based + // on how we do chunking. This is more generic. + loop { + let next_set = >::get(index); + if next_set.is_empty() { + break; + } else { + index += 1; + all.extend(next_set); + } + } + all + } + + /// Shorthand for fetching a voter at a specific (global) index. + /// + /// NOTE: this function is used for checking indices. Yet, it does not take holes into account. + /// This means that any account submitting an index at any point in time should submit: + /// `VOTER_SET_SIZE * set_index + local_index`, meaning that you are ignoring all holes in the + /// first `set_index` sets. + fn voter_at(index: usize) -> Option { + let (set_index, vec_index) = Self::split_index(index, VOTER_SET_SIZE); + let set = Self::voters(set_index); + if vec_index < set.len() { + set[vec_index].clone() + } else { + None + } + } + + /// A more sophisticated version of `voter_at`. Will be kept separate as most often it is an overdue + /// compared to `voter_at`. Only used when setting approvals. + fn cell_status(set_index: SetIndex, vec_index: usize) -> CellStatus { + let set = Self::voters(set_index); + if vec_index < set.len() { + if let Some(_) = set[vec_index] { + CellStatus::Occupied + } else { + CellStatus::Hole + } + } else { + CellStatus::Head + } + } + + /// Sets the approval of a voter in a chunked manner. + fn set_approvals_chunked(who: &T::AccountId, approvals: Vec) { + let approvals_flag_vec = Self::bool_to_flag(approvals); + approvals_flag_vec + .chunks(APPROVAL_SET_SIZE) + .enumerate() + .for_each(|(index, slice)| >::insert((who.clone(), index as SetIndex), slice.to_vec())); + } + + /// shorthand for fetching a specific approval of a voter at a specific (global) index. + /// + /// Using this function to read a vote is preferred as it reads `APPROVAL_SET_SIZE` items of type + /// `ApprovalFlag` from storage at most; not all of them. + /// + /// Note that false is returned in case of no-vote or an explicit `false`. + fn approvals_of_at(who: &T::AccountId, index: usize) -> bool { + let (flag_index, bit) = Self::split_index(index, APPROVAL_FLAG_LEN); + let (set_index, vec_index) = Self::split_index(flag_index as usize, APPROVAL_SET_SIZE); + let set = Self::approvals_of((who.clone(), set_index)); + if vec_index < set.len() { + // This is because bit_at treats numbers in lsb -> msb order. + let reversed_index = set.len() - 1 - vec_index; + Self::bit_at(set[reversed_index], bit) + } else { + false + } + } + + /// Return true of the bit `n` of scalar `x` is set to `1` and false otherwise. + fn bit_at(x: ApprovalFlag, n: usize) -> bool { + if n < APPROVAL_FLAG_LEN { + // x & ( APPROVAL_FLAG_MASK >> n ) != 0 + x & ( 1 << n ) != 0 + } else { + false + } + } + + /// Convert a vec of boolean approval flags to a vec of integers, as denoted by + /// the type `ApprovalFlag`. see `bool_to_flag_should_work` test for examples. + pub fn bool_to_flag(x: Vec) -> Vec { + let mut result: Vec = Vec::with_capacity(x.len() / APPROVAL_FLAG_LEN); + if x.is_empty() { + return result; + } + result.push(0); + let mut index = 0; + let mut counter = 0; + loop { + let shl_index = counter % APPROVAL_FLAG_LEN; + result[index] += (if x[counter] { 1 } else { 0 }) << shl_index; + counter += 1; + if counter > x.len() - 1 { break; } + if counter % APPROVAL_FLAG_LEN == 0 { + result.push(0); + index += 1; + } + } + result + } + + /// Convert a vec of flags (u32) to boolean. + pub fn flag_to_bool(chunk: Vec) -> Vec { + let mut result = Vec::with_capacity(chunk.len()); + if chunk.is_empty() { return vec![] } + chunk.into_iter() + .map(|num| (0..APPROVAL_FLAG_LEN).map(|bit| Self::bit_at(num, bit)).collect::>()) + .for_each(|c| { + let last_approve = match c.iter().rposition(|n| *n) { + Some(index) => index + 1, + None => 0 + }; + result.extend(c.into_iter().take(last_approve)); + }); + result + } + + /// Return a concatenated vector over all approvals of a voter as boolean. + /// The trailing zeros are removed. + fn all_approvals_of(who: &T::AccountId) -> Vec { + let mut all: Vec = vec![]; + let mut index = 0_u32; + loop { + let chunk = Self::approvals_of((who.clone(), index)); + if chunk.is_empty() { break; } + all.extend(Self::flag_to_bool(chunk)); + index += 1; + } + all + } + + /// Remove all approvals associated with one account. + fn remove_all_approvals_of(who: &T::AccountId) { + let mut index = 0; + loop { + let set = Self::approvals_of((who.clone(), index)); + if set.len() > 0 { + >::remove((who.clone(), index)); + index += 1; + } else { + break + } + } + } + + /// Calculates the offset value (stored pot) of a stake, based on the distance + /// to the last win_index, `t`. Regardless of the internal implementation, + /// it should always be used with the following structure: + /// + /// Given Stake of voter `V` being `x` and distance to last_win index `t`, the new weight + /// of `V` is `x + get_offset(x, t)`. + /// + /// In other words, this function returns everything extra that should be added + /// to a voter's stake value to get the correct weight. Indeed, zero is + /// returned if `t` is zero. + fn get_offset(stake: BalanceOf, t: VoteIndex) -> BalanceOf { + let decay_ratio: BalanceOf = T::DecayRatio::get().into(); + if t > 150 { return stake * decay_ratio } + let mut offset = stake; + let mut r = Zero::zero(); + let decay = decay_ratio + One::one(); + for _ in 0..t { + offset = offset.saturating_sub(offset / decay); + r += offset + } + r + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::cell::RefCell; + use srml_support::{assert_ok, assert_err, assert_noop, parameter_types}; + use runtime_io::with_externalities; + use substrate_primitives::{H256, Blake2Hasher}; + use primitives::{ + traits::{BlakeTwo256, IdentityLookup, Block as BlockT}, testing::Header, BuildStorage + }; + use crate as elections; + + parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + } + impl system::Trait for Test { + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type Event = Event; + type WeightMultiplierUpdate = (); + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; + } + 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; + } + parameter_types! { + pub const CandidacyBond: u64 = 3; + pub const CarryCount: u32 = 2; + pub const InactiveGracePeriod: u32 = 1; + pub const VotingPeriod: u64 = 4; + } + + 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); + static MEMBERS: RefCell> = RefCell::new(vec![]); + } + + 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()) } + } + + pub struct TestChangeMembers; + impl ChangeMembers for TestChangeMembers { + fn change_members(incoming: &[u64], outgoing: &[u64], new: &[u64]) { + let mut old_plus_incoming = MEMBERS.with(|m| m.borrow().to_vec()); + old_plus_incoming.extend_from_slice(incoming); + old_plus_incoming.sort(); + let mut new_plus_outgoing = new.to_vec(); + new_plus_outgoing.extend_from_slice(outgoing); + new_plus_outgoing.sort(); + assert_eq!(old_plus_incoming, new_plus_outgoing); + + MEMBERS.with(|m| *m.borrow_mut() = new.to_vec()); + } + } + + impl Trait for Test { + type Event = Event; + type Currency = Balances; + type BadPresentation = (); + type BadReaper = (); + type BadVoterIndex = (); + type LoserCandidate = (); + type ChangeMembers = TestChangeMembers; + type CandidacyBond = CandidacyBond; + type VotingBond = VotingBond; + type VotingFee = VotingFee; + type PresentSlashPerVoter = PresentSlashPerVoter; + type CarryCount = CarryCount; + type InactiveGracePeriod = InactiveGracePeriod; + type VotingPeriod = VotingPeriod; + type DecayRatio = DecayRatio; + } + + pub type Block = primitives::generic::Block; + pub type UncheckedExtrinsic = primitives::generic::UncheckedExtrinsic; + + srml_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + System: system::{Module, Call, Event}, + Balances: balances::{Module, Call, Event, Config}, + Elections: elections::{Module, Call, Event, Config}, + } + ); + + pub struct ExtBuilder { + balance_factor: u64, + decay_ratio: u32, + voting_fee: u64, + voter_bond: u64, + bad_presentation_punishment: u64, + } + + impl Default for ExtBuilder { + fn default() -> Self { + Self { + balance_factor: 1, + decay_ratio: 24, + voting_fee: 0, + voter_bond: 0, + bad_presentation_punishment: 1, + } + } + } + + impl ExtBuilder { + 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 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); + DECAY_RATIO.with(|v| *v.borrow_mut() = self.decay_ratio); + GenesisConfig { + balances: Some(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![], + }), + elections: Some(elections::GenesisConfig::{ + members: vec![], + desired_seats: 2, + presentation_duration: 2, + term_duration: 5, + }), + }.build_storage().unwrap().0.into() + } + } + + fn voter_ids() -> Vec { + Elections::all_voters().iter().map(|v| v.unwrap_or(0) ).collect::>() + } + + fn vote(i: u64, l: usize) { + let _ = Balances::make_free_balance_be(&i, 20); + assert_ok!(Elections::set_approvals(Origin::signed(i), (0..l).map(|_| true).collect::>(), 0, 0)); + } + + fn vote_at(i: u64, l: usize, index: VoteIndex) { + let _ = Balances::make_free_balance_be(&i, 20); + assert_ok!(Elections::set_approvals(Origin::signed(i), (0..l).map(|_| true).collect::>(), 0, index)); + } + + fn create_candidate(i: u64, index: u32) { + let _ = Balances::make_free_balance_be(&i, 20); + assert_ok!(Elections::submit_candidacy(Origin::signed(i), index)); + } + + fn bond() -> u64 { + ::VotingBond::get() + } + + #[test] + fn bool_to_flag_should_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + 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]); + assert_eq!(Elections::bool_to_flag(vec![true, true, true, true]), vec![15]); + assert_eq!(Elections::bool_to_flag(vec![true, true, true, true, true]), vec![15 + 16]); + + let set_1 = vec![ + true, false, false, false, // 0x1 + false, true, true, true, // 0xE + ]; + assert_eq!( + Elections::bool_to_flag(set_1.clone()), + vec![0x00_00_00_E1_u32] + ); + assert_eq!( + Elections::flag_to_bool(vec![0x00_00_00_E1_u32]), + set_1 + ); + + let set_2 = vec![ + false, false, false, true, // 0x8 + false, true, false, true, // 0xA + ]; + assert_eq!( + Elections::bool_to_flag(set_2.clone()), + vec![0x00_00_00_A8_u32] + ); + assert_eq!( + Elections::flag_to_bool(vec![0x00_00_00_A8_u32]), + set_2 + ); + + let mut rhs = (0..100/APPROVAL_FLAG_LEN).map(|_| 0xFFFFFFFF_u32).collect::>(); + // NOTE: this might be need change based on `APPROVAL_FLAG_LEN`. + rhs.extend(vec![0x00_00_00_0F]); + assert_eq!( + Elections::bool_to_flag((0..100).map(|_| true).collect()), + rhs + ) + }) + } + + #[test] + fn params_should_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + System::set_block_number(1); + assert_eq!(Elections::next_vote_from(1), 4); + assert_eq!(Elections::next_vote_from(4), 4); + assert_eq!(Elections::next_vote_from(5), 8); + assert_eq!(Elections::vote_index(), 0); + assert_eq!(Elections::presentation_duration(), 2); + assert_eq!(Elections::term_duration(), 5); + assert_eq!(Elections::desired_seats(), 2); + + assert_eq!(Elections::members(), vec![]); + assert_eq!(Elections::next_tally(), Some(4)); + assert_eq!(Elections::presentation_active(), false); + assert_eq!(Elections::next_finalize(), None); + + assert_eq!(Elections::candidates(), Vec::::new()); + assert_eq!(Elections::is_a_candidate(&1), false); + assert_eq!(Elections::candidate_reg_info(1), None); + + assert_eq!(Elections::voters(0), Vec::>::new()); + assert_eq!(Elections::voter_info(1), None); + assert_eq!(Elections::all_approvals_of(&1), vec![]); + }); + } + + #[test] + fn voter_set_growth_should_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 1)); + + // create 65. 64 (set0) + 1 (set1) + (1..=63).for_each(|i| vote(i, 2)); + assert_eq!(Elections::next_nonfull_voter_set(), 0); + vote(64, 2); + assert_eq!(Elections::next_nonfull_voter_set(), 1); + vote(65, 2); + + let set1 = Elections::voters(0); + let set2 = Elections::voters(1); + + assert_eq!(set1.len(), 64); + assert_eq!(set2.len(), 1); + + assert_eq!(set1[0], Some(1)); + assert_eq!(set1[10], Some(11)); + assert_eq!(set2[0], Some(65)); + }) + } + + #[test] + fn voter_set_reclaim_should_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 1)); + + (1..=129).for_each(|i| vote(i, 2)); + assert_eq!(Elections::next_nonfull_voter_set(), 2); + + assert_ok!(Elections::retract_voter(Origin::signed(11), 10)); + + assert_ok!(Elections::retract_voter(Origin::signed(66), 65)); + assert_ok!(Elections::retract_voter(Origin::signed(67), 66)); + + // length does not show it but holes do exist. + assert_eq!(Elections::voters(0).len(), 64); + assert_eq!(Elections::voters(1).len(), 64); + assert_eq!(Elections::voters(2).len(), 1); + + assert_eq!(Elections::voters(0)[10], None); + assert_eq!(Elections::voters(1)[1], None); + assert_eq!(Elections::voters(1)[2], None); + // Next set with capacity is 2. + assert_eq!(Elections::next_nonfull_voter_set(), 2); + + // But we can fill a hole. + vote_at(130, 2, 10); + + // Nothing added to set 2. A hole was filled. + assert_eq!(Elections::voters(0).len(), 64); + assert_eq!(Elections::voters(1).len(), 64); + assert_eq!(Elections::voters(2).len(), 1); + + // and the next two (scheduled) to the second set. + assert_eq!(Elections::next_nonfull_voter_set(), 2); + }) + } + + #[test] + fn approvals_set_growth_should_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + // 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)); + + // all approvals of should return the exact expected vector. + assert_eq!(Elections::all_approvals_of(&180), (0..180).map(|_| true).collect::>()); + + assert_eq!(Elections::all_approvals_of(&32), (0..32).map(|_| true).collect::>()); + assert_eq!(Elections::all_approvals_of(&8), (0..8).map(|_| true).collect::>()); + assert_eq!(Elections::all_approvals_of(&64), (0..64).map(|_| true).collect::>()); + assert_eq!(Elections::all_approvals_of(&65), (0..65).map(|_| true).collect::>()); + assert_eq!(Elections::all_approvals_of(&63), (0..63).map(|_| true).collect::>()); + + // NOTE: assuming that APPROVAL_SET_SIZE is more or less small-ish. Might fail otherwise. + let full_sets = (180 / APPROVAL_FLAG_LEN) / APPROVAL_SET_SIZE; + let left_over = (180 / APPROVAL_FLAG_LEN) / APPROVAL_SET_SIZE; + let rem = 180 % APPROVAL_FLAG_LEN; + + // grab and check the last full set, if it exists. + if full_sets > 0 { + assert_eq!( + Elections::approvals_of((180, (full_sets-1) as SetIndex )), + Elections::bool_to_flag((0..APPROVAL_SET_SIZE * APPROVAL_FLAG_LEN).map(|_| true).collect::>()) + ); + } + + // grab and check the last, half-empty, set. + if left_over > 0 { + assert_eq!( + Elections::approvals_of((180, full_sets as SetIndex)), + Elections::bool_to_flag((0..left_over * APPROVAL_FLAG_LEN + rem).map(|_| true).collect::>()) + ); + } + }) + } + + + #[test] + fn cell_status_works() { + with_externalities(&mut ExtBuilder::default().build(), || { + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 1)); + + (1..=63).for_each(|i| vote(i, 2)); + + assert_ok!(Elections::retract_voter(Origin::signed(11), 10)); + assert_ok!(Elections::retract_voter(Origin::signed(21), 20)); + + assert_eq!(Elections::cell_status(0, 10), CellStatus::Hole); + assert_eq!(Elections::cell_status(0, 0), CellStatus::Occupied); + assert_eq!(Elections::cell_status(0, 20), CellStatus::Hole); + assert_eq!(Elections::cell_status(0, 63), CellStatus::Head); + assert_eq!(Elections::cell_status(1, 0), CellStatus::Head); + assert_eq!(Elections::cell_status(1, 10), CellStatus::Head); + }) + } + + #[test] + fn initial_set_approvals_ignores_voter_index() { + with_externalities(&mut ExtBuilder::default().build(), || { + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + + // Last argument is essentially irrelevant. You might get or miss a tip. + assert_ok!(Elections::set_approvals(Origin::signed(3), vec![true], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(4), vec![true], 0, 5)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![true], 0, 100)); + + // indices are more or less ignored. all is pushed. + assert_eq!(voter_ids(), vec![3, 4, 5]); + }) + } + + #[test] + fn bad_approval_index_slashes_voters_and_bond_reduces_stake() { + with_externalities(&mut ExtBuilder::default().voting_fee(5).voter_bond(2).build(), || { + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 1)); + + (1..=63).for_each(|i| vote(i, 2)); + assert_eq!(Balances::free_balance(&1), 20 - 5 - 2); // -5 fee -2 bond + assert_eq!(Balances::free_balance(&10), 20 - 2); + assert_eq!(Balances::free_balance(&60), 20 - 2); + + // still no fee + vote(64, 2); + assert_eq!(Balances::free_balance(&64), 20 - 2); // -2 bond + assert_eq!( + Elections::voter_info(&64).unwrap(), + VoterInfo { last_win: 0, last_active: 0, stake: 20, pot:0 } + ); + + assert_eq!(Elections::next_nonfull_voter_set(), 1); + + // now we charge the next voter. + vote(65, 2); + assert_eq!(Balances::free_balance(&65), 20 - 5 - 2); + assert_eq!( + Elections::voter_info(&65).unwrap(), + VoterInfo { last_win: 0, last_active: 0, stake: 15, pot:0 } + ); + }); + } + + #[test] + fn subsequent_set_approvals_checks_voter_index() { + with_externalities(&mut ExtBuilder::default().build(), || { + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + + assert_ok!(Elections::set_approvals(Origin::signed(3), vec![true], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(4), vec![true], 0, 5)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![true], 0, 100)); + + // invalid index + assert_noop!(Elections::set_approvals(Origin::signed(4), vec![true], 0, 5), "invalid voter index"); + // wrong index + assert_noop!(Elections::set_approvals(Origin::signed(4), vec![true], 0, 0), "wrong voter index"); + // correct + assert_ok!(Elections::set_approvals(Origin::signed(4), vec![true], 0, 1)); + }) + } + + #[test] + fn voter_index_does_not_take_holes_into_account() { + with_externalities(&mut ExtBuilder::default().build(), || { + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 1)); + + // create 65. 64 (set0) + 1 (set1) + (1..=65).for_each(|i| vote(i, 2)); + + // account 65 has global index 65. + assert_eq!(Elections::voter_at(64).unwrap(), 65); + + assert_ok!(Elections::retract_voter(Origin::signed(1), 0)); + assert_ok!(Elections::retract_voter(Origin::signed(2), 1)); + + // still the same. These holes are in some other set. + assert_eq!(Elections::voter_at(64).unwrap(), 65); + // proof: can submit a new approval with the old index. + assert_noop!(Elections::set_approvals(Origin::signed(65), vec![false, true], 0, 64 - 2), "wrong voter index"); + assert_ok!(Elections::set_approvals(Origin::signed(65), vec![false, true], 0, 64)); + }) + } + + #[test] + fn simple_candidate_submission_should_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + System::set_block_number(1); + assert_eq!(Elections::candidates(), Vec::::new()); + assert_eq!(Elections::candidate_reg_info(1), None); + assert_eq!(Elections::candidate_reg_info(2), None); + assert_eq!(Elections::is_a_candidate(&1), false); + assert_eq!(Elections::is_a_candidate(&2), false); + + assert_ok!(Elections::submit_candidacy(Origin::signed(1), 0)); + assert_eq!(Elections::candidates(), vec![1]); + assert_eq!(Elections::candidate_reg_info(1), Some((0, 0))); + assert_eq!(Elections::candidate_reg_info(2), None); + assert_eq!(Elections::is_a_candidate(&1), true); + assert_eq!(Elections::is_a_candidate(&2), false); + + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 1)); + assert_eq!(Elections::candidates(), vec![1, 2]); + assert_eq!(Elections::candidate_reg_info(1), Some((0, 0))); + assert_eq!(Elections::candidate_reg_info(2), Some((0, 1))); + assert_eq!(Elections::is_a_candidate(&1), true); + assert_eq!(Elections::is_a_candidate(&2), true); + }); + } + + fn new_test_ext_with_candidate_holes() -> runtime_io::TestExternalities { + let mut t = ExtBuilder::default().build(); + with_externalities(&mut t, || { + >::put(vec![0, 0, 1]); + CandidateCount::put(1); + >::insert(1, (0, 2)); + }); + t + } + + #[test] + fn candidate_submission_using_free_slot_should_work() { + let mut t = new_test_ext_with_candidate_holes(); + + with_externalities(&mut t, || { + System::set_block_number(1); + assert_eq!(Elections::candidates(), vec![0, 0, 1]); + + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 1)); + assert_eq!(Elections::candidates(), vec![0, 2, 1]); + + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 0)); + assert_eq!(Elections::candidates(), vec![3, 2, 1]); + }); + } + + #[test] + fn candidate_submission_using_alternative_free_slot_should_work() { + let mut t = new_test_ext_with_candidate_holes(); + + with_externalities(&mut t, || { + System::set_block_number(1); + assert_eq!(Elections::candidates(), vec![0, 0, 1]); + + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + assert_eq!(Elections::candidates(), vec![2, 0, 1]); + + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 1)); + assert_eq!(Elections::candidates(), vec![2, 3, 1]); + }); + } + + #[test] + fn candidate_submission_not_using_free_slot_should_not_work() { + let mut t = new_test_ext_with_candidate_holes(); + + with_externalities(&mut t, || { + System::set_block_number(1); + assert_noop!(Elections::submit_candidacy(Origin::signed(4), 3), "invalid candidate slot"); + }); + } + + #[test] + fn bad_candidate_slot_submission_should_not_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + System::set_block_number(1); + assert_eq!(Elections::candidates(), Vec::::new()); + assert_noop!(Elections::submit_candidacy(Origin::signed(1), 1), "invalid candidate slot"); + }); + } + + #[test] + fn non_free_candidate_slot_submission_should_not_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + System::set_block_number(1); + assert_eq!(Elections::candidates(), Vec::::new()); + assert_ok!(Elections::submit_candidacy(Origin::signed(1), 0)); + assert_eq!(Elections::candidates(), vec![1]); + assert_noop!(Elections::submit_candidacy(Origin::signed(2), 0), "invalid candidate slot"); + }); + } + + #[test] + fn dupe_candidate_submission_should_not_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + System::set_block_number(1); + assert_eq!(Elections::candidates(), Vec::::new()); + assert_ok!(Elections::submit_candidacy(Origin::signed(1), 0)); + assert_eq!(Elections::candidates(), vec![1]); + assert_noop!(Elections::submit_candidacy(Origin::signed(1), 1), "duplicate candidate submission"); + }); + } + + #[test] + fn poor_candidate_submission_should_not_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + System::set_block_number(1); + assert_eq!(Elections::candidates(), Vec::::new()); + assert_noop!(Elections::submit_candidacy(Origin::signed(7), 0), "candidate has not enough funds"); + }); + } + + #[test] + fn balance_should_lock_to_the_maximum() { + with_externalities(&mut ExtBuilder::default().build(), || { + System::set_block_number(1); + assert_eq!(Elections::candidates(), Vec::::new()); + assert_eq!(Balances::free_balance(&2), 20); + + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0)); + + assert_eq!(Balances::free_balance(&2), 20 - bond() ); + assert_noop!(Balances::reserve(&2, 1), "account liquidity restrictions prevent withdrawal"); // locked. + + // deposit a bit more. + let _ = Balances::deposit_creating(&2, 100); + assert_ok!(Balances::reserve(&2, 1)); // locked but now has enough. + + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0)); + assert_noop!(Balances::reserve(&2, 1), "account liquidity restrictions prevent withdrawal"); // locked. + assert_eq!(Balances::locks(&2).len(), 1); + assert_eq!(Balances::locks(&2)[0].amount, 100 + 20); + + assert_ok!(Elections::retract_voter(Origin::signed(2), 0)); + + assert_eq!(Balances::locks(&2).len(), 0); + assert_eq!(Balances::free_balance(&2), 120 - 1); // 1 ok call to .reserve() happened. + assert_ok!(Balances::reserve(&2, 1)); // unlocked. + }); + } + + #[test] + fn balance_should_lock_on_submit_approvals_unlock_on_retract() { + with_externalities(&mut ExtBuilder::default().voter_bond(8).voting_fee(0).build(), || { + System::set_block_number(1); + assert_eq!(Elections::candidates(), Vec::::new()); + assert_eq!(Balances::free_balance(&2), 20); + + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0)); + + assert_eq!(Balances::free_balance(&2), 12); // 20 - 8 (bond) + assert_noop!(Balances::reserve(&2, 10), "account liquidity restrictions prevent withdrawal"); // locked. + + assert_ok!(Elections::retract_voter(Origin::signed(2), 0)); + + assert_eq!(Balances::free_balance(&2), 20); + assert_ok!(Balances::reserve(&2, 10)); // unlocked. + }); + } + + #[test] + fn accumulating_weight_and_decaying_should_work() { + with_externalities(&mut ExtBuilder::default().balance_factor(10).build(), || { + 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::set_approvals(Origin::signed(6), vec![true, false, false], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, true, false], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(1), vec![false, false, true], 0, 0)); + + assert_ok!(Elections::end_block(System::block_number())); + + 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::members(), vec![(6, 11), (5, 11)]); + assert_eq!(Elections::voter_info(6).unwrap(), VoterInfo { last_win: 1, last_active: 0, stake: 600, pot: 0}); + assert_eq!(Elections::voter_info(5).unwrap(), VoterInfo { last_win: 1, last_active: 0, stake: 500, pot: 0}); + assert_eq!(Elections::voter_info(1).unwrap(), VoterInfo { last_win: 0, last_active: 0, stake: 100, pot: 0}); + + System::set_block_number(12); + // retract needed to unlock approval funds => submit candidacy again. + 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)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, true, false], 1, 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, 100 + Elections::get_offset(100, 1), 1), Ok(())); + assert_eq!(Elections::leaderboard(), Some(vec![(0, 0), (100 + 96, 1), (500, 5), (600, 6)])); + assert_ok!(Elections::end_block(System::block_number())); + + assert_eq!(Elections::members(), vec![(6, 19), (5, 19)]); + assert_eq!( + Elections::voter_info(6).unwrap(), + VoterInfo { last_win: 2, last_active: 1, stake: 600, pot:0 } + ); + assert_eq!(Elections::voter_info(5).unwrap(), VoterInfo { last_win: 2, last_active: 1, stake: 500, pot:0 }); + assert_eq!(Elections::voter_info(1).unwrap(), VoterInfo { last_win: 0, last_active: 0, stake: 100, pot:0 }); + + System::set_block_number(20); + 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], 2, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, true, false], 2, 1)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(22); + assert!(Elections::presentation_active()); + assert_eq!(Elections::present_winner(Origin::signed(6), 6, 600, 2), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(5), 5, 500, 2), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(1), 1, 100 + Elections::get_offset(100, 2), 2), Ok(())); + assert_eq!(Elections::leaderboard(), Some(vec![(0, 0), (100 + 96 + 93, 1), (500, 5), (600, 6)])); + assert_ok!(Elections::end_block(System::block_number())); + + assert_eq!(Elections::members(), vec![(6, 27), (5, 27)]); + assert_eq!( + Elections::voter_info(6).unwrap(), + VoterInfo { last_win: 3, last_active: 2, stake: 600, pot: 0} + ); + assert_eq!(Elections::voter_info(5).unwrap(), VoterInfo { last_win: 3, last_active: 2, stake: 500, pot: 0}); + assert_eq!(Elections::voter_info(1).unwrap(), VoterInfo { last_win: 0, last_active: 0, stake: 100, pot: 0}); + + + System::set_block_number(28); + 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], 3, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, true, false], 3, 1)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(30); + assert!(Elections::presentation_active()); + assert_eq!(Elections::present_winner(Origin::signed(6), 6, 600, 3), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(5), 5, 500, 3), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(1), 1, 100 + Elections::get_offset(100, 3), 3), Ok(())); + assert_eq!(Elections::leaderboard(), Some(vec![(0, 0), (100 + 96 + 93 + 90, 1), (500, 5), (600, 6)])); + assert_ok!(Elections::end_block(System::block_number())); + + assert_eq!(Elections::members(), vec![(6, 35), (5, 35)]); + assert_eq!( + Elections::voter_info(6).unwrap(), + VoterInfo { last_win: 4, last_active: 3, stake: 600, pot: 0} + ); + assert_eq!(Elections::voter_info(5).unwrap(), VoterInfo { last_win: 4, last_active: 3, stake: 500, pot: 0}); + assert_eq!(Elections::voter_info(1).unwrap(), VoterInfo { last_win: 0, last_active: 0, stake: 100, pot: 0}); + }) + } + + #[test] + fn winning_resets_accumulated_pot() { + with_externalities(&mut ExtBuilder::default().balance_factor(10).build(), || { + 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(4), 1)); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 2)); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 3)); + + assert_ok!(Elections::set_approvals(Origin::signed(6), vec![true, false, false, false], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(4), vec![false, true, false, false], 0, 1)); + assert_ok!(Elections::set_approvals(Origin::signed(3), vec![false, false, true, true], 0, 2)); + assert_ok!(Elections::end_block(System::block_number())); + + 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(4), 4, 400, 0), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(3), 3, 300, 0), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(2), 2, 300, 0), Ok(())); + assert_eq!(Elections::leaderboard(), Some(vec![(300, 2), (300, 3), (400, 4), (600, 6)])); + assert_ok!(Elections::end_block(System::block_number())); + + assert_eq!(Elections::members(), vec![(6, 11), (4, 11)]); + + System::set_block_number(12); + assert_ok!(Elections::retract_voter(Origin::signed(6), 0)); + assert_ok!(Elections::retract_voter(Origin::signed(4), 1)); + assert_ok!(Elections::submit_candidacy(Origin::signed(6), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(4), 1)); + assert_ok!(Elections::set_approvals(Origin::signed(6), vec![true, false, false, false], 1, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(4), vec![false, true, false, false], 1, 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(4), 4, 400, 1), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(3), 3, 300 + Elections::get_offset(300, 1), 1), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(2), 2, 300 + Elections::get_offset(300, 1), 1), Ok(())); + assert_eq!(Elections::leaderboard(), Some(vec![(400, 4), (588, 2), (588, 3), (600, 6)])); + assert_ok!(Elections::end_block(System::block_number())); + + assert_eq!(Elections::members(), vec![(6, 19), (3, 19)]); + + System::set_block_number(20); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(22); + // 2 will not get re-elected with 300 + 288, instead just 300. + // because one of 3's candidates (3) won in previous round + // 4 on the other hand will get extra weight since it was unlucky. + assert_eq!(Elections::present_winner(Origin::signed(3), 2, 300, 2), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(4), 4, 400 + Elections::get_offset(400, 1), 2), Ok(())); + assert_ok!(Elections::end_block(System::block_number())); + + assert_eq!(Elections::members(), vec![(4, 27), (2, 27)]); + }) + } + + #[test] + fn resubmitting_approvals_stores_pot() { + with_externalities(&mut ExtBuilder::default() + .voter_bond(0) + .voting_fee(0) + .balance_factor(10) + .build(), + || { 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::set_approvals(Origin::signed(6), vec![true, false, false], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, true, false], 0, 1)); + assert_ok!(Elections::set_approvals(Origin::signed(1), vec![false, false, true], 0, 2)); + + assert_ok!(Elections::end_block(System::block_number())); + + 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::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)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, true, false], 1, 1)); + // 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)); + 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)]); + + 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)]); + }) + } + + #[test] + fn get_offset_should_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + 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); + assert_eq!(Elections::get_offset(100, 3), 96 + 93 + 90); + assert_eq!(Elections::get_offset(100, 4), 96 + 93 + 90 + 87); + // limit + assert_eq!(Elections::get_offset(100, 1000), 100 * 24); + + assert_eq!(Elections::get_offset(50_000_000_000, 0), 0); + assert_eq!(Elections::get_offset(50_000_000_000, 1), 48_000_000_000); + assert_eq!(Elections::get_offset(50_000_000_000, 2), 48_000_000_000 + 46_080_000_000); + assert_eq!(Elections::get_offset(50_000_000_000, 3), 48_000_000_000 + 46_080_000_000 + 44_236_800_000); + assert_eq!( + Elections::get_offset(50_000_000_000, 4), + 48_000_000_000 + 46_080_000_000 + 44_236_800_000 + 42_467_328_000 + ); + // limit + assert_eq!(Elections::get_offset(50_000_000_000, 1000), 50_000_000_000 * 24); + }) + } + + #[test] + fn get_offset_with_zero_decay() { + with_externalities(&mut ExtBuilder::default().decay_ratio(0).build(), || { + assert_eq!(Elections::get_offset(100, 0), 0); + assert_eq!(Elections::get_offset(100, 1), 0); + assert_eq!(Elections::get_offset(100, 2), 0); + assert_eq!(Elections::get_offset(100, 3), 0); + // limit + assert_eq!(Elections::get_offset(100, 1000), 0); + }) + } + + #[test] + fn voting_should_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + System::set_block_number(1); + + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0)); + + assert_ok!(Elections::set_approvals(Origin::signed(1), vec![true], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(4), vec![true], 0, 1)); + + assert_eq!(Elections::all_approvals_of(&1), vec![true]); + assert_eq!(Elections::all_approvals_of(&4), vec![true]); + assert_eq!(voter_ids(), vec![1, 4]); + + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 1)); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 2)); + + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![false, true, true], 0, 2)); + assert_ok!(Elections::set_approvals(Origin::signed(3), vec![false, true, true], 0, 3)); + + assert_eq!(Elections::all_approvals_of(&1), vec![true]); + assert_eq!(Elections::all_approvals_of(&4), vec![true]); + assert_eq!(Elections::all_approvals_of(&2), vec![false, true, true]); + assert_eq!(Elections::all_approvals_of(&3), vec![false, true, true]); + + assert_eq!(voter_ids(), vec![1, 4, 2, 3]); + }); + } + + #[test] + fn proxy_voting_should_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + System::set_block_number(1); + + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0)); + + >::insert(11, 1); + >::insert(12, 2); + >::insert(13, 3); + >::insert(14, 4); + assert_ok!(Elections::proxy_set_approvals(Origin::signed(11), vec![true], 0, 0)); + assert_ok!(Elections::proxy_set_approvals(Origin::signed(14), vec![true], 0, 1)); + + assert_eq!(Elections::all_approvals_of(&1), vec![true]); + assert_eq!(Elections::all_approvals_of(&4), vec![true]); + assert_eq!(voter_ids(), vec![1, 4]); + + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 1)); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 2)); + + assert_ok!(Elections::proxy_set_approvals(Origin::signed(12), vec![false, true, true], 0, 2)); + assert_ok!(Elections::proxy_set_approvals(Origin::signed(13), vec![false, true, true], 0, 3)); + + assert_eq!(Elections::all_approvals_of(&1), vec![true]); + assert_eq!(Elections::all_approvals_of(&4), vec![true]); + assert_eq!(Elections::all_approvals_of(&2), vec![false, true, true]); + assert_eq!(Elections::all_approvals_of(&3), vec![false, true, true]); + + assert_eq!(voter_ids(), vec![1, 4, 2, 3]); + }); + } + + #[test] + fn setting_any_approval_vote_count_without_any_candidate_count_should_not_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + System::set_block_number(1); + + assert_eq!(Elections::candidates().len(), 0); + + assert_noop!( + Elections::set_approvals(Origin::signed(4), vec![], 0, 0), + "amount of candidates to receive approval votes should be non-zero" + ); + }); + } + + #[test] + fn setting_an_approval_vote_count_more_than_candidate_count_should_not_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + System::set_block_number(1); + + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0)); + assert_eq!(Elections::candidates().len(), 1); + + assert_noop!( + Elections::set_approvals(Origin::signed(4),vec![true, true], 0, 0), + "amount of candidate votes cannot exceed amount of candidates" + ); + }); + } + + #[test] + fn resubmitting_voting_should_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + System::set_block_number(1); + + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0)); + assert_ok!(Elections::set_approvals(Origin::signed(4), vec![true], 0, 0)); + + assert_eq!(Elections::all_approvals_of(&4), vec![true]); + + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 1)); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 2)); + assert_eq!(Elections::candidates().len(), 3); + assert_ok!(Elections::set_approvals(Origin::signed(4), vec![true, false, true], 0, 0)); + + assert_eq!(Elections::all_approvals_of(&4), vec![true, false, true]); + }); + } + + #[test] + fn retracting_voter_should_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + System::set_block_number(1); + + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 1)); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 2)); + assert_eq!(Elections::candidates().len(), 3); + + assert_ok!(Elections::set_approvals(Origin::signed(1), vec![true], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![false, true, true], 0, 1)); + assert_ok!(Elections::set_approvals(Origin::signed(3), vec![false, true, true], 0, 2)); + assert_ok!(Elections::set_approvals(Origin::signed(4), vec![true, false, true], 0, 3)); + + assert_eq!(voter_ids(), vec![1, 2, 3, 4]); + assert_eq!(Elections::all_approvals_of(&1), vec![true]); + assert_eq!(Elections::all_approvals_of(&2), vec![false, true, true]); + assert_eq!(Elections::all_approvals_of(&3), vec![false, true, true]); + assert_eq!(Elections::all_approvals_of(&4), vec![true, false, true]); + + assert_ok!(Elections::retract_voter(Origin::signed(1), 0)); + + assert_eq!(voter_ids(), vec![0, 2, 3, 4]); + assert_eq!(Elections::all_approvals_of(&1), Vec::::new()); + assert_eq!(Elections::all_approvals_of(&2), vec![false, true, true]); + assert_eq!(Elections::all_approvals_of(&3), vec![false, true, true]); + assert_eq!(Elections::all_approvals_of(&4), vec![true, false, true]); + + assert_ok!(Elections::retract_voter(Origin::signed(2), 1)); + + assert_eq!(voter_ids(), vec![0, 0, 3, 4]); + assert_eq!(Elections::all_approvals_of(&1), Vec::::new()); + assert_eq!(Elections::all_approvals_of(&2), Vec::::new()); + assert_eq!(Elections::all_approvals_of(&3), vec![false, true, true]); + assert_eq!(Elections::all_approvals_of(&4), vec![true, false, true]); + + assert_ok!(Elections::retract_voter(Origin::signed(3), 2)); + + assert_eq!(voter_ids(), vec![0, 0, 0, 4]); + assert_eq!(Elections::all_approvals_of(&1), Vec::::new()); + assert_eq!(Elections::all_approvals_of(&2), Vec::::new()); + assert_eq!(Elections::all_approvals_of(&3), Vec::::new()); + assert_eq!(Elections::all_approvals_of(&4), vec![true, false, true]); + }); + } + + #[test] + fn invalid_retraction_index_should_not_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + System::set_block_number(1); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 0)); + assert_ok!(Elections::set_approvals(Origin::signed(1), vec![true], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0)); + assert_eq!(voter_ids(), vec![1, 2]); + assert_noop!(Elections::retract_voter(Origin::signed(1), 1), "retraction index mismatch"); + }); + } + + #[test] + fn overflow_retraction_index_should_not_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + System::set_block_number(1); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 0)); + assert_ok!(Elections::set_approvals(Origin::signed(1), vec![true], 0, 0)); + assert_noop!(Elections::retract_voter(Origin::signed(1), 1), "retraction index invalid"); + }); + } + + #[test] + fn non_voter_retraction_should_not_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + System::set_block_number(1); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 0)); + assert_ok!(Elections::set_approvals(Origin::signed(1), vec![true], 0, 0)); + assert_noop!(Elections::retract_voter(Origin::signed(2), 0), "cannot retract non-voter"); + }); + } + + #[test] + fn approval_storage_should_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 1)); + + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true, false], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(3), vec![false, false], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(4), vec![], 0, 0)); + + assert_eq!(Elections::all_approvals_of(&2), vec![true]); + // NOTE: these two are stored in mem differently though. + assert_eq!(Elections::all_approvals_of(&3), vec![]); + assert_eq!(Elections::all_approvals_of(&4), vec![]); + + assert_eq!(Elections::approvals_of((3, 0)), vec![0]); + assert_eq!(Elections::approvals_of((4, 0)), vec![]); + }); + } + + #[test] + fn simple_tally_should_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + System::set_block_number(4); + assert!(!Elections::presentation_active()); + + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 1)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, true], 0, 0)); + assert_eq!(voter_ids(), vec![2, 5]); + assert_eq!(Elections::all_approvals_of(&2), vec![true]); + assert_eq!(Elections::all_approvals_of(&5), vec![false, true]); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(6); + assert!(Elections::presentation_active()); + assert_eq!(Elections::present_winner(Origin::signed(4), 2, 20, 0), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(4), 5, 50, 0), Ok(())); + assert_eq!(Elections::leaderboard(), Some(vec![(0, 0), (0, 0), (20, 2), (50, 5)])); + assert_ok!(Elections::end_block(System::block_number())); + + assert!(!Elections::presentation_active()); + assert_eq!(Elections::members(), vec![(5, 11), (2, 11)]); + + assert!(!Elections::is_a_candidate(&2)); + assert!(!Elections::is_a_candidate(&5)); + assert_eq!(Elections::vote_index(), 1); + assert_eq!(Elections::voter_info(2), Some(VoterInfo { last_win: 1, last_active: 0, stake: 20, pot: 0 })); + assert_eq!(Elections::voter_info(5), Some(VoterInfo { last_win: 1, last_active: 0, stake: 50, pot: 0 })); + }); + } + + #[test] + fn seats_should_be_released() { + with_externalities(&mut ExtBuilder::default().build(), || { + System::set_block_number(4); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 1)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true, false], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, true], 0, 0)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(6); + assert!(Elections::presentation_active()); + assert_eq!(Elections::present_winner(Origin::signed(4), 2, 20, 0), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(4), 5, 50, 0), Ok(())); + assert_eq!(Elections::leaderboard(), Some(vec![(0, 0), (0, 0), (20, 2), (50, 5)])); + assert_ok!(Elections::end_block(System::block_number())); + + assert_eq!(Elections::members(), vec![(5, 11), (2, 11)]); + let mut current = System::block_number(); + let free_block; + loop { + current += 1; + System::set_block_number(current); + assert_ok!(Elections::end_block(System::block_number())); + if Elections::members().len() == 0 { + free_block = current; + break; + } + } + // 11 + 2 which is the next voting period. + assert_eq!(free_block, 14); + }); + } + + #[test] + fn presentations_with_zero_staked_deposit_should_not_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + 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)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(6); + assert_noop!( + Elections::present_winner(Origin::signed(4), 2, 0, 0), + "stake deposited to present winner and be added to leaderboard should be non-zero" + ); + }); + } + + #[test] + fn double_presentations_should_be_punished() { + with_externalities(&mut ExtBuilder::default().build(), || { + assert!(Balances::can_slash(&4, 10)); + + System::set_block_number(4); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 1)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true, false], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, true], 0, 0)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(6); + assert_ok!(Elections::present_winner(Origin::signed(4), 2, 20, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 5, 50, 0)); + assert_eq!(Elections::present_winner(Origin::signed(4), 5, 50, 0), Err("duplicate presentation")); + assert_ok!(Elections::end_block(System::block_number())); + + assert_eq!(Elections::members(), vec![(5, 11), (2, 11)]); + assert_eq!(Balances::total_balance(&4), 38); + }); + } + + #[test] + fn retracting_inactive_voter_should_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + 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)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(6); + assert_ok!(Elections::present_winner(Origin::signed(4), 2, 20, 0)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(8); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![true], 1, 0)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(10); + assert_ok!(Elections::present_winner(Origin::signed(4), 5, 50, 1)); + assert_ok!(Elections::end_block(System::block_number())); + + assert_ok!(Elections::reap_inactive_voter(Origin::signed(5), + (voter_ids().iter().position(|&i| i == 5).unwrap() as u32).into(), + 2, (voter_ids().iter().position(|&i| i == 2).unwrap() as u32).into(), + 2 + )); + + assert_eq!(voter_ids(), vec![0, 5]); + assert_eq!(Elections::all_approvals_of(&2).len(), 0); + assert_eq!(Balances::total_balance(&2), 20); + assert_eq!(Balances::total_balance(&5), 50); + }); + } + + #[test] + fn presenting_for_double_election_should_not_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + 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)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(6); + assert_ok!(Elections::present_winner(Origin::signed(4), 2, 20, 0)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(8); + // NOTE: This is now mandatory to disable the lock + assert_ok!(Elections::retract_voter(Origin::signed(2), 0)); + assert_eq!(Elections::submit_candidacy(Origin::signed(2), 0), Ok(())); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 1, 0)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(10); + assert_noop!( + Elections::present_winner(Origin::signed(4), 2, 20, 1), + "candidate must not form a duplicated member if elected" + ); + }); + } + + #[test] + fn retracting_inactive_voter_with_other_candidates_in_slots_should_work() { + with_externalities(&mut ExtBuilder::default().voter_bond(2).build(), || { + 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)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(6); + assert_ok!(Elections::present_winner(Origin::signed(4), 2, 20, 0)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(8); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![true], 1, 0)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(10); + assert_ok!(Elections::present_winner(Origin::signed(4), 5, 50, 1)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(11); + assert_ok!(Elections::submit_candidacy(Origin::signed(1), 0)); + + assert_ok!(Elections::reap_inactive_voter(Origin::signed(5), + (voter_ids().iter().position(|&i| i == 5).unwrap() as u32).into(), + 2, (voter_ids().iter().position(|&i| i == 2).unwrap() as u32).into(), + 2 + )); + + assert_eq!(voter_ids(), vec![0, 5]); + assert_eq!(Elections::all_approvals_of(&2).len(), 0); + assert_eq!(Balances::total_balance(&2), 18); + assert_eq!(Balances::total_balance(&5), 52); + }); + } + + #[test] + fn retracting_inactive_voter_with_bad_reporter_index_should_not_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + 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)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(6); + assert_ok!(Elections::present_winner(Origin::signed(4), 2, 20, 0)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(8); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![true], 1, 0)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(10); + assert_ok!(Elections::present_winner(Origin::signed(4), 5, 50, 1)); + assert_ok!(Elections::end_block(System::block_number())); + + assert_noop!(Elections::reap_inactive_voter(Origin::signed(2), + 42, + 2, (voter_ids().iter().position(|&i| i == 2).unwrap() as u32).into(), + 2 + ), "invalid reporter index"); + }); + } + + #[test] + fn retracting_inactive_voter_with_bad_target_index_should_not_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + 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)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(6); + assert_ok!(Elections::present_winner(Origin::signed(4), 2, 20, 0)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(8); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![true], 1, 0)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(10); + assert_ok!(Elections::present_winner(Origin::signed(4), 5, 50, 1)); + assert_ok!(Elections::end_block(System::block_number())); + + assert_noop!(Elections::reap_inactive_voter(Origin::signed(2), + (voter_ids().iter().position(|&i| i == 2).unwrap() as u32).into(), + 2, 42, + 2 + ), "invalid target index"); + }); + } + + #[test] + fn attempting_to_retract_active_voter_should_slash_reporter() { + with_externalities(&mut ExtBuilder::default().build(), || { + System::set_block_number(4); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 1)); + assert_ok!(Elections::submit_candidacy(Origin::signed(4), 2)); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 3)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true, false, false, false], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(3), vec![false, true, false, false], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(4), vec![false, false, true, false], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, false, false, true], 0, 0)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(6); + assert_ok!(Elections::present_winner(Origin::signed(4), 2, 20, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 3, 30, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 4, 40, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 5, 50, 0)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(8); + assert_ok!(Elections::set_desired_seats(Origin::ROOT, 3)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(10); + assert_ok!(Elections::present_winner(Origin::signed(4), 2, 20 + Elections::get_offset(20, 1), 1)); + assert_ok!(Elections::present_winner(Origin::signed(4), 3, 30 + Elections::get_offset(30, 1), 1)); + assert_ok!(Elections::end_block(System::block_number())); + + assert_eq!(Elections::vote_index(), 2); + assert_eq!(::InactiveGracePeriod::get(), 1); + assert_eq!(::VotingPeriod::get(), 4); + assert_eq!(Elections::voter_info(4), Some(VoterInfo { last_win: 1, last_active: 0, stake: 40, pot: 0 })); + + assert_ok!(Elections::reap_inactive_voter(Origin::signed(4), + (voter_ids().iter().position(|&i| i == 4).unwrap() as u32).into(), + 2, + (voter_ids().iter().position(|&i| i == 2).unwrap() as u32).into(), + 2 + )); + + assert_eq!(voter_ids(), vec![2, 3, 0, 5]); + assert_eq!(Elections::all_approvals_of(&4).len(), 0); + assert_eq!(Balances::total_balance(&4), 40); + }); + } + + #[test] + fn attempting_to_retract_inactive_voter_by_nonvoter_should_not_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + 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)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(6); + assert_ok!(Elections::present_winner(Origin::signed(4), 2, 20, 0)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(8); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![true], 1, 0)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(10); + assert_ok!(Elections::present_winner(Origin::signed(4), 5, 50, 1)); + assert_ok!(Elections::end_block(System::block_number())); + + assert_noop!(Elections::reap_inactive_voter(Origin::signed(4), + 0, + 2, (voter_ids().iter().position(|&i| i == 2).unwrap() as u32).into(), + 2 + ), "reporter must be a voter"); + }); + } + + #[test] + fn presenting_loser_should_not_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + 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)); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 1)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![false, true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 2)); + assert_ok!(Elections::set_approvals(Origin::signed(3), vec![false, false, true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(4), 3)); + assert_ok!(Elections::set_approvals(Origin::signed(4), vec![false, false, false, true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 4)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, false, false, false, true], 0, 0)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(6); + assert_ok!(Elections::present_winner(Origin::signed(4), 1, 60, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 3, 30, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 4, 40, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 5, 50, 0)); + + assert_eq!(Elections::leaderboard(), Some(vec![ + (30, 3), + (40, 4), + (50, 5), + (60, 1) + ])); + + assert_noop!(Elections::present_winner(Origin::signed(4), 2, 20, 0), "candidate not worthy of leaderboard"); + }); + } + + #[test] + fn presenting_loser_first_should_not_matter() { + with_externalities(&mut ExtBuilder::default().build(), || { + 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)); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 1)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![false, true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 2)); + assert_ok!(Elections::set_approvals(Origin::signed(3), vec![false, false, true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(4), 3)); + assert_ok!(Elections::set_approvals(Origin::signed(4), vec![false, false, false, true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 4)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, false, false, false, true], 0, 0)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(6); + assert_ok!(Elections::present_winner(Origin::signed(4), 2, 20, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 1, 60, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 3, 30, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 4, 40, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 5, 50, 0)); + + assert_eq!(Elections::leaderboard(), Some(vec![ + (30, 3), + (40, 4), + (50, 5), + (60, 1) + ])); + }); + } + + #[test] + fn present_outside_of_presentation_period_should_not_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + System::set_block_number(4); + assert!(!Elections::presentation_active()); + assert_noop!( + Elections::present_winner(Origin::signed(5), 5, 1, 0), + "cannot present outside of presentation period" + ); + }); + } + + #[test] + fn present_with_invalid_vote_index_should_not_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + System::set_block_number(4); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 1)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true, false], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, true], 0, 0)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(6); + assert_noop!(Elections::present_winner(Origin::signed(4), 2, 20, 1), "index not current"); + }); + } + + #[test] + fn present_when_presenter_is_poor_should_not_work() { + let test_present = |p| { + with_externalities(&mut 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)); // -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)); + } + }); + }; + test_present(4); + test_present(6); + } + + #[test] + fn invalid_present_tally_should_slash() { + with_externalities(&mut ExtBuilder::default().build(), || { + System::set_block_number(4); + assert!(!Elections::presentation_active()); + assert_eq!(Balances::total_balance(&4), 40); + + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 1)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true, false], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, true], 0, 0)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(6); + assert_err!(Elections::present_winner(Origin::signed(4), 2, 80, 0), "incorrect total"); + + assert_eq!(Balances::total_balance(&4), 38); + }); + } + + #[test] + fn runners_up_should_be_kept() { + with_externalities(&mut ExtBuilder::default().build(), || { + System::set_block_number(4); + assert!(!Elections::presentation_active()); + + assert_ok!(Elections::submit_candidacy(Origin::signed(1), 0)); + assert_ok!(Elections::set_approvals(Origin::signed(6), vec![true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 1)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![false, true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 2)); + assert_ok!(Elections::set_approvals(Origin::signed(3), vec![false, false, true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(4), 3)); + assert_ok!(Elections::set_approvals(Origin::signed(4), vec![false, false, false, true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 4)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, false, false, false, true], 0, 0)); + + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(6); + assert!(Elections::presentation_active()); + assert_ok!(Elections::present_winner(Origin::signed(4), 1, 60, 0)); + // leaderboard length is the empty seats plus the carry count (i.e. 5 + 2), where those + // to be carried are the lowest and stored in lowest indices + assert_eq!(Elections::leaderboard(), Some(vec![ + (0, 0), + (0, 0), + (0, 0), + (60, 1) + ])); + assert_ok!(Elections::present_winner(Origin::signed(4), 3, 30, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 4, 40, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 5, 50, 0)); + assert_eq!(Elections::leaderboard(), Some(vec![ + (30, 3), + (40, 4), + (50, 5), + (60, 1) + ])); + + assert_ok!(Elections::end_block(System::block_number())); + + assert!(!Elections::presentation_active()); + assert_eq!(Elections::members(), vec![(1, 11), (5, 11)]); + + assert!(!Elections::is_a_candidate(&1)); + assert!(!Elections::is_a_candidate(&5)); + assert!(!Elections::is_a_candidate(&2)); + assert!(Elections::is_a_candidate(&3)); + assert!(Elections::is_a_candidate(&4)); + assert_eq!(Elections::vote_index(), 1); + assert_eq!(Elections::voter_info(2), Some(VoterInfo { last_win: 0, last_active: 0, stake: 20, pot: 0 })); + assert_eq!(Elections::voter_info(3), Some(VoterInfo { last_win: 0, last_active: 0, stake: 30, pot: 0 })); + assert_eq!(Elections::voter_info(4), Some(VoterInfo { last_win: 0, last_active: 0, stake: 40, pot: 0 })); + assert_eq!(Elections::voter_info(5), Some(VoterInfo { last_win: 1, last_active: 0, stake: 50, pot: 0 })); + assert_eq!(Elections::voter_info(6), Some(VoterInfo { last_win: 1, last_active: 0, stake: 60, pot: 0 })); + assert_eq!(Elections::candidate_reg_info(3), Some((0, 2))); + assert_eq!(Elections::candidate_reg_info(4), Some((0, 3))); + }); + } + + #[test] + fn second_tally_should_use_runners_up() { + with_externalities(&mut ExtBuilder::default().build(), || { + 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)); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 1)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![false, true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 2)); + assert_ok!(Elections::set_approvals(Origin::signed(3), vec![false, false, true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(4), 3)); + assert_ok!(Elections::set_approvals(Origin::signed(4), vec![false, false, false, true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 4)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, false, false, false, true], 0, 0)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(6); + assert_ok!(Elections::present_winner(Origin::signed(4), 1, 60, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 3, 30, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 4, 40, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 5, 50, 0)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(8); + assert_ok!(Elections::set_approvals(Origin::signed(6), vec![false, false, true, false], 1, 0)); + assert_ok!(Elections::set_desired_seats(Origin::ROOT, 3)); + assert_ok!(Elections::end_block(System::block_number())); + + System::set_block_number(10); + assert_ok!(Elections::present_winner(Origin::signed(4), 3, 30 + Elections::get_offset(30, 1) + 60, 1)); + assert_ok!(Elections::present_winner(Origin::signed(4), 4, 40 + Elections::get_offset(40, 1), 1)); + assert_ok!(Elections::end_block(System::block_number())); + + assert!(!Elections::presentation_active()); + assert_eq!(Elections::members(), vec![(1, 11), (5, 11), (3, 15)]); + + assert!(!Elections::is_a_candidate(&1)); + assert!(!Elections::is_a_candidate(&2)); + assert!(!Elections::is_a_candidate(&3)); + assert!(!Elections::is_a_candidate(&5)); + assert!(Elections::is_a_candidate(&4)); + assert_eq!(Elections::vote_index(), 2); + assert_eq!(Elections::voter_info(2), Some( VoterInfo { last_win: 0, last_active: 0, stake: 20, pot: 0})); + assert_eq!(Elections::voter_info(3), Some( VoterInfo { last_win: 2, last_active: 0, stake: 30, pot: 0})); + assert_eq!(Elections::voter_info(4), Some( VoterInfo { last_win: 0, last_active: 0, stake: 40, pot: 0})); + assert_eq!(Elections::voter_info(5), Some( VoterInfo { last_win: 1, last_active: 0, stake: 50, pot: 0})); + assert_eq!( + Elections::voter_info(6), + Some(VoterInfo { last_win: 2, last_active: 1, stake: 60, pot: 0}) + ); + + assert_eq!(Elections::candidate_reg_info(4), Some((0, 3))); + }); + } +} diff --git a/srml/example/Cargo.toml b/srml/example/Cargo.toml index 44a6a85e47e0f0c408bba993990f6f5f1b804663..f46ce5474924274de827bf42031be85341f2001c 100644 --- a/srml/example/Cargo.toml +++ b/srml/example/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" [dependencies] serde = { version = "1.0", optional = true } -parity-codec = { version = "3.3", default-features = false } +parity-codec = { version = "4.1.1", default-features = false } srml-support = { path = "../support", default-features = false } system = { package = "srml-system", path = "../system", default-features = false } balances = { package = "srml-balances", path = "../balances", default-features = false } diff --git a/srml/example/src/lib.rs b/srml/example/src/lib.rs index dd14b7198acd3aee381228b1064bfdd0c75279bc..a64cfc0af090ebfc48cde46af8ec196792fd008e 100644 --- a/srml/example/src/lib.rs +++ b/srml/example/src/lib.rs @@ -254,8 +254,8 @@ #![cfg_attr(not(feature = "std"), no_std)] use srml_support::{StorageValue, dispatch::Result, decl_module, decl_storage, decl_event}; -use system::ensure_signed; -use sr_primitives::weights::TransactionWeight; +use system::{ensure_signed, ensure_root}; +use sr_primitives::weights::SimpleDispatchInfo; /// Our module's configuration trait. All our types and consts go in here. If the /// module is dependent on specific other modules, then their configuration traits @@ -396,19 +396,18 @@ decl_module! { // // If you don't respect these rules, it is likely that your chain will be attackable. // - // Each transaction can optionally indicate a weight. The weight is passed in as a - // custom attribute and the value can be anything that implements the `Weighable` - // trait. Most often using substrate's default `TransactionWeight` is enough for you. + // Each transaction can define an optional `#[weight]` attribute to convey a set of static + // information about its dispatch. The `system` and `executive` module then use this + // information to properly execute the transaction, whilst keeping the total load of the + // chain in a moderate rate. // - // A basic weight is a tuple of `(base_weight, byte_weight)`. Upon including each transaction - // in a block, the final weight is calculated as `base_weight + byte_weight * tx_size`. - // If this value, added to the weight of all included transactions, exceeds `MAX_TRANSACTION_WEIGHT`, - // the transaction is not included. If no weight attribute is provided, the `::default()` - // implementation of `TransactionWeight` is used. - // - // The example below showcases a transaction which is relatively costly, but less dependent on - // the input, hence `byte_weight` is configured smaller. - #[weight = TransactionWeight::Basic(100_000, 10)] + // 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. + #[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. let _sender = ensure_signed(origin)?; @@ -440,7 +439,7 @@ decl_module! { } /// A privileged call; in this case it resets our dummy value to something new. - // Implementation of a privileged call. This doesn't have an `origin` parameter because + // Implementation of a privileged call. The `origin` parameter is ROOT because // it's not (directly) from an extrinsic, but rather the system as a whole has decided // to execute it. Different runtimes have different reasons for allow privileged // calls to be executed - we don't need to care why. Because it's privileged, we can @@ -448,7 +447,8 @@ 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. - fn set_dummy(#[compact] new_value: T::Balance) { + fn set_dummy(origin, #[compact] new_value: T::Balance) { + ensure_root(origin)?; // Put the new value into storage. >::put(new_value); } @@ -504,14 +504,13 @@ impl Module { mod tests { use super::*; - use srml_support::{impl_outer_origin, assert_ok}; + use srml_support::{assert_ok, impl_outer_origin, parameter_types}; use sr_io::with_externalities; use substrate_primitives::{H256, Blake2Hasher}; // 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::{ - BuildStorage, traits::{BlakeTwo256, OnInitialize, OnFinalize, IdentityLookup}, - testing::Header + traits::{BlakeTwo256, OnInitialize, OnFinalize, IdentityLookup}, testing::Header }; impl_outer_origin! { @@ -523,6 +522,11 @@ mod tests { // 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; + } impl system::Trait for Test { type Origin = Origin; type Index = u64; @@ -532,7 +536,18 @@ mod tests { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; + type WeightMultiplierUpdate = (); type Event = (); + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; + } + 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; @@ -542,6 +557,11 @@ mod tests { type TransactionPayment = (); type TransferPayment = (); type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type TransferFee = TransferFee; + type CreationFee = CreationFee; + type TransactionBaseFee = TransactionBaseFee; + type TransactionByteFee = TransactionByteFee; } impl Trait for Test { type Event = (); @@ -551,7 +571,7 @@ mod tests { // This function basically just builds a genesis storage key/value store according to // our desired mockup. fn new_test_ext() -> sr_io::TestExternalities { - let mut t = system::GenesisConfig::::default().build_storage().unwrap().0; + let mut t = system::GenesisConfig::default().build_storage::().unwrap().0; // We use default for brevity, but you can configure as desired if needed. t.extend(balances::GenesisConfig::::default().build_storage().unwrap().0); t.extend(GenesisConfig::{ diff --git a/srml/executive/Cargo.toml b/srml/executive/Cargo.toml index 7089a2aa9b973b363705041792c54d1bf985206d..27057fe523ab39b01baa4d57f085c0b991dd7c21 100644 --- a/srml/executive/Cargo.toml +++ b/srml/executive/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" [dependencies] serde = { version = "1.0", optional = true } -parity-codec = { version = "3.3", default-features = false, features = ["derive"] } +parity-codec = { version = "4.1.1", 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 } primitives = { package = "sr-primitives", path = "../../core/sr-primitives", default-features = false } diff --git a/srml/executive/src/lib.rs b/srml/executive/src/lib.rs index f4299abe476306dba563bf27491eb57f3ca3fd6d..326c3d7ab95d9101081aad8a20b0dfd3cf46b394 100644 --- a/srml/executive/src/lib.rs +++ b/srml/executive/src/lib.rs @@ -69,7 +69,7 @@ //! # } //! # } //! /// Executive: handles dispatch to the various modules. -//! pub type Executive = executive::Executive; +//! pub type Executive = executive::Executive; //! ``` #![cfg_attr(not(feature = "std"), no_std)] @@ -79,18 +79,17 @@ use rstd::marker::PhantomData; use rstd::result; use primitives::{generic::Digest, traits::{ self, Header, Zero, One, Checkable, Applyable, CheckEqual, OnFinalize, - OnInitialize, NumberFor, Block as BlockT, OffchainWorker, - ValidateUnsigned, + OnInitialize, NumberFor, Block as BlockT, OffchainWorker, ValidateUnsigned }}; -use srml_support::{Dispatchable, traits::MakePayment}; +use srml_support::Dispatchable; use parity_codec::{Codec, Encode}; use system::{extrinsics_root, DigestOf}; use primitives::{ApplyOutcome, ApplyError}; -use primitives::transaction_validity::{TransactionValidity, TransactionPriority, TransactionLongevity}; -use primitives::weights::Weighable; +use primitives::transaction_validity::TransactionValidity; +use primitives::weights::GetDispatchInfo; mod internal { - pub const MAX_TRANSACTIONS_WEIGHT: u32 = 4 * 1024 * 1024; + use primitives::traits::DispatchError; pub enum ApplyError { BadSignature(&'static str), @@ -104,6 +103,19 @@ mod internal { Success, Fail(&'static str), } + + impl From for ApplyError { + fn from(d: DispatchError) -> Self { + match d { + DispatchError::Payment => ApplyError::CantPay, + DispatchError::NoPermission => ApplyError::CantPay, + DispatchError::BadState => ApplyError::CantPay, + DispatchError::Stale => ApplyError::Stale, + DispatchError::Future => ApplyError::Future, + DispatchError::BadProof => ApplyError::BadSignature(""), + } + } + } } /// Trait that can be used to execute a block. @@ -116,27 +128,26 @@ pub type CheckedOf = >::Checked; pub type CallOf = as Applyable>::Call; pub type OriginOf = as Dispatchable>::Origin; -pub struct Executive( - PhantomData<(System, Block, Context, Payment, UnsignedValidator, AllModules)> +pub struct Executive( + PhantomData<(System, Block, Context, UnsignedValidator, AllModules)> ); impl< System: system::Trait, Block: traits::Block, Context: Default, - Payment: MakePayment, UnsignedValidator, AllModules: OnInitialize + OnFinalize + OffchainWorker, -> ExecuteBlock for Executive +> ExecuteBlock for Executive where Block::Extrinsic: Checkable + Codec, - CheckedOf: Applyable + Weighable, + CheckedOf: Applyable + GetDispatchInfo, CallOf: Dispatchable, OriginOf: From>, UnsignedValidator: ValidateUnsigned>, { fn execute_block(block: Block) { - Executive::::execute_block(block); + Executive::::execute_block(block); } } @@ -144,13 +155,12 @@ impl< System: system::Trait, Block: traits::Block, Context: Default, - Payment: MakePayment, UnsignedValidator, AllModules: OnInitialize + OnFinalize + OffchainWorker, -> Executive +> Executive where Block::Extrinsic: Checkable + Codec, - CheckedOf: Applyable + Weighable, + CheckedOf: Applyable + GetDispatchInfo, CallOf: Dispatchable, OriginOf: From>, UnsignedValidator: ValidateUnsigned>, @@ -264,37 +274,20 @@ where // Verify that the signature is good. let xt = uxt.check(&Default::default()).map_err(internal::ApplyError::BadSignature)?; - // Check the weight of the block if that extrinsic is applied. - let weight = xt.weight(encoded_len); - if >::all_extrinsics_weight() + weight > internal::MAX_TRANSACTIONS_WEIGHT { - return Err(internal::ApplyError::FullBlock); - } - - if let (Some(sender), Some(index)) = (xt.sender(), xt.index()) { - // check index - let expected_index = >::account_nonce(sender); - if index != &expected_index { return Err( - if index < &expected_index { internal::ApplyError::Stale } else { internal::ApplyError::Future } - ) } - // pay any fees - Payment::make_payment(sender, encoded_len).map_err(|_| internal::ApplyError::CantPay)?; - - // AUDIT: Under no circumstances may this function panic from here onwards. - // FIXME: ensure this at compile-time (such as by not defining a panic function, forcing - // a linker error unless the compiler can prove it cannot be called). - // increment nonce in storage - >::inc_account_nonce(sender); - } - - // Make sure to `note_extrinsic` only after we know it's going to be executed - // to prevent it from leaking in storage. + // We don't need to make sure to `note_extrinsic` only after we know it's going to be + // executed to prevent it from leaking in storage since at this point, it will either + // execute or panic (and revert storage changes). if let Some(encoded) = to_note { >::note_extrinsic(encoded); } + // AUDIT: Under no circumstances may this function panic from here onwards. + // Decode parameters and dispatch - let (f, s) = xt.deconstruct(); - let r = f.dispatch(s.into()); + let dispatch_info = xt.get_dispatch_info(); + let r = Applyable::dispatch(xt, dispatch_info, encoded_len) + .map_err(internal::ApplyError::from)?; + >::note_applied_extrinsic(&r, encoded_len as u32); r.map(|_| internal::ApplyOutcome::Success).or_else(|e| match e { @@ -332,11 +325,9 @@ where pub fn validate_transaction(uxt: Block::Extrinsic) -> TransactionValidity { // Note errors > 0 are from ApplyError const UNKNOWN_ERROR: i8 = -127; - const MISSING_SENDER: i8 = -20; const INVALID_INDEX: i8 = -10; - let encoded_len = uxt.encode().len(); - + let encoded_len = uxt.using_encoded(|d| d.len()); let xt = match uxt.check(&Default::default()) { // Checks out. Carry on. Ok(xt) => xt, @@ -348,39 +339,8 @@ where Err(_) => return TransactionValidity::Invalid(UNKNOWN_ERROR), }; - match (xt.sender(), xt.index()) { - (Some(sender), Some(index)) => { - // pay any fees - if Payment::make_payment(sender, encoded_len).is_err() { - return TransactionValidity::Invalid(ApplyError::CantPay as i8) - } - - // check index - let expected_index = >::account_nonce(sender); - if index < &expected_index { - return TransactionValidity::Invalid(ApplyError::Stale as i8) - } - - let index = *index; - let provides = vec![(sender, index).encode()]; - let requires = if expected_index < index { - vec![(sender, index - One::one()).encode()] - } else { - vec![] - }; - - TransactionValidity::Valid { - priority: encoded_len as TransactionPriority, - requires, - provides, - longevity: TransactionLongevity::max_value(), - propagate: true, - } - }, - (None, None) => UnsignedValidator::validate_unsigned(&xt.deconstruct().0), - (Some(_), None) => TransactionValidity::Invalid(INVALID_INDEX), - (None, Some(_)) => TransactionValidity::Invalid(MISSING_SENDER), - } + let dispatch_info = xt.get_dispatch_info(); + xt.validate::(dispatch_info, encoded_len) } /// Start an offchain worker and generate extrinsics. @@ -389,16 +349,18 @@ where } } + #[cfg(test)] mod tests { use super::*; use balances::Call; use runtime_io::with_externalities; use substrate_primitives::{H256, Blake2Hasher}; - use primitives::BuildStorage; + use primitives::generic::Era; use primitives::traits::{Header as HeaderT, BlakeTwo256, IdentityLookup}; use primitives::testing::{Digest, Header, Block}; - use srml_support::{traits::Currency, impl_outer_origin, impl_outer_event}; + use srml_support::{impl_outer_event, impl_outer_origin, parameter_types}; + use srml_support::traits::{Currency, LockIdentifier, LockableCurrency, WithdrawReasons, WithdrawReason, Get}; use system; use hex_literal::hex; @@ -416,6 +378,11 @@ mod tests { // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. #[derive(Clone, Eq, PartialEq)] pub struct Runtime; + parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + } impl system::Trait for Runtime { type Origin = Origin; type Index = u64; @@ -426,6 +393,17 @@ mod tests { type Lookup = IdentityLookup; type Header = Header; type Event = MetaEvent; + type BlockHashCount = BlockHashCount; + type WeightMultiplierUpdate = (); + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; + } + parameter_types! { + 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; @@ -435,6 +413,11 @@ mod tests { type TransactionPayment = (); type DustRemoval = (); type TransferPayment = (); + type ExistentialDeposit = ExistentialDeposit; + type TransferFee = TransferFee; + type CreationFee = CreationFee; + type TransactionBaseFee = TransactionBaseFee; + type TransactionByteFee = TransactionByteFee; } impl ValidateUnsigned for Runtime { @@ -442,42 +425,44 @@ mod tests { fn validate_unsigned(call: &Self::Call) -> TransactionValidity { match call { - Call::set_balance(_, _, _) => TransactionValidity::Valid { - priority: 0, - requires: vec![], - provides: vec![], - longevity: std::u64::MAX, - propagate: false, - }, + Call::set_balance(_, _, _) => TransactionValidity::Valid(Default::default()), _ => TransactionValidity::Invalid(0), } } } - type TestXt = primitives::testing::TestXt>; - type Executive = super::Executive< - Runtime, - Block, - system::ChainContext, - balances::Module, - Runtime, - () - >; + type SignedExtra = ( + system::CheckEra, + system::CheckNonce, + system::CheckWeight, + balances::TakeFees + ); + type TestXt = primitives::testing::TestXt, SignedExtra>; + type Executive = super::Executive, system::ChainContext, Runtime, ()>; + + fn extra(nonce: u64, fee: u64) -> SignedExtra { + ( + system::CheckEra::from(Era::Immortal), + system::CheckNonce::from(nonce), + system::CheckWeight::from(), + balances::TakeFees::from(fee) + ) + } + + fn sign_extra(who: u64, nonce: u64, fee: u64) -> Option<(u64, SignedExtra)> { + Some((who, extra(nonce, fee))) + } #[test] fn balance_transfer_dispatch_works() { - let mut t = system::GenesisConfig::::default().build_storage().unwrap().0; - t.extend(balances::GenesisConfig:: { - transaction_base_fee: 10, - transaction_byte_fee: 0, + let mut t = system::GenesisConfig::default().build_storage::().unwrap(); + balances::GenesisConfig:: { balances: vec![(1, 111)], - existential_deposit: 0, - transfer_fee: 0, - creation_fee: 0, vesting: vec![], - }.build_storage().unwrap().0); - let xt = primitives::testing::TestXt(Some(1), 0, Call::transfer(2, 69)); - let mut t = runtime_io::TestExternalities::::new(t); + }.assimilate_storage(&mut t.0, &mut t.1).unwrap(); + let xt = primitives::testing::TestXt(sign_extra(1, 0, 0), Call::transfer(2, 69)); + let weight = xt.get_dispatch_info().weight as u64; + let mut t = runtime_io::TestExternalities::::new_with_children(t); with_externalities(&mut t, || { Executive::initialize_block(&Header::new( 1, @@ -486,26 +471,30 @@ mod tests { [69u8; 32].into(), Digest::default(), )); - Executive::apply_extrinsic(xt).unwrap(); - assert_eq!(>::total_balance(&1), 32); + let r = Executive::apply_extrinsic(xt); + assert_eq!(r, Ok(ApplyOutcome::Success)); + assert_eq!(>::total_balance(&1), 42 - 10 - weight); assert_eq!(>::total_balance(&2), 69); }); } - fn new_test_ext() -> runtime_io::TestExternalities { - let mut t = system::GenesisConfig::::default().build_storage().unwrap().0; - t.extend(balances::GenesisConfig::::default().build_storage().unwrap().0); + fn new_test_ext(balance_factor: u64) -> runtime_io::TestExternalities { + let mut t = system::GenesisConfig::default().build_storage::().unwrap().0; + t.extend(balances::GenesisConfig:: { + balances: vec![(1, 111 * balance_factor)], + vesting: vec![], + }.build_storage().unwrap().0); t.into() } #[test] fn block_import_works() { - with_externalities(&mut new_test_ext(), || { + with_externalities(&mut new_test_ext(1), || { Executive::execute_block(Block { header: Header { parent_hash: [69u8; 32].into(), number: 1, - state_root: hex!("5ba497e45e379d80a4524f9509d224e9c175d0fa30f3491481e7e44a6a758adf").into(), + state_root: hex!("ba811447b8ae3bf798a07a18f5355ea59926917c8a9cc7527ede20b261aacfdf").into(), extrinsics_root: hex!("03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").into(), digest: Digest { logs: vec![], }, }, @@ -517,7 +506,7 @@ mod tests { #[test] #[should_panic] fn block_import_of_bad_state_root_fails() { - with_externalities(&mut new_test_ext(), || { + with_externalities(&mut new_test_ext(1), || { Executive::execute_block(Block { header: Header { parent_hash: [69u8; 32].into(), @@ -534,7 +523,7 @@ mod tests { #[test] #[should_panic] fn block_import_of_bad_extrinsic_root_fails() { - with_externalities(&mut new_test_ext(), || { + with_externalities(&mut new_test_ext(1), || { Executive::execute_block(Block { header: Header { parent_hash: [69u8; 32].into(), @@ -550,8 +539,9 @@ mod tests { #[test] fn bad_extrinsic_not_inserted() { - let mut t = new_test_ext(); - let xt = primitives::testing::TestXt(Some(1), 42, Call::transfer(33, 69)); + let mut t = new_test_ext(1); + // bad nonce check! + let xt = primitives::testing::TestXt(sign_extra(1, 30, 0), Call::transfer(33, 69)); with_externalities(&mut t, || { Executive::initialize_block(&Header::new( 1, @@ -567,71 +557,110 @@ mod tests { #[test] fn block_weight_limit_enforced() { - let run_test = |should_fail: bool| { - let mut t = new_test_ext(); - let xt = primitives::testing::TestXt(Some(1), 0, Call::transfer(33, 69)); - let xt2 = primitives::testing::TestXt(Some(1), 1, Call::transfer(33, 69)); - let encoded = xt2.encode(); - let len = if should_fail { (internal::MAX_TRANSACTIONS_WEIGHT - 1) as usize } else { encoded.len() }; - with_externalities(&mut t, || { - Executive::initialize_block(&Header::new( - 1, - H256::default(), - H256::default(), - [69u8; 32].into(), - Digest::default(), - )); - assert_eq!(>::all_extrinsics_weight(), 0); - - Executive::apply_extrinsic(xt).unwrap(); - let res = Executive::apply_extrinsic_with_len(xt2, len, Some(encoded)); - - if should_fail { - assert!(res.is_err()); - assert_eq!(>::all_extrinsics_weight(), 28); - assert_eq!(>::extrinsic_index(), Some(1)); + let mut t = new_test_ext(10000); + // given: TestXt uses the encoded len as fixed Len: + let xt = primitives::testing::TestXt(sign_extra(1, 0, 0), Call::transfer::(33, 0)); + let encoded = xt.encode(); + let encoded_len = encoded.len() as u32; + let limit = >::get() / 4; + let num_to_exhaust_block = limit / encoded_len; + with_externalities(&mut t, || { + Executive::initialize_block(&Header::new( + 1, + H256::default(), + H256::default(), + [69u8; 32].into(), + Digest::default(), + )); + assert_eq!(>::all_extrinsics_weight(), 0); + + for nonce in 0..=num_to_exhaust_block { + let xt = primitives::testing::TestXt(sign_extra(1, nonce.into(), 0), Call::transfer::(33, 0)); + let res = Executive::apply_extrinsic(xt); + if nonce != num_to_exhaust_block { + assert_eq!(res.unwrap(), ApplyOutcome::Success); + assert_eq!(>::all_extrinsics_weight(), encoded_len * (nonce + 1)); + assert_eq!(>::extrinsic_index(), Some(nonce + 1)); } else { - assert!(res.is_ok()); - assert_eq!(>::all_extrinsics_weight(), 56); - assert_eq!(>::extrinsic_index(), Some(2)); + assert_eq!(res, Err(ApplyError::CantPay)); } - }); - }; - - run_test(false); - run_test(true); + } + }); } #[test] - fn default_block_weight() { - let xt = primitives::testing::TestXt(None, 0, Call::set_balance(33, 69, 69)); - let mut t = new_test_ext(); + fn block_weight_and_size_is_stored_per_tx() { + let xt = primitives::testing::TestXt(sign_extra(1, 0, 0), Call::transfer(33, 0)); + let x1 = primitives::testing::TestXt(sign_extra(1, 1, 0), Call::transfer(33, 0)); + let x2 = primitives::testing::TestXt(sign_extra(1, 2, 0), Call::transfer(33, 0)); + let len = xt.clone().encode().len() as u32; + let mut t = new_test_ext(1); with_externalities(&mut t, || { - Executive::apply_extrinsic(xt.clone()).unwrap(); - Executive::apply_extrinsic(xt.clone()).unwrap(); - Executive::apply_extrinsic(xt.clone()).unwrap(); - assert_eq!( - >::all_extrinsics_weight(), - 3 * (0 /*base*/ + 22 /*len*/ * 1 /*byte*/) - ); + assert_eq!(>::all_extrinsics_weight(), 0); + assert_eq!(>::all_extrinsics_weight(), 0); + + assert_eq!(Executive::apply_extrinsic(xt.clone()).unwrap(), ApplyOutcome::Success); + assert_eq!(Executive::apply_extrinsic(x1.clone()).unwrap(), ApplyOutcome::Success); + assert_eq!(Executive::apply_extrinsic(x2.clone()).unwrap(), ApplyOutcome::Success); + + // default weight for `TestXt` == encoded length. + assert_eq!( >::all_extrinsics_weight(), 3 * len); + assert_eq!(>::all_extrinsics_len(), 3 * len); + + let _ = >::finalize(); + + assert_eq!(>::all_extrinsics_weight(), 0); + assert_eq!(>::all_extrinsics_weight(), 0); }); } #[test] fn validate_unsigned() { - let xt = primitives::testing::TestXt(None, 0, Call::set_balance(33, 69, 69)); - let valid = TransactionValidity::Valid { - priority: 0, - requires: vec![], - provides: vec![], - longevity: 18446744073709551615, - propagate: false, - }; - let mut t = new_test_ext(); + let xt = primitives::testing::TestXt(None, Call::set_balance(33, 69, 69)); + let valid = TransactionValidity::Valid(Default::default()); + let mut t = new_test_ext(1); with_externalities(&mut t, || { assert_eq!(Executive::validate_transaction(xt.clone()), valid); assert_eq!(Executive::apply_extrinsic(xt), Ok(ApplyOutcome::Fail)); }); } + + #[test] + fn can_pay_for_tx_fee_on_full_lock() { + let id: LockIdentifier = *b"0 "; + let execute_with_lock = |lock: WithdrawReasons| { + let mut t = new_test_ext(1); + with_externalities(&mut t, || { + as LockableCurrency>::set_lock( + id, + &1, + 110, + 10, + lock, + ); + let xt = primitives::testing::TestXt(sign_extra(1, 0, 0), Call::transfer(2, 10)); + let weight = xt.get_dispatch_info().weight as u64; + Executive::initialize_block(&Header::new( + 1, + H256::default(), + H256::default(), + [69u8; 32].into(), + Digest::default(), + )); + + if lock == WithdrawReasons::except(WithdrawReason::TransactionPayment) { + assert_eq!(Executive::apply_extrinsic(xt).unwrap(), ApplyOutcome::Fail); + // but tx fee has been deducted. the transaction failed on transfer, not on fee. + assert_eq!(>::total_balance(&1), 111 - 10 - weight); + } else { + assert_eq!(Executive::apply_extrinsic(xt), Err(ApplyError::CantPay)); + assert_eq!(>::total_balance(&1), 111); + } + }); + }; + + execute_with_lock(WithdrawReasons::all()); + execute_with_lock(WithdrawReasons::except(WithdrawReason::TransactionPayment)); + } } diff --git a/srml/finality-tracker/Cargo.toml b/srml/finality-tracker/Cargo.toml index e6cf47ab25a26db462e35d96a8a3e344e9446cd8..e7eea0e152676dcde2ab5340759ce102f7c0de06 100644 --- a/srml/finality-tracker/Cargo.toml +++ b/srml/finality-tracker/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" [dependencies] serde = { version = "1.0", default-features = false, features = ["derive"] } -parity-codec = { version = "3.3", default-features = false } +parity-codec = { version = "4.1.1", default-features = false } inherents = { package = "substrate-inherents", path = "../../core/inherents", default-features = false } rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false } primitives = { package = "sr-primitives", path = "../../core/sr-primitives", default-features = false } @@ -16,8 +16,6 @@ srml-system = { path = "../system", default-features = false } [dev-dependencies] substrate-primitives = { path = "../../core/primitives", default-features = false } sr-io = { path = "../../core/sr-io", default-features = false } -lazy_static = "1.0" -parking_lot = "0.8.0" [features] default = ["std"] diff --git a/srml/finality-tracker/src/lib.rs b/srml/finality-tracker/src/lib.rs index 6442fee543ab4dc7d996907c7167af3f3d91c4e2..38377f35006efafdecd75e4afeb97dcc9c8211d1 100644 --- a/srml/finality-tracker/src/lib.rs +++ b/srml/finality-tracker/src/lib.rs @@ -18,9 +18,6 @@ #![cfg_attr(not(feature = "std"), no_std)] -#[macro_use] -extern crate srml_support; - use inherents::{ RuntimeString, InherentIdentifier, ProvideInherent, InherentData, MakeFatalError, @@ -29,14 +26,13 @@ use srml_support::StorageValue; use primitives::traits::{One, Zero, SaturatedConversion}; use rstd::{prelude::*, result, cmp, vec}; use parity_codec::Decode; +use srml_support::{decl_module, decl_storage, for_each_tuple}; +use srml_support::traits::Get; use srml_system::{ensure_none, Trait as SystemTrait}; #[cfg(feature = "std")] use parity_codec::Encode; -const DEFAULT_WINDOW_SIZE: u32 = 101; -const DEFAULT_DELAY: u32 = 1000; - /// The identifier for the `finalnum` inherent. pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"finalnum"; @@ -85,10 +81,17 @@ impl inherents::ProvideInherentData for InherentDataProvider } } +pub const DEFAULT_WINDOW_SIZE: u32 = 101; +pub const DEFAULT_REPORT_LATENCY: u32 = 1000; pub trait Trait: SystemTrait { - /// Something which can be notified when the timestamp is set. Set this to `()` if not needed. + /// Something which can be notified when the timestamp is set. Set this to `()` + /// if not needed. type OnFinalizationStalled: OnFinalizationStalled; + /// The number of recent samples to keep from this chain. Default is 101. + type WindowSize: Get; + /// The delay after which point things become suspicious. Default is 1000. + type ReportLatency: Get; } decl_storage! { @@ -99,10 +102,6 @@ decl_storage! { OrderedHints get(ordered_hints) build(|_| vec![T::BlockNumber::zero()]): Vec; /// The median. Median get(median) build(|_| T::BlockNumber::zero()): T::BlockNumber; - /// The number of recent samples to keep from this chain. Default is n-100 - pub WindowSize get(window_size) config(window_size): T::BlockNumber = DEFAULT_WINDOW_SIZE.into(); - /// The delay after which point things become suspicious. - pub ReportLatency get(report_latency) config(report_latency): T::BlockNumber = DEFAULT_DELAY.into(); /// Final hint to apply in the block. `None` means "same as parent". Update: Option; @@ -114,6 +113,12 @@ decl_storage! { decl_module! { pub struct Module for enum Call where origin: T::Origin { + /// The number of recent samples to keep from this chain. Default is 101. + const WindowSize: T::BlockNumber = T::WindowSize::get(); + + /// The delay after which point things become suspicious. Default is 1000. + const ReportLatency: T::BlockNumber = T::ReportLatency::get(); + /// Hint that the author of this block thinks the best finalized /// block is the given number. fn final_hint(origin, #[compact] hint: T::BlockNumber) { @@ -144,7 +149,7 @@ impl Module { let mut recent = Self::recent_hints(); let mut ordered = Self::ordered_hints(); - let window_size = cmp::max(T::BlockNumber::one(), Self::window_size()); + let window_size = cmp::max(T::BlockNumber::one(), T::WindowSize::get()); let hint = hint.unwrap_or_else(|| recent.last() .expect("always at least one recent sample; qed").clone() @@ -196,7 +201,7 @@ impl Module { if T::BlockNumber::from(our_window_size) == window_size { let now = srml_system::Module::::block_number(); - let latency = Self::report_latency(); + let latency = T::ReportLatency::get(); // the delay is the latency plus half the window size. let delay = latency + (window_size / two); @@ -261,13 +266,11 @@ mod tests { use sr_io::{with_externalities, TestExternalities}; use substrate_primitives::H256; - use primitives::BuildStorage; use primitives::traits::{BlakeTwo256, IdentityLookup, OnFinalize, Header as HeaderT}; use primitives::testing::Header; - use srml_support::impl_outer_origin; + use srml_support::{assert_ok, impl_outer_origin, parameter_types}; use srml_system as system; - use lazy_static::lazy_static; - use parking_lot::Mutex; + use std::cell::RefCell; #[derive(Clone, PartialEq, Debug)] pub struct StallEvent { @@ -275,71 +278,72 @@ mod tests { further_wait: u64, } - macro_rules! make_test_context { - () => { - #[derive(Clone, Eq, PartialEq)] - pub struct Test; - - impl_outer_origin! { - pub enum Origin for Test {} - } - - impl system::Trait for Test { - type Origin = Origin; - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; - type Header = Header; - type Event = (); - } - - type System = system::Module; - - lazy_static! { - static ref NOTIFICATIONS: Mutex> = Mutex::new(Vec::new()); - } + #[derive(Clone, Eq, PartialEq)] + pub struct Test; - pub struct StallTracker; - impl OnFinalizationStalled for StallTracker { - fn on_stalled(further_wait: u64, _median: u64) { - let now = System::block_number(); - NOTIFICATIONS.lock().push(StallEvent { at: now, further_wait }); - } - } + impl_outer_origin! { + pub enum Origin for Test {} + } - impl Trait for Test { - type OnFinalizationStalled = StallTracker; - } + thread_local! { + static NOTIFICATIONS: RefCell> = Default::default(); + } - type FinalityTracker = Module; + pub struct StallTracker; + impl OnFinalizationStalled for StallTracker { + fn on_stalled(further_wait: u64, _median: u64) { + let now = System::block_number(); + NOTIFICATIONS.with(|v| v.borrow_mut().push(StallEvent { at: now, further_wait })); } } + parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + } + impl system::Trait for Test { + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type WeightMultiplierUpdate = (); + type Event = (); + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; + } + parameter_types! { + pub const WindowSize: u64 = 11; + pub const ReportLatency: u64 = 100; + } + impl Trait for Test { + type OnFinalizationStalled = StallTracker; + type WindowSize = WindowSize; + type ReportLatency = ReportLatency; + } + + type System = system::Module; + type FinalityTracker = Module; + #[test] fn median_works() { - make_test_context!(); - let t = system::GenesisConfig::::default().build_storage().unwrap().0; - - with_externalities(&mut TestExternalities::new(t), || { + let t = system::GenesisConfig::default().build_storage::().unwrap(); + with_externalities(&mut TestExternalities::new_with_children(t), || { FinalityTracker::update_hint(Some(500)); assert_eq!(FinalityTracker::median(), 250); - assert!(NOTIFICATIONS.lock().is_empty()); + assert!(NOTIFICATIONS.with(|n| n.borrow().is_empty())); }); } #[test] fn notifies_when_stalled() { - make_test_context!(); - let mut t = system::GenesisConfig::::default().build_storage().unwrap().0; - t.extend(GenesisConfig:: { - window_size: 11, - report_latency: 100 - }.build_storage().unwrap().0); - - with_externalities(&mut TestExternalities::new(t), || { + let t = system::GenesisConfig::default().build_storage::().unwrap(); + with_externalities(&mut TestExternalities::new_with_children(t), || { let mut parent_hash = System::parent_hash(); for i in 2..106 { System::initialize(&i, &parent_hash, &Default::default(), &Default::default()); @@ -349,7 +353,7 @@ mod tests { } assert_eq!( - NOTIFICATIONS.lock().to_vec(), + NOTIFICATIONS.with(|n| n.borrow().clone()), vec![StallEvent { at: 105, further_wait: 10 }] ) }); @@ -357,14 +361,8 @@ mod tests { #[test] fn recent_notifications_prevent_stalling() { - make_test_context!(); - let mut t = system::GenesisConfig::::default().build_storage().unwrap().0; - t.extend(GenesisConfig:: { - window_size: 11, - report_latency: 100 - }.build_storage().unwrap().0); - - with_externalities(&mut TestExternalities::new(t), || { + let t = system::GenesisConfig::default().build_storage::().unwrap(); + with_externalities(&mut TestExternalities::new_with_children(t), || { let mut parent_hash = System::parent_hash(); for i in 2..106 { System::initialize(&i, &parent_hash, &Default::default(), &Default::default()); @@ -377,7 +375,7 @@ mod tests { parent_hash = hdr.hash(); } - assert!(NOTIFICATIONS.lock().is_empty()); + assert!(NOTIFICATIONS.with(|n| n.borrow().is_empty())); }); } } diff --git a/srml/generic-asset/Cargo.toml b/srml/generic-asset/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..bfa49d8018e52484783caa7631ff17025b8baf30 --- /dev/null +++ b/srml/generic-asset/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "srml-generic-asset" +version = "2.0.0" +authors = ["Centrality Developers "] +edition = "2018" + +[dependencies] +serde = { version = "1.0", optional = true } +parity-codec = { version = "4.1.1", default-features = false, features = ["derive"] } +rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false } +primitives = { package = "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] +runtime_io = { package = "sr-io", path = "../../core/sr-io" } +substrate-primitives = { path = "../../core/primitives" } + +[features] +default = ["std"] +std =[ + "serde/std", + "parity-codec/std", + "rstd/std", + "primitives/std", + "support/std", + "system/std", +] diff --git a/srml/generic-asset/src/lib.rs b/srml/generic-asset/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..60947e68eb0070afb83d5c3d5bfc333bd0441d17 --- /dev/null +++ b/srml/generic-asset/src/lib.rs @@ -0,0 +1,1289 @@ +// Copyright 2019 +// by Centrality Investments Ltd. +// and 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 . + +//! # Generic Asset Module +//! +//! The Generic Asset module provides functionality for handling accounts and asset balances. +//! +//! ## Overview +//! +//! The Generic Asset module provides functions for: +//! +//! - Creating a new kind of asset. +//! - Setting permissions of an asset. +//! - Getting and setting free balances. +//! - Retrieving total, reserved and unreserved balances. +//! - Repatriating a reserved balance to a beneficiary account. +//! - Transferring a balance between accounts (when not reserved). +//! - Slashing an account balance. +//! - Managing total issuance. +//! - Setting and managing locks. +//! +//! ### Terminology +//! +//! - **Staking Asset:** The asset for staking, to participate as Validators in the network. +//! - **Spending Asset:** The asset for payment, such as paying transfer fees, gas fees, etc. +//! - **Permissions:** A set of rules for a kind of asset, defining the allowed operations to the asset, and which +//! accounts are allowed to possess it. +//! - **Total Issuance:** The total number of units in existence in a system. +//! - **Free Balance:** The portion of a balance that is not reserved. The free balance is the only balance that matters +//! for most operations. When this balance falls below the existential deposit, most functionality of the account is +//! removed. When both it and the reserved balance are deleted, then the account is said to be dead. +//! - **Reserved Balance:** Reserved balance still belongs to the account holder, but is suspended. Reserved balance +//! can still be slashed, but only after all the free balance has been slashed. If the reserved balance falls below the +//! existential deposit then it and any related functionality will be deleted. When both it and the free balance are +//! deleted, then the account is said to be dead. +//! - **Imbalance:** A condition when some assets were credited or debited without equal and opposite accounting +//! (i.e. a difference between total issuance and account balances). Functions that result in an imbalance will +//! return an object of the `Imbalance` trait that can be managed within your runtime logic. (If an imbalance is +//! simply dropped, it should automatically maintain any book-keeping such as total issuance.) +//! - **Lock:** A freeze on a specified amount of an account's free balance until a specified block number. Multiple +//! locks always operate over the same funds, so they "overlay" rather than "stack". +//! +//! ### Implementations +//! +//! The Generic Asset module provides `AssetCurrency`, which implements the following traits. If these traits provide +//! the functionality that you need, you can avoid coupling with the Generic Asset module. +//! +//! - `Currency`: Functions for dealing with a fungible assets system. +//! - `ReservableCurrency`: Functions for dealing with assets that can be reserved from an account. +//! - `LockableCurrency`: Functions for dealing with accounts that allow liquidity restrictions. +//! - `Imbalance`: Functions for handling imbalances between total issuance in the system and account balances. +//! Must be used when a function creates new assets (e.g. a reward) or destroys some assets (e.g. a system fee). +//! +//! The Generic Asset module provides two types of `AssetCurrency` as follows. +//! +//! - `StakingAssetCurrency`: Currency for staking. +//! - `SpendingAssetCurrency`: Currency for payments such as transfer fee, gas fee. +//! +//! ## Interface +//! +//! ### Dispatchable Functions +//! +//! - `create`: Create a new kind of asset. +//! - `transfer`: Transfer some liquid free balance to another account. +//! - `update_permission`: Updates permission for a given `asset_id` and an account. The origin of this call +//! must have update permissions. +//! - `mint`: Mint an asset, increases its total issuance. The origin of this call must have mint permissions. +//! - `burn`: Burn an asset, decreases its total issuance. The origin of this call must have burn permissions. +//! - `create_reserved`: Create a new kind of reserved asset. The origin of this call must be root. +//! +//! ### Public Functions +//! +//! - `total_balance`: Get an account's total balance of an asset kind. +//! - `free_balance`: Get an account's free balance of an asset kind. +//! - `reserved_balance`: Get an account's reserved balance of an asset kind. +//! - `create_asset`: Creates an asset. +//! - `make_transfer`: Transfer some liquid free balance from one account to another. +//! This will not emit the `Transferred` event. +//! - `make_transfer_with_event`: Transfer some liquid free balance from one account to another. +//! This will emit the `Transferred` event. +//! - `reserve`: Moves an amount from free balance to reserved balance. +//! - `unreserve`: Move up to an amount from reserved balance to free balance. This function cannot fail. +//! - `slash`: Deduct up to an amount from the combined balance of `who`, preferring to deduct from the +//! free balance. This function cannot fail. +//! - `reward`: Add up to an amount to the free balance of an account. +//! - `slash_reserved`: Deduct up to an amount from reserved balance of an account. This function cannot fail. +//! - `repatriate_reserved`: Move up to an amount from reserved balance of an account to free balance of another +//! account. +//! - `check_permission`: Check permission to perform burn, mint or update. +//! - `ensure_can_withdraw`: Check if the account is able to make a withdrawal of the given amount +//! for the given reason. +//! +//! ### Usage +//! +//! The following examples show how to use the Generic Asset module in your custom module. +//! +//! ### Examples from the PRML +//! +//! The Fees module uses the `Currency` trait to handle fee charge/refund, and its types inherit from `Currency`: +//! +//! ``` +//! use support::{ +//! traits::{Currency, ExistenceRequirement, WithdrawReason}, +//! dispatch::Result, +//! }; +//! # pub trait Trait: system::Trait { +//! # type Currency: Currency; +//! # } +//! type AssetOf = <::Currency as Currency<::AccountId>>::Balance; +//! +//! fn charge_fee(transactor: &T::AccountId, amount: AssetOf) -> Result { +//! // ... +//! T::Currency::withdraw( +//! transactor, +//! amount, +//! WithdrawReason::TransactionPayment, +//! ExistenceRequirement::KeepAlive, +//! )?; +//! // ... +//! Ok(()) +//! } +//! +//! fn refund_fee(transactor: &T::AccountId, amount: AssetOf) -> Result { +//! // ... +//! T::Currency::deposit_into_existing(transactor, amount)?; +//! // ... +//! Ok(()) +//! } +//! +//! # fn main() {} +//! ``` +//! +//! ## Genesis config +//! +//! The Generic Asset module depends on the [`GenesisConfig`](./struct.GenesisConfig.html). + +#![cfg_attr(not(feature = "std"), no_std)] + +use parity_codec::{Decode, Encode, HasCompact, Input, Output}; + +use primitives::traits::{ + CheckedAdd, CheckedSub, MaybeSerializeDebug, Member, One, Saturating, SimpleArithmetic, Zero, Bounded +}; + +use rstd::prelude::*; +use rstd::{cmp, result}; +use support::dispatch::Result; +use support::{ + decl_event, decl_module, decl_storage, ensure, + traits::{ + Currency, ExistenceRequirement, Imbalance, LockIdentifier, LockableCurrency, ReservableCurrency, + SignedImbalance, UpdateBalanceOutcome, WithdrawReason, WithdrawReasons, + }, + Parameter, StorageDoubleMap, StorageMap, StorageValue, +}; +use system::{ensure_signed, ensure_root}; + +mod mock; +mod tests; + +pub use self::imbalances::{NegativeImbalance, PositiveImbalance}; + +pub trait Trait: system::Trait { + type Balance: Parameter + + Member + + SimpleArithmetic + + Default + + Copy + + MaybeSerializeDebug; + type AssetId: Parameter + Member + SimpleArithmetic + Default + Copy; + type Event: From> + Into<::Event>; +} + +pub trait Subtrait: system::Trait { + type Balance: Parameter + + Member + + SimpleArithmetic + + Default + + Copy + + MaybeSerializeDebug; + type AssetId: Parameter + Member + SimpleArithmetic + Default + Copy; +} + +impl Subtrait for T { + type Balance = T::Balance; + type AssetId = T::AssetId; +} + +/// Asset creation options. +#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Clone, Encode, Decode, PartialEq, Eq)] +pub struct AssetOptions { + /// Initial issuance of this asset. All deposit to the creater of the asset. + #[codec(compact)] + pub initial_issuance: Balance, + /// Which accounts are allowed to possess this asset. + pub permissions: PermissionLatest, +} + +/// Owner of an asset. +#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Clone, Encode, Decode, PartialEq, Eq)] +pub enum Owner { + /// No owner. + None, + /// Owned by an AccountId + Address(AccountId), +} + +impl Default for Owner { + fn default() -> Self { + Owner::None + } +} + +/// Asset permissions +#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Clone, Encode, Decode, PartialEq, Eq)] +pub struct PermissionsV1 { + /// Who have permission to update asset permission + pub update: Owner, + /// Who have permission to mint new asset + pub mint: Owner, + /// Who have permission to burn asset + pub burn: Owner, +} + +#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Clone, Encode, Decode, PartialEq, Eq)] +#[repr(u8)] +enum PermissionVersionNumber { + V1 = 0, +} + +/// Versioned asset permission +#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Clone, PartialEq, Eq)] +pub enum PermissionVersions { + V1(PermissionsV1), +} + +/// Asset permission types +pub enum PermissionType { + /// Permission to update asset permission + Burn, + /// Permission to mint new asset + Mint, + /// Permission to burn asset + Update, +} + +/// Alias to latest asset permissions +pub type PermissionLatest = PermissionsV1; + +impl Default for PermissionVersions { + fn default() -> Self { + PermissionVersions::V1(Default::default()) + } +} + +impl Encode for PermissionVersions { + fn encode_to(&self, dest: &mut T) { + match self { + PermissionVersions::V1(payload) => { + dest.push(&PermissionVersionNumber::V1); + dest.push(payload); + }, + } + } +} + +impl Decode for PermissionVersions { + fn decode(input: &mut I) -> Option { + let version = PermissionVersionNumber::decode(input)?; + Some( + match version { + PermissionVersionNumber::V1 => PermissionVersions::V1(Decode::decode(input)?) + } + ) + } +} + +impl Default for PermissionsV1 { + fn default() -> Self { + PermissionsV1 { + update: Owner::None, + mint: Owner::None, + burn: Owner::None, + } + } +} + +impl Into> for PermissionVersions { + fn into(self) -> PermissionLatest { + match self { + PermissionVersions::V1(v1) => v1, + } + } +} + +/// Converts the latest permission to other version. +impl Into> for PermissionLatest { + fn into(self) -> PermissionVersions { + PermissionVersions::V1(self) + } +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + fn deposit_event() = default; + + /// Create a new kind of asset. + fn create(origin, options: AssetOptions) -> Result { + let origin = ensure_signed(origin)?; + let id = Self::next_asset_id(); + + let permissions: PermissionVersions = options.permissions.clone().into(); + + // The last available id serves as the overflow mark and won't be used. + let next_id = id.checked_add(&One::one()).ok_or_else(|| "No new assets id available.")?; + + >::put(next_id); + >::insert(id, &options.initial_issuance); + >::insert(&id, &origin, &options.initial_issuance); + >::insert(&id, permissions); + + Self::deposit_event(RawEvent::Created(id, origin, options)); + + Ok(()) + } + + /// Transfer some liquid free balance to another account. + pub fn transfer(origin, #[compact] asset_id: T::AssetId, to: T::AccountId, #[compact] amount: T::Balance) { + let origin = ensure_signed(origin)?; + ensure!(!amount.is_zero(), "cannot transfer zero amount"); + Self::make_transfer_with_event(&asset_id, &origin, &to, amount)?; + } + + /// Updates permission for a given `asset_id` and an account. + /// + /// The `origin` must have `update` permission. + fn update_permission( + origin, + #[compact] asset_id: T::AssetId, + new_permission: PermissionLatest + ) -> Result { + let origin = ensure_signed(origin)?; + + let permissions: PermissionVersions = new_permission.into(); + + if Self::check_permission(&asset_id, &origin, &PermissionType::Update) { + >::insert(asset_id, &permissions); + + Self::deposit_event(RawEvent::PermissionUpdated(asset_id, permissions.into())); + + Ok(()) + } else { + Err("Origin does not have enough permission to update permissions.") + } + } + + /// Mints an asset, increases its total issuance. + /// The origin must have `mint` permissions. + fn mint(origin, #[compact] asset_id: T::AssetId, to: T::AccountId, amount: T::Balance) -> Result { + let origin = ensure_signed(origin)?; + if Self::check_permission(&asset_id, &origin, &PermissionType::Mint) { + let original_free_balance = Self::free_balance(&asset_id, &to); + let current_total_issuance = >::get(asset_id); + let new_total_issuance = current_total_issuance.checked_add(&amount) + .ok_or_else(|| "total_issuance got overflow after minting.")?; + let value = original_free_balance.checked_add(&amount) + .ok_or_else(|| "free balance got overflow after minting.")?; + + >::insert(asset_id, new_total_issuance); + Self::set_free_balance(&asset_id, &to, value); + + Self::deposit_event(RawEvent::Minted(asset_id, to, amount)); + + Ok(()) + } else { + Err("The origin does not have permission to mint an asset.") + } + } + + /// Burns an asset, decreases its total issuance. + /// + /// The `origin` must have `burn` permissions. + fn burn(origin, #[compact] asset_id: T::AssetId, to: T::AccountId, amount: T::Balance) -> Result { + let origin = ensure_signed(origin)?; + + if Self::check_permission(&asset_id, &origin, &PermissionType::Burn) { + let original_free_balance = Self::free_balance(&asset_id, &to); + + let current_total_issuance = >::get(asset_id); + let new_total_issuance = current_total_issuance.checked_sub(&amount) + .ok_or_else(|| "total_issuance got underflow after burning")?; + let value = original_free_balance.checked_sub(&amount) + .ok_or_else(|| "free_balance got underflow after burning")?; + + >::insert(asset_id, new_total_issuance); + + Self::set_free_balance(&asset_id, &to, value); + + Self::deposit_event(RawEvent::Burned(asset_id, to, amount)); + + Ok(()) + } else { + Err("The origin does not have permission to burn an asset.") + } + } + + /// Can be used to create reserved tokens. + /// Requires Root call. + fn create_reserved(origin, asset_id: T::AssetId, options: AssetOptions) -> Result { + ensure_root(origin)?; + Self::create_asset(Some(asset_id), None, options) + } + } +} + +#[derive(Encode, Decode, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(Debug))] +pub struct BalanceLock { + pub id: LockIdentifier, + pub amount: Balance, + pub until: BlockNumber, + pub reasons: WithdrawReasons, +} + +decl_storage! { + trait Store for Module as GenericAsset { + /// Total issuance of a given asset. + pub TotalIssuance get(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; + + /// The free balance of a given asset under an account. + pub FreeBalance: double_map T::AssetId, twox_128(T::AccountId) => T::Balance; + + /// The reserved balance of a given asset under an account. + 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; + + /// Permission options for a given asset. + pub Permissions get(get_permission): map T::AssetId => PermissionVersions; + + /// Any liquidity locks on some account balances. + pub Locks get(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; + + /// 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; + } + add_extra_genesis { + config(assets): Vec; + config(initial_balance): T::Balance; + config(endowed_accounts): Vec; + + build(| + storage: &mut primitives::StorageOverlay, + _: &mut primitives::ChildrenStorageOverlay, + config: &GenesisConfig| { + config.assets.iter().for_each(|asset_id| { + config.endowed_accounts.iter().for_each(|account_id| { + storage.insert( + >::key_for(asset_id, account_id), + ::encode(&config.initial_balance) + ); + }); + }); + }); + } +} + +decl_event!( + pub enum Event where + ::AccountId, + ::Balance, + ::AssetId, + AssetOptions = AssetOptions<::Balance, ::AccountId> + { + /// Asset created (asset_id, creator, asset_options). + Created(AssetId, AccountId, AssetOptions), + /// Asset transfer succeeded (asset_id, from, to, amount). + Transferred(AssetId, AccountId, AccountId, Balance), + /// Asset permission updated (asset_id, new_permissions). + PermissionUpdated(AssetId, PermissionLatest), + /// New asset minted (asset_id, account, amount). + Minted(AssetId, AccountId, Balance), + /// Asset burned (asset_id, account, amount). + Burned(AssetId, AccountId, Balance), + } +); + +impl Module { + // PUBLIC IMMUTABLES + + /// Get an account's total balance of an asset kind. + pub fn total_balance(asset_id: &T::AssetId, who: &T::AccountId) -> T::Balance { + Self::free_balance(asset_id, who) + Self::reserved_balance(asset_id, who) + } + + /// Get an account's free balance of an asset kind. + pub fn free_balance(asset_id: &T::AssetId, who: &T::AccountId) -> T::Balance { + >::get(asset_id, who) + } + + /// Get an account's reserved balance of an asset kind. + pub fn reserved_balance(asset_id: &T::AssetId, who: &T::AccountId) -> T::Balance { + >::get(asset_id, who) + } + + /// Creates an asset. + /// + /// # Arguments + /// * `asset_id`: An ID of a reserved asset. + /// If not provided, a user-generated asset will be created with the next available ID. + /// * `from_account`: The initiator account of this call + /// * `asset_options`: Asset creation options. + /// + pub fn create_asset( + asset_id: Option, + from_account: Option, + options: AssetOptions, + ) -> Result { + let asset_id = if let Some(asset_id) = asset_id { + ensure!(!>::exists(&asset_id), "Asset id already taken."); + ensure!(asset_id < Self::next_asset_id(), "Asset id not available."); + asset_id + } else { + let asset_id = Self::next_asset_id(); + let next_id = asset_id + .checked_add(&One::one()) + .ok_or_else(|| "No new user asset id available.")?; + >::put(next_id); + asset_id + }; + + let account_id = from_account.unwrap_or_default(); + let permissions: PermissionVersions = options.permissions.clone().into(); + + >::insert(asset_id, &options.initial_issuance); + >::insert(&asset_id, &account_id, &options.initial_issuance); + >::insert(&asset_id, permissions); + + Self::deposit_event(RawEvent::Created(asset_id, account_id, options)); + + Ok(()) + } + + /// 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 { + 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)?; + + if from != to { + >::mutate(asset_id, from, |balance| *balance -= amount); + >::mutate(asset_id, to, |balance| *balance += amount); + } + + Ok(()) + } + + /// Transfer some liquid free balance from one account to another. + /// This will emit the `Transferred` event. + pub fn make_transfer_with_event( + asset_id: &T::AssetId, + from: &T::AccountId, + to: &T::AccountId, + amount: T::Balance, + ) -> Result { + Self::make_transfer(asset_id, from, to, amount)?; + + if from != to { + Self::deposit_event(RawEvent::Transferred(*asset_id, from.clone(), to.clone(), amount)); + } + + Ok(()) + } + + /// Move `amount` from free balance to reserved balance. + /// + /// If the free balance is lower than `amount`, then no funds will be moved and an `Err` will + /// be returned. This is different behavior than `unreserve`. + pub fn reserve(asset_id: &T::AssetId, who: &T::AccountId, amount: T::Balance) -> Result { + // Do we need to consider that this is an atomic transaction? + let original_reserve_balance = Self::reserved_balance(asset_id, who); + let original_free_balance = Self::free_balance(asset_id, who); + if original_free_balance < amount { + return Err("not enough free funds"); + } + let new_reserve_balance = original_reserve_balance + amount; + Self::set_reserved_balance(asset_id, who, new_reserve_balance); + let new_free_balance = original_free_balance - amount; + Self::set_free_balance(asset_id, who, new_free_balance); + Ok(()) + } + + /// Moves up to `amount` from reserved balance to free balance. This function cannot fail. + /// + /// As many assets up to `amount` will be moved as possible. If the reserve balance of `who` + /// is less than `amount`, then the remaining amount will be returned. + /// NOTE: This is different behavior than `reserve`. + pub fn unreserve(asset_id: &T::AssetId, who: &T::AccountId, amount: T::Balance) -> T::Balance { + let b = Self::reserved_balance(asset_id, who); + let actual = rstd::cmp::min(b, amount); + let original_free_balance = Self::free_balance(asset_id, who); + let new_free_balance = original_free_balance + actual; + Self::set_free_balance(asset_id, who, new_free_balance); + Self::set_reserved_balance(asset_id, who, b - actual); + amount - actual + } + + /// Deduct up to `amount` from the combined balance of `who`, preferring to deduct from the + /// free balance. This function cannot fail. + /// + /// As much funds up to `amount` will be deducted as possible. If this is less than `amount` + /// then `Some(remaining)` will be returned. Full completion is given by `None`. + /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that + /// the caller will do this. + fn slash(asset_id: &T::AssetId, who: &T::AccountId, amount: T::Balance) -> Option { + let free_balance = Self::free_balance(asset_id, who); + let free_slash = rstd::cmp::min(free_balance, amount); + let new_free_balance = free_balance - free_slash; + Self::set_free_balance(asset_id, who, new_free_balance); + if free_slash < amount { + Self::slash_reserved(asset_id, who, amount - free_slash) + } else { + None + } + } + + /// Deducts up to `amount` from reserved balance of `who`. This function cannot fail. + /// + /// As much funds up to `amount` will be deducted as possible. If the reserve balance of `who` + /// is less than `amount`, then a non-zero second item will be returned. + /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that + /// the caller will do this. + fn slash_reserved(asset_id: &T::AssetId, who: &T::AccountId, amount: T::Balance) -> Option { + let original_reserve_balance = Self::reserved_balance(asset_id, who); + let slash = rstd::cmp::min(original_reserve_balance, amount); + let new_reserve_balance = original_reserve_balance - slash; + Self::set_reserved_balance(asset_id, who, new_reserve_balance); + if amount == slash { + None + } else { + Some(amount - slash) + } + } + + /// Move up to `amount` from reserved balance of account `who` to free balance of account + /// `beneficiary`. + /// + /// As much funds up to `amount` will be moved as possible. If this is less than `amount`, then + /// the `remaining` would be returned, else `Zero::zero()`. + /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that + /// the caller will do this. + fn repatriate_reserved( + asset_id: &T::AssetId, + who: &T::AccountId, + beneficiary: &T::AccountId, + amount: T::Balance, + ) -> T::Balance { + let b = Self::reserved_balance(asset_id, who); + let slash = rstd::cmp::min(b, amount); + + let original_free_balance = Self::free_balance(asset_id, beneficiary); + let new_free_balance = original_free_balance + slash; + Self::set_free_balance(asset_id, beneficiary, new_free_balance); + + let new_reserve_balance = b - slash; + Self::set_reserved_balance(asset_id, who, new_reserve_balance); + amount - slash + } + + /// Check permission to perform burn, mint or update. + /// + /// # Arguments + /// * `asset_id`: A `T::AssetId` type that contains the `asset_id`, which has the permission embedded. + /// * `who`: A `T::AccountId` type that contains the `account_id` for which to check permissions. + /// * `what`: The permission to check. + /// + pub fn check_permission(asset_id: &T::AssetId, who: &T::AccountId, what: &PermissionType) -> bool { + let permission_versions: PermissionVersions = Self::get_permission(asset_id); + let permission = permission_versions.into(); + + match (what, permission) { + ( + PermissionType::Burn, + PermissionLatest { + burn: Owner::Address(account), + .. + }, + ) => account == *who, + ( + PermissionType::Mint, + PermissionLatest { + mint: Owner::Address(account), + .. + }, + ) => account == *who, + ( + PermissionType::Update, + PermissionLatest { + update: Owner::Address(account), + .. + }, + ) => account == *who, + _ => false, + } + } + + /// Return `Ok` iff the account is able to make a withdrawal of the given amount + /// for the given reason. + /// + /// `Err(...)` with the reason why not otherwise. + pub fn ensure_can_withdraw( + asset_id: &T::AssetId, + who: &T::AccountId, + _amount: T::Balance, + reason: WithdrawReason, + new_balance: T::Balance, + ) -> Result { + if asset_id != &Self::staking_asset_id() { + return Ok(()); + } + + let locks = Self::locks(who); + if locks.is_empty() { + return Ok(()); + } + let now = >::block_number(); + if Self::locks(who) + .into_iter() + .all(|l| now >= l.until || new_balance >= l.amount || !l.reasons.contains(reason)) + { + Ok(()) + } else { + Err("account liquidity restrictions prevent withdrawal") + } + } + + // PRIVATE MUTABLES + + /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that + /// the caller will do this. + fn set_reserved_balance(asset_id: &T::AssetId, who: &T::AccountId, balance: T::Balance) { + >::insert(asset_id, who, &balance); + } + + /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that + /// the caller will do this. + fn set_free_balance(asset_id: &T::AssetId, who: &T::AccountId, balance: T::Balance) { + >::insert(asset_id, who, &balance); + } + + fn set_lock( + id: LockIdentifier, + who: &T::AccountId, + amount: T::Balance, + until: T::BlockNumber, + reasons: WithdrawReasons, + ) { + let now = >::block_number(); + let mut new_lock = Some(BalanceLock { + id, + amount, + until, + reasons, + }); + let mut locks = >::locks(who) + .into_iter() + .filter_map(|l| { + if l.id == id { + new_lock.take() + } else if l.until > now { + Some(l) + } else { + None + } + }) + .collect::>(); + if let Some(lock) = new_lock { + locks.push(lock) + } + >::insert(who, locks); + } + + fn extend_lock( + id: LockIdentifier, + who: &T::AccountId, + amount: T::Balance, + until: T::BlockNumber, + reasons: WithdrawReasons, + ) { + let now = >::block_number(); + let mut new_lock = Some(BalanceLock { + id, + amount, + until, + reasons, + }); + let mut locks = >::locks(who) + .into_iter() + .filter_map(|l| { + if l.id == id { + new_lock.take().map(|nl| BalanceLock { + id: l.id, + amount: l.amount.max(nl.amount), + until: l.until.max(nl.until), + reasons: l.reasons | nl.reasons, + }) + } else if l.until > now { + Some(l) + } else { + None + } + }) + .collect::>(); + if let Some(lock) = new_lock { + locks.push(lock) + } + >::insert(who, locks); + } + + fn remove_lock(id: LockIdentifier, who: &T::AccountId) { + let now = >::block_number(); + let locks = >::locks(who) + .into_iter() + .filter_map(|l| if l.until > now && l.id != id { Some(l) } else { None }) + .collect::>(); + >::insert(who, locks); + } +} + +pub trait AssetIdProvider { + type AssetId; + fn asset_id() -> Self::AssetId; +} + +// wrapping these imbalanes in a private module is necessary to ensure absolute privacy +// of the inner member. +mod imbalances { + use super::{result, AssetIdProvider, Imbalance, Saturating, StorageMap, Subtrait, Zero}; + use rstd::mem; + + /// Opaque, move-only struct with private fields that serves as a token denoting that + /// funds have been created without any equal and opposite accounting. + #[must_use] + pub struct PositiveImbalance>( + T::Balance, + rstd::marker::PhantomData, + ); + impl PositiveImbalance + where + T: Subtrait, + U: AssetIdProvider, + { + pub fn new(amount: T::Balance) -> Self { + PositiveImbalance(amount, Default::default()) + } + } + + /// Opaque, move-only struct with private fields that serves as a token denoting that + /// funds have been destroyed without any equal and opposite accounting. + #[must_use] + pub struct NegativeImbalance>( + T::Balance, + rstd::marker::PhantomData, + ); + impl NegativeImbalance + where + T: Subtrait, + U: AssetIdProvider, + { + pub fn new(amount: T::Balance) -> Self { + NegativeImbalance(amount, Default::default()) + } + } + + impl Imbalance for PositiveImbalance + where + T: Subtrait, + U: AssetIdProvider, + { + type Opposite = NegativeImbalance; + + fn zero() -> Self { + Self::new(Zero::zero()) + } + fn drop_zero(self) -> result::Result<(), Self> { + if self.0.is_zero() { + Ok(()) + } else { + Err(self) + } + } + fn split(self, amount: T::Balance) -> (Self, Self) { + let first = self.0.min(amount); + let second = self.0 - first; + + mem::forget(self); + (Self::new(first), Self::new(second)) + } + fn merge(mut self, other: Self) -> Self { + self.0 = self.0.saturating_add(other.0); + mem::forget(other); + + self + } + fn subsume(&mut self, other: Self) { + self.0 = self.0.saturating_add(other.0); + mem::forget(other); + } + fn offset(self, other: Self::Opposite) -> result::Result { + let (a, b) = (self.0, other.0); + mem::forget((self, other)); + + if a >= b { + Ok(Self::new(a - b)) + } else { + Err(NegativeImbalance::new(b - a)) + } + } + fn peek(&self) -> T::Balance { + self.0.clone() + } + } + + impl Imbalance for NegativeImbalance + where + T: Subtrait, + U: AssetIdProvider, + { + type Opposite = PositiveImbalance; + + fn zero() -> Self { + Self::new(Zero::zero()) + } + fn drop_zero(self) -> result::Result<(), Self> { + if self.0.is_zero() { + Ok(()) + } else { + Err(self) + } + } + fn split(self, amount: T::Balance) -> (Self, Self) { + let first = self.0.min(amount); + let second = self.0 - first; + + mem::forget(self); + (Self::new(first), Self::new(second)) + } + fn merge(mut self, other: Self) -> Self { + self.0 = self.0.saturating_add(other.0); + mem::forget(other); + + self + } + fn subsume(&mut self, other: Self) { + self.0 = self.0.saturating_add(other.0); + mem::forget(other); + } + fn offset(self, other: Self::Opposite) -> result::Result { + let (a, b) = (self.0, other.0); + mem::forget((self, other)); + + if a >= b { + Ok(Self::new(a - b)) + } else { + Err(PositiveImbalance::new(b - a)) + } + } + fn peek(&self) -> T::Balance { + self.0.clone() + } + } + + impl Drop for PositiveImbalance + where + T: Subtrait, + U: AssetIdProvider, + { + /// Basic drop handler will just square up the total issuance. + fn drop(&mut self) { + >>::mutate(&U::asset_id(), |v| *v = v.saturating_add(self.0)); + } + } + + impl Drop for NegativeImbalance + where + T: Subtrait, + U: AssetIdProvider, + { + /// Basic drop handler will just square up the total issuance. + fn drop(&mut self) { + >>::mutate(&U::asset_id(), |v| *v = v.saturating_sub(self.0)); + } + } +} + +// TODO: #2052 +// Somewhat ugly hack in order to gain access to module's `increase_total_issuance_by` +// using only the Subtrait (which defines only the types that are not dependent +// on Positive/NegativeImbalance). Subtrait must be used otherwise we end up with a +// circular dependency with Trait having some types be dependent on PositiveImbalance +// and PositiveImbalance itself depending back on Trait for its Drop impl (and thus +// its type declaration). +// This works as long as `increase_total_issuance_by` doesn't use the Imbalance +// types (basically for charging fees). +// This should eventually be refactored so that the three type items that do +// depend on the Imbalance type (TransactionPayment, TransferPayment, DustRemoval) +// are placed in their own SRML module. +struct ElevatedTrait(T); +impl Clone for ElevatedTrait { + fn clone(&self) -> Self { + unimplemented!() + } +} +impl PartialEq for ElevatedTrait { + fn eq(&self, _: &Self) -> bool { + unimplemented!() + } +} +impl Eq for ElevatedTrait {} +impl system::Trait for ElevatedTrait { + type Origin = T::Origin; + type Index = T::Index; + type BlockNumber = T::BlockNumber; + type Hash = T::Hash; + type Hashing = T::Hashing; + type AccountId = T::AccountId; + type Lookup = T::Lookup; + type Header = T::Header; + type Event = (); + type MaximumBlockWeight = T::MaximumBlockWeight; + type MaximumBlockLength = T::MaximumBlockLength; + type WeightMultiplierUpdate = (); + type BlockHashCount = T::BlockHashCount; +} +impl Trait for ElevatedTrait { + type Balance = T::Balance; + type AssetId = T::AssetId; + type Event = (); +} + +#[derive(Encode, Decode, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(Debug))] +pub struct AssetCurrency(rstd::marker::PhantomData, rstd::marker::PhantomData); + +impl Currency for AssetCurrency +where + T: Trait, + U: AssetIdProvider, +{ + type Balance = T::Balance; + type PositiveImbalance = PositiveImbalance; + type NegativeImbalance = NegativeImbalance; + + fn total_balance(who: &T::AccountId) -> Self::Balance { + Self::free_balance(&who) + Self::reserved_balance(&who) + } + + fn free_balance(who: &T::AccountId) -> Self::Balance { + >::free_balance(&U::asset_id(), &who) + } + + /// Returns the total staking asset issuance + fn total_issuance() -> Self::Balance { + >::total_issuance(U::asset_id()) + } + + fn minimum_balance() -> Self::Balance { + Zero::zero() + } + + fn transfer(transactor: &T::AccountId, dest: &T::AccountId, value: Self::Balance) -> Result { + >::make_transfer(&U::asset_id(), transactor, dest, value) + } + + fn ensure_can_withdraw( + who: &T::AccountId, + amount: Self::Balance, + reason: WithdrawReason, + new_balance: Self::Balance, + ) -> Result { + >::ensure_can_withdraw(&U::asset_id(), who, amount, reason, new_balance) + } + + fn withdraw( + who: &T::AccountId, + value: Self::Balance, + reason: WithdrawReason, + _: 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)?; + >::set_free_balance(&U::asset_id(), who, new_balance); + Ok(NegativeImbalance::new(value)) + } + + fn deposit_into_existing( + who: &T::AccountId, + value: Self::Balance, + ) -> result::Result { + // No existential deposit rule and creation fee in GA. `deposit_into_existing` is same with `deposit_creating`. + Ok(Self::deposit_creating(who, value)) + } + + fn deposit_creating(who: &T::AccountId, value: Self::Balance) -> Self::PositiveImbalance { + let (imbalance, _) = Self::make_free_balance_be(who, Self::free_balance(who) + value); + if let SignedImbalance::Positive(p) = imbalance { + p + } else { + // Impossible, but be defensive. + Self::PositiveImbalance::zero() + } + } + + fn make_free_balance_be( + who: &T::AccountId, + balance: Self::Balance, + ) -> ( + SignedImbalance, + UpdateBalanceOutcome, + ) { + let original = >::free_balance(&U::asset_id(), who); + let imbalance = if original <= balance { + SignedImbalance::Positive(PositiveImbalance::new(balance - original)) + } else { + SignedImbalance::Negative(NegativeImbalance::new(original - balance)) + }; + >::set_free_balance(&U::asset_id(), who, balance); + (imbalance, UpdateBalanceOutcome::Updated) + } + + fn can_slash(who: &T::AccountId, value: Self::Balance) -> bool { + >::free_balance(&U::asset_id(), &who) >= value + } + + fn slash(who: &T::AccountId, value: Self::Balance) -> (Self::NegativeImbalance, Self::Balance) { + let remaining = >::slash(&U::asset_id(), who, value); + if let Some(r) = remaining { + (NegativeImbalance::new(value - r), r) + } else { + (NegativeImbalance::new(value), Zero::zero()) + } + } + + fn burn(mut amount: Self::Balance) -> Self::PositiveImbalance { + >::mutate(&U::asset_id(), |issued| + issued.checked_sub(&amount).unwrap_or_else(|| { + amount = *issued; + Zero::zero() + }) + ); + PositiveImbalance::new(amount) + } + + fn issue(mut amount: Self::Balance) -> Self::NegativeImbalance { + >::mutate(&U::asset_id(), |issued| + *issued = issued.checked_add(&amount).unwrap_or_else(|| { + amount = Self::Balance::max_value() - *issued; + Self::Balance::max_value() + }) + ); + NegativeImbalance::new(amount) + } +} + +impl ReservableCurrency for AssetCurrency +where + T: Trait, + U: AssetIdProvider, +{ + fn can_reserve(who: &T::AccountId, value: Self::Balance) -> bool { + 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() + ) + } + + fn reserved_balance(who: &T::AccountId) -> Self::Balance { + >::reserved_balance(&U::asset_id(), &who) + } + + fn reserve(who: &T::AccountId, value: Self::Balance) -> result::Result<(), &'static str> { + >::reserve(&U::asset_id(), who, value) + } + + fn unreserve(who: &T::AccountId, value: Self::Balance) -> Self::Balance { + >::unreserve(&U::asset_id(), who, value) + } + + fn slash_reserved(who: &T::AccountId, value: Self::Balance) -> (Self::NegativeImbalance, Self::Balance) { + let b = Self::reserved_balance(&who.clone()); + let slash = cmp::min(b, value); + + >::set_reserved_balance(&U::asset_id(), who, b - slash); + (NegativeImbalance::new(slash), value - slash) + } + + fn repatriate_reserved( + slashed: &T::AccountId, + beneficiary: &T::AccountId, + value: Self::Balance, + ) -> result::Result { + Ok(>::repatriate_reserved(&U::asset_id(), slashed, beneficiary, value)) + } +} + +pub struct StakingAssetIdProvider(rstd::marker::PhantomData); + +impl AssetIdProvider for StakingAssetIdProvider { + type AssetId = T::AssetId; + fn asset_id() -> Self::AssetId { + >::staking_asset_id() + } +} + +pub struct SpendingAssetIdProvider(rstd::marker::PhantomData); + +impl AssetIdProvider for SpendingAssetIdProvider { + type AssetId = T::AssetId; + fn asset_id() -> Self::AssetId { + >::spending_asset_id() + } +} + +impl LockableCurrency for AssetCurrency> +where + T: Trait, + T::Balance: MaybeSerializeDebug, +{ + type Moment = T::BlockNumber; + + fn set_lock( + id: LockIdentifier, + who: &T::AccountId, + amount: T::Balance, + until: T::BlockNumber, + reasons: WithdrawReasons, + ) { + >::set_lock(id, who, amount, until, reasons) + } + + fn extend_lock( + id: LockIdentifier, + who: &T::AccountId, + amount: T::Balance, + until: T::BlockNumber, + reasons: WithdrawReasons, + ) { + >::extend_lock(id, who, amount, until, reasons) + } + + fn remove_lock(id: LockIdentifier, who: &T::AccountId) { + >::remove_lock(id, who) + } +} + +pub type StakingAssetCurrency = AssetCurrency>; +pub type SpendingAssetCurrency = AssetCurrency>; diff --git a/srml/generic-asset/src/mock.rs b/srml/generic-asset/src/mock.rs new file mode 100644 index 0000000000000000000000000000000000000000..fb7fc04caa682c449101dc6fb688fac333f4390d --- /dev/null +++ b/srml/generic-asset/src/mock.rs @@ -0,0 +1,145 @@ +// Copyright 2019 +// by Centrality Investments Ltd. +// and 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 . + +//! Mocks for the module. + +#![cfg(test)] + +use primitives::{ + testing::Header, + traits::{BlakeTwo256, IdentityLookup}, +}; +use substrate_primitives::{Blake2Hasher, H256}; +use support::{parameter_types, impl_outer_event, impl_outer_origin}; + +use super::*; + +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; +} +impl system::Trait for Test { + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type Event = TestEvent; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; + type WeightMultiplierUpdate = (); + type BlockHashCount = BlockHashCount; +} + +impl Trait for Test { + type Balance = u64; + type AssetId = u32; + type Event = TestEvent; +} + +mod generic_asset { + pub use crate::Event; +} + +impl_outer_event! { + pub enum TestEvent for Test { + generic_asset, + } +} + +pub type GenericAsset = Module; + +pub type System = system::Module; + +pub struct ExtBuilder { + asset_id: u32, + next_asset_id: u32, + accounts: Vec, + initial_balance: u64, +} + +// Returns default values for genesis config +impl Default for ExtBuilder { + fn default() -> Self { + Self { + asset_id: 0, + next_asset_id: 1000, + accounts: vec![0], + initial_balance: 0, + } + } +} + +impl ExtBuilder { + // Sets free balance to genesis config + pub fn free_balance(mut self, free_balance: (u32, u64, u64)) -> Self { + self.asset_id = free_balance.0; + self.accounts = vec![free_balance.1]; + self.initial_balance = free_balance.2; + self + } + + pub fn next_asset_id(mut self, asset_id: u32) -> Self { + self.next_asset_id = asset_id; + self + } + + // builds genesis config + pub fn build(self) -> runtime_io::TestExternalities { + let mut t = system::GenesisConfig::default().build_storage::().unwrap().0; + + t.extend( + GenesisConfig:: { + assets: vec![self.asset_id], + endowed_accounts: self.accounts, + initial_balance: self.initial_balance, + next_asset_id: self.next_asset_id, + staking_asset_id: 16000, + spending_asset_id: 16001, + } + .build_storage() + .unwrap() + .0, + ); + + t.into() + } +} + +// This function basically just builds a genesis storage key/value store according to +// our desired mockup. +pub fn new_test_ext() -> runtime_io::TestExternalities { + system::GenesisConfig::default() + .build_storage::() + .unwrap() + .0 + .into() +} diff --git a/srml/generic-asset/src/tests.rs b/srml/generic-asset/src/tests.rs new file mode 100644 index 0000000000000000000000000000000000000000..685e553c1c14d03687a3595ece8032746f273fea --- /dev/null +++ b/srml/generic-asset/src/tests.rs @@ -0,0 +1,1288 @@ +// Copyright 2019 +// by Centrality Investments Ltd. +// and 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 . + +//! Tests for the module. + +#![cfg(test)] + +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(), || { + let default_permission = PermissionLatest { + update: Owner::Address(1), + mint: Owner::Address(1), + burn: Owner::Address(1), + }; + + let expected_balance = balance; + + assert_ok!(GenericAsset::create( + Origin::signed(1), + AssetOptions { + initial_issuance: balance, + permissions: default_permission + } + )); + assert_eq!(GenericAsset::free_balance(&16000, &1), expected_balance); + }); +} + +#[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()); + }, + ); +} + +#[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); + }, + ); +} + +// Given +// - The next asset id as `asset_id` = 1000. +// - AssetOptions with all permissions. +// - GenesisStore has sufficient free balance. +// +// When +// - Create an asset from `origin` as 1. +// Then +// - free_balance of next asset id = 100. +// +// When +// - After transferring 40 from account 1 to account 2. +// Then +// - Origin account's `free_balance` = 60. +// - account 2's `free_balance` = 40. +#[test] +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); + }, + ); +} + +// Given +// - The next asset id as `asset_id` = 1000. +// - AssetOptions with all permissions. +// - GenesisStore has sufficient free balance. +// +// When +// - Create an asset from `origin` as 1. +// Then +// - free_balance of next asset id = 100. +// +// When +// - After transferring 40 from account 1 to account 2. +// Then +// - Origin account's `free_balance` = 60. +// - account 2's `free_balance` = 40. +#[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" + ); + }, + ); +} + +#[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" + ); + }, + ); +} + +// Given +// - Next asset id as `asset_id` = 1000. +// - Sufficient free balance. +// - initial balance = 100. +// When +// - After performing a self transfer from account 1 to 1. +// Then +// - Should not throw any errors. +// - Free balance after self transfer should equal to the free balance before self transfer. +#[test] +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 + } + )); + + 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" + ); + }, + ); +} + +// Ensures it uses fake money for staking asset id. +#[test] +fn staking_asset_id_should_return_0() { + with_externalities(&mut ExtBuilder::default().build(), || { + assert_eq!(GenericAsset::staking_asset_id(), 16000); + }); +} + +// Ensures it uses fake money for spending asset id. +#[test] +fn spending_asset_id_should_return_10() { + with_externalities(&mut ExtBuilder::default().build(), || { + assert_eq!(GenericAsset::spending_asset_id(), 16001); + }); +} + +// Given +// -Â Free balance is 0 and the reserved balance is 0. +// Then +// -Â total_balance should return 0 +#[test] +fn total_balance_should_be_zero() { + with_externalities(&mut new_test_ext(), || { + assert_eq!(GenericAsset::total_balance(&0, &0), 0); + }); +} + +// Given +// -Â Free balance is 0 and the reserved balance > 0. +// When +// - After calling total_balance. +// Then +// -Â total_balance should equals to reserved balance. +#[test] +fn total_balance_should_be_equal_to_account_balance() { + let default_permission = PermissionLatest { + update: Owner::Address(1), + 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); + }, + ); +} + +// Given +// - An account presents with AccountId = 1 +// -Â free_balance > 0. +// - reserved_balance = 50. +// When +// - After calling free_balance. +// Then +// -Â 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(), || { + GenericAsset::set_reserved_balance(&1, &0, 70); + assert_eq!(GenericAsset::free_balance(&1, &0), 50); + }); +} + +// Given +// - An account presents with AccountId = 1. +// -Â Free balance > 0 and the reserved balance > 0. +// When +// - After calling total_balance. +// Then +// -Â 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(), || { + GenericAsset::set_reserved_balance(&1, &0, 70); + assert_eq!(GenericAsset::total_balance(&1, &0), 120); + }); +} + +// Given +// -Â free_balance > 0. +// - reserved_balance = 70. +// When +// - After calling reserved_balance. +// Then +// - 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(), || { + GenericAsset::set_reserved_balance(&1, &0, 70); + assert_eq!(GenericAsset::reserved_balance(&1, &0), 70); + }); +} + +// Given +// - A valid account presents. +// - Initial reserved_balance = 0 +// When +// - After calls set_reserved_balance +// Then +// - Should persists the amount as reserved_balance. +// - reserved_balance = amount +#[test] +fn set_reserved_balance_should_add_balance_as_reserved() { + with_externalities(&mut ExtBuilder::default().build(), || { + GenericAsset::set_reserved_balance(&1, &0, 50); + assert_eq!(GenericAsset::reserved_balance(&1, &0), 50); + }); +} + +// Given +// - A valid account presents. +// - Initial free_balance = 100. +// When +// - After calling set_free_balance. +// Then +// - Should persists the amount as free_balance. +// - 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(), || { + GenericAsset::set_free_balance(&1, &0, 50); + assert_eq!(GenericAsset::free_balance(&1, &0), 50); + }); +} + +// Given +// - free_balance is greater than the account balance. +// - free_balance = 100 +// - reserved_balance = 0 +// - reserve amount = 70 +// When +// - After calling reserve +// Then +// - Funds should be removed from the account. +// - new free_balance = original free_balance - reserved amount +// - 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(), || { + assert_ok!(GenericAsset::reserve(&1, &0, 70)); + assert_eq!(GenericAsset::free_balance(&1, &0), 30); + assert_eq!(GenericAsset::reserved_balance(&1, &0), 70); + }); +} + +// Given +// - Free balance is lower than the account balance. +// - free_balance = 100 +// - reserved_balance = 0 +// - reserve amount = 120 +// When +// - After calling reverse function. +// Then +// - Funds should not be removed from the account. +// - 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(), || { + 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); + }); +} + +// Given +// - unreserved_amount > reserved_balance. +// - reserved_balance = 100. +// - free_balance = 100. +// - unreserved_amount = 120. +// When +// - After calling unreserve function. +// Then +// - 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(), || { + GenericAsset::set_reserved_balance(&1, &0, 100); + assert_eq!(GenericAsset::unreserve(&1, &0, 120), 20); + }); +} + +// Given +// - unreserved_amount < reserved_balance. +// - reserved_balance = 100. +// - free_balance = 100. +// - unreserved_amount = 50. +// When +// - After calling unreserve function. +// Then +// - unreserved should return None. +#[test] +fn unreserve_should_return_none() { + with_externalities(&mut ExtBuilder::default().free_balance((1, 0, 100)).build(), || { + GenericAsset::set_reserved_balance(&1, &0, 100); + assert_eq!(GenericAsset::unreserve(&1, &0, 50), 0); + }); +} + +// Given +// - unreserved_amount > reserved_balance. +// - reserved_balance = 100. +// - free_balance = 100. +// - unreserved_amount = 120. +// When +// - After calling unreserve function. +// Then +// - 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(), || { + GenericAsset::set_reserved_balance(&1, &0, 100); + GenericAsset::unreserve(&1, &0, 120); + assert_eq!(GenericAsset::free_balance(&1, &0), 200); + }); +} + +// Given +// - unreserved_amount > reserved_balance. +// - reserved_balance = 100. +// - free_balance = 100. +// - unreserved_amount = 120. +// When +// - After calling unreserve function. +// Then +// - 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(), || { + GenericAsset::set_free_balance(&1, &0, 100); + GenericAsset::unreserve(&1, &0, 120); + assert_eq!(GenericAsset::reserved_balance(&1, &0), 0); + }); +} + +// Given +// - slash amount < free_balance. +// - reserved_balance = 100. +// - free_balance = 100. +// - slash amount = 70. +// When +// - After calling slash function. +// Then +// - slash should return None. +#[test] +fn slash_should_return_slash_reserved_amount() { + with_externalities(&mut ExtBuilder::default().free_balance((1, 0, 100)).build(), || { + GenericAsset::set_reserved_balance(&1, &0, 100); + assert_eq!(GenericAsset::slash(&1, &0, 70), None); + }); +} + +// Given +// - slashed_amount > reserved_balance. +// When +// - After calling slashed_reverse function. +// Then +// - Should return slashed_reserved - reserved_balance. +#[test] +fn slash_reserved_should_deducts_up_to_amount_from_reserved_balance() { + with_externalities(&mut ExtBuilder::default().build(), || { + GenericAsset::set_reserved_balance(&1, &0, 100); + assert_eq!(GenericAsset::slash_reserved(&1, &0, 150), Some(50)); + }); +} + +// Given +// - slashed_amount equals to reserved_amount. +// When +// - After calling slashed_reverse function. +// Then +// - Should return None. +#[test] +fn slash_reserved_should_return_none() { + with_externalities(&mut ExtBuilder::default().build(), || { + GenericAsset::set_reserved_balance(&1, &0, 100); + assert_eq!(GenericAsset::slash_reserved(&1, &0, 100), None); + }); +} + +// Given +// - reserved_balance = 100. +// - repatriate_reserved_amount > reserved_balance. +// When +// - After calling repatriate_reserved. +// Then +// - Should not return None. +#[test] +fn repatriate_reserved_return_amount_substracted_by_slash_amount() { + with_externalities(&mut ExtBuilder::default().build(), || { + GenericAsset::set_reserved_balance(&1, &0, 100); + assert_eq!(GenericAsset::repatriate_reserved(&1, &0, &1, 130), 30); + }); +} + +// Given +// - reserved_balance = 100. +// - repatriate_reserved_amount > reserved_balance. +// When +// - After calling repatriate_reserved. +// Then +// - Should return None. +#[test] +fn repatriate_reserved_return_none() { + with_externalities(&mut ExtBuilder::default().build(), || { + GenericAsset::set_reserved_balance(&1, &0, 100); + assert_eq!(GenericAsset::repatriate_reserved(&1, &0, &1, 90), 0); + }); +} + +// Given +// - An asset with all permissions +// When +// - After calling `create_reserved` function. +// Then +// - 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(), || { + let default_permission = PermissionLatest { + update: Owner::Address(1), + mint: Owner::Address(1), + burn: Owner::Address(1), + }; + let options = AssetOptions { + initial_issuance: 500, + permissions: default_permission, + }; + + let expected_total_issuance = 500; + let created_asset_id = 9; + let created_account_id = 0; + + assert_ok!(GenericAsset::create_reserved(Origin::ROOT, created_asset_id, options)); + + // Tests for side effects. + assert_eq!(>::get(created_asset_id), expected_total_issuance); + assert_eq!( + >::get(&created_asset_id, &created_account_id), + expected_total_issuance + ); + }); +} + +// Given +// - Origin is signed +// - Origin does not have minting permission +// When +// - After calling mint function +// Then +// - Should throw a permission error +#[test] +fn mint_should_throw_permission_error() { + with_externalities(&mut ExtBuilder::default().build(), || { + let origin = 1; + let asset_id = 4; + let to_account = 2; + let amount = 100; + + assert_noop!( + GenericAsset::mint(Origin::signed(origin), asset_id, to_account, amount), + "The origin does not have permission to mint an asset." + ); + }); +} + +// Given +// - Origin is signed. +// - Origin has permissions. +// When +// - After calling mint function +// Then +// - Should increase `to` free_balance. +// - 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), + }; + + 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_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 +// - Origin is signed. +// - Origin does not have burning permission. +// When +// - After calling burn function. +// Then +// - 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." + ); + }, + ); +} + +// Given +// - Origin is signed. +// - Origin has permissions. +// When +// - After calling burn function +// Then +// - Should decrease `to`'s free_balance. +// - 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), + }; + + 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 +// - `default_permission` with all privileges. +// - All permissions for origin. +// When +// - After executing create function and check_permission function. +// Then +// - 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), + }; + + assert_ok!(GenericAsset::create( + Origin::signed(origin), + AssetOptions { + initial_issuance: initial_issuance, + permissions: default_permission + } + )); + + 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 + ); + }, + ); +} + +// Given +// - `default_permission` with no privileges. +// - No permissions for origin. +// When +// - After executing create function and check_permission function. +// Then +// - 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, + }; + + assert_ok!(GenericAsset::create( + Origin::signed(origin), + AssetOptions { + initial_issuance: initial_issuance, + permissions: default_permission + } + )); + + 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 + ); + }, + ); +} + +// Given +// - `default_permission` only with update. +// When +// - After executing update_permission function. +// Then +// - The account origin should not have the burn 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, + }; + + 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_eq!( + GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Mint), + true + ); + assert_eq!( + GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Burn), + false + ); + }, + ); +} + +// Given +// - `default_permission` without any permissions. +// When +// - After executing update_permission function. +// Then +// - 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."; + + 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 +// - `asset_id` provided. +// - `from_account` is present. +// - All permissions for origin. +// When +// - After calling create_asset. +// Then +// - Should create a reserved token with provided id. +// - NextAssetId doesn't change. +// - TotalIssuance must equal to initial issuance. +// - FreeBalance must equal to initial issuance for the given account. +// - 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(), || { + let origin = 1; + let from_account: Option<::AccountId> = Some(1); + + let default_permission = PermissionLatest { + update: Owner::Address(origin), + mint: Owner::Address(origin), + burn: Owner::Address(origin), + }; + let expected_permission = PermissionVersions::V1(default_permission.clone()); + let asset_id = 9; + let initial_issuance = 100; + + assert_ok!(GenericAsset::create_asset( + Some(asset_id), + from_account, + AssetOptions { + initial_issuance: initial_issuance, + permissions: default_permission.clone() + } + )); + + // Test for side effects. + assert_eq!(>::get(), 10); + assert_eq!(>::get(asset_id), initial_issuance); + assert_eq!(>::get(&asset_id, &origin), initial_issuance); + assert_eq!(>::get(&asset_id), expected_permission); + }); +} + +// Given +// - `asset_id` is an id for user generated assets. +// - Whatever other params. +// Then +// - `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(), || { + let origin = 1; + let from_account: Option<::AccountId> = Some(1); + + let default_permission = PermissionLatest { + update: Owner::Address(origin), + mint: Owner::Address(origin), + burn: Owner::Address(origin), + }; + + let asset_id = 11; + let initial_issuance = 100; + + assert_noop!( + GenericAsset::create_asset( + Some(asset_id), + from_account, + AssetOptions { + initial_issuance, + permissions: default_permission.clone() + } + ), + "Asset id not available." + ); + }); +} + +// Given +// - `asset_id` is for reserved assets, but already taken. +// - Whatever other params. +// Then +// - `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(), || { + let origin = 1; + let from_account: Option<::AccountId> = Some(1); + + let default_permission = PermissionLatest { + update: Owner::Address(origin), + mint: Owner::Address(origin), + burn: Owner::Address(origin), + }; + + let asset_id = 9; + let initial_issuance = 100; + + assert_ok!(GenericAsset::create_asset( + Some(asset_id), + from_account, + AssetOptions { + initial_issuance, + permissions: default_permission.clone() + } + )); + assert_noop!( + GenericAsset::create_asset( + Some(asset_id), + from_account, + AssetOptions { + initial_issuance, + permissions: default_permission.clone() + } + ), + "Asset id already taken." + ); + }); +} + +// Given +// - `asset_id` provided. +// - `from_account` is None. +// - All permissions for origin. +// When +// - After calling create_asset. +// Then +// - 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(), || { + let origin = 1; + let from_account: Option<::AccountId> = None; + + let default_permission = PermissionLatest { + update: Owner::Address(origin), + mint: Owner::Address(origin), + burn: Owner::Address(origin), + }; + + let created_account_id = 0; + let asset_id = 9; + let initial_issuance = 100; + + assert_ok!(GenericAsset::create_asset( + Some(asset_id), + from_account, + AssetOptions { + initial_issuance: initial_issuance, + permissions: default_permission + } + )); + + // Test for a side effect. + assert_eq!( + >::get(&asset_id, &created_account_id), + initial_issuance + ); + }); +} + +// Given +// - `asset_id` not provided. +// - `from_account` is None. +// - All permissions for origin. +// When +// - After calling create_asset. +// Then +// - Should create a user token. +// - `NextAssetId`'s get should return a new value. +// - 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(), || { + let origin = 1; + let from_account: Option<::AccountId> = None; + + let default_permission = PermissionLatest { + update: Owner::Address(origin), + mint: Owner::Address(origin), + burn: Owner::Address(origin), + }; + + let created_account_id = 0; + let reserved_asset_id = 100000; + let initial_issuance = 100; + let created_user_asset_id = 10; + + assert_ok!(GenericAsset::create_asset( + None, + from_account, + AssetOptions { + initial_issuance: initial_issuance, + permissions: default_permission + } + )); + + // Test for side effects. + assert_eq!(>::get(&reserved_asset_id, &created_account_id), 0); + assert_eq!( + >::get(&created_user_asset_id, &created_account_id), + initial_issuance + ); + assert_eq!(>::get(created_user_asset_id), initial_issuance); + }); +} + +#[test] +fn update_permission_should_raise_event() { + // Arrange + let staking_asset_id = 16000; + let asset_id = 1000; + let origin = 1; + let initial_balance = 1000; + let permissions = PermissionLatest { + update: Owner::Address(origin), + mint: Owner::Address(origin), + burn: Owner::Address(origin), + }; + + with_externalities( + &mut ExtBuilder::default() + .next_asset_id(asset_id) + .free_balance((staking_asset_id, origin, initial_balance)) + .build(), + || { + assert_ok!(GenericAsset::create( + Origin::signed(origin), + AssetOptions { + initial_issuance: 0, + permissions: permissions.clone(), + } + )); + + // Act + assert_ok!(GenericAsset::update_permission( + Origin::signed(origin), + asset_id, + permissions.clone() + )); + + // Assert + assert!(System::events().iter().any(|record| record.event + == TestEvent::generic_asset(RawEvent::PermissionUpdated(asset_id, permissions.clone())))); + }, + ); +} + +#[test] +fn mint_should_raise_event() { + // Arrange + let staking_asset_id = 16000; + let asset_id = 1000; + let origin = 1; + let initial_balance = 1000; + let permissions = PermissionLatest { + update: Owner::Address(origin), + mint: Owner::Address(origin), + burn: Owner::Address(origin), + }; + 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(), + || { + 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)); + + // Assert + assert!(System::events() + .iter() + .any(|record| record.event == TestEvent::generic_asset(RawEvent::Minted(asset_id, to, amount)))); + }, + ); +} + +#[test] +fn burn_should_raise_event() { + // Arrange + let staking_asset_id = 16000; + let asset_id = 1000; + let origin = 1; + let initial_balance = 1000; + let permissions = PermissionLatest { + update: Owner::Address(origin), + mint: Owner::Address(origin), + burn: Owner::Address(origin), + }; + let amount = 100; + + with_externalities( + &mut ExtBuilder::default() + .next_asset_id(asset_id) + .free_balance((staking_asset_id, origin, initial_balance)) + .build(), + || { + 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)); + + // Assert + assert!(System::events() + .iter() + .any(|record| record.event == TestEvent::generic_asset(RawEvent::Burned(asset_id, origin, amount)))); + }, + ); +} diff --git a/srml/grandpa/Cargo.toml b/srml/grandpa/Cargo.toml index 9e61029f728c2eabd3e78aa136262af91ce4edf6..2f13bd018fcaebca078e33d626a4568a77b339e4 100644 --- a/srml/grandpa/Cargo.toml +++ b/srml/grandpa/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" [dependencies] serde = { version = "1.0", optional = true, features = ["derive"] } -parity-codec = { version = "3.3", default-features = false, features = ["derive"] } +parity-codec = { version = "4.1.1", default-features = false, features = ["derive"] } substrate-primitives = { path = "../../core/primitives", default-features = false } substrate-finality-grandpa-primitives = { path = "../../core/finality-grandpa/primitives", default-features = false } rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false } diff --git a/srml/grandpa/src/lib.rs b/srml/grandpa/src/lib.rs index 61610e91940fd05e5f825f291e8bdbac99adc390..9615953e67683aec0757e910e13617c403e46b37 100644 --- a/srml/grandpa/src/lib.rs +++ b/srml/grandpa/src/lib.rs @@ -30,8 +30,6 @@ // re-export since this is necessary for `impl_apis` in runtime. pub use substrate_finality_grandpa_primitives as fg_primitives; -#[cfg(feature = "std")] -use serde::Serialize; use rstd::prelude::*; use parity_codec::{self as codec, Encode, Decode}; use srml_support::{ @@ -40,44 +38,13 @@ use srml_support::{ use primitives::{ generic::{DigestItem, OpaqueDigestItemId}, traits::CurrentHeight }; -use fg_primitives::{ScheduledChange, GRANDPA_ENGINE_ID}; +use fg_primitives::{ScheduledChange, ConsensusLog, GRANDPA_ENGINE_ID}; pub use fg_primitives::{AuthorityId, AuthorityWeight}; use system::{ensure_signed, DigestOf}; mod mock; mod tests; -/// Consensus log type of this module. -#[cfg_attr(feature = "std", derive(Serialize, Debug))] -#[derive(Encode, Decode, PartialEq, Eq, Clone)] -pub enum Signal { - /// Authorities set change has been signaled. Contains the new set of authorities - /// and the delay in blocks _to finalize_ before applying. - AuthoritiesChange(ScheduledChange), - /// A forced authorities set change. Contains in this order: the median last - /// finalized block when the change was signaled, the delay in blocks _to import_ - /// before applying and the new set of authorities. - ForcedAuthoritiesChange(N, ScheduledChange), -} - -impl Signal { - /// Try to cast the log entry as a contained signal. - pub fn try_into_change(self) -> Option> { - match self { - Signal::AuthoritiesChange(change) => Some(change), - Signal::ForcedAuthoritiesChange(_, _) => None, - } - } - - /// Try to cast the log entry as a contained forced signal. - pub fn try_into_forced_change(self) -> Option<(N, ScheduledChange)> { - match self { - Signal::ForcedAuthoritiesChange(median, change) => Some((median, change)), - Signal::AuthoritiesChange(_) => None, - } - } -} - pub trait Trait: system::Trait { /// The event type of this module. type Event: From + Into<::Event>; @@ -124,10 +91,42 @@ impl Decode for StoredPendingChange { } } +/// Current state of the GRANDPA authority set. State transitions must happen in +/// the same order of states defined below, e.g. `Paused` implies a prior +/// `PendingPause`. +#[derive(Decode, Encode)] +#[cfg_attr(test, derive(Debug, PartialEq))] +pub enum StoredState { + /// The current authority set is live, and GRANDPA is enabled. + Live, + /// There is a pending pause event which will be enacted at the given block + /// height. + PendingPause { + /// Block at which the intention to pause was scheduled. + scheduled_at: N, + /// Number of blocks after which the change will be enacted. + delay: N + }, + /// The current GRANDPA authority set is paused. + Paused, + /// There is a pending resume event which will be enacted at the given block + /// height. + PendingResume { + /// Block at which the intention to resume was scheduled. + scheduled_at: N, + /// Number of blocks after which the change will be enacted. + delay: N, + }, +} + decl_event!( pub enum Event { /// New authority set has been applied. NewAuthorities(Vec<(AuthorityId, u64)>), + /// Current authority set has been paused. + Paused, + /// Current authority set has been resumed. + Resumed, } ); @@ -136,6 +135,9 @@ decl_storage! { /// The current authority set. Authorities get(authorities) config(): Vec<(AuthorityId, AuthorityWeight)>; + /// State of the current authority set. + State get(state): StoredState = StoredState::Live; + /// Pending change: (signaled at, scheduled change). PendingChange: Option>; @@ -158,18 +160,20 @@ decl_module! { } fn on_finalize(block_number: T::BlockNumber) { + // check for scheduled pending authority set changes if let Some(pending_change) = >::get() { + // emit signal if we're at the block that scheduled the change if block_number == pending_change.scheduled_at { if let Some(median) = pending_change.forced { - Self::deposit_log(Signal::ForcedAuthoritiesChange( + Self::deposit_log(ConsensusLog::ForcedChange( median, - ScheduledChange{ + ScheduledChange { delay: pending_change.delay, next_authorities: pending_change.next_authorities.clone(), } )) } else { - Self::deposit_log(Signal::AuthoritiesChange( + Self::deposit_log(ConsensusLog::ScheduledChange( ScheduledChange{ delay: pending_change.delay, next_authorities: pending_change.next_authorities.clone(), @@ -178,14 +182,44 @@ decl_module! { } } + // enact the change if we've reached the enacting block if block_number == pending_change.scheduled_at + pending_change.delay { - >::put(&pending_change.next_authorities); + Authorities::put(&pending_change.next_authorities); Self::deposit_event( Event::NewAuthorities(pending_change.next_authorities) ); >::kill(); } } + + // check for scheduled pending state changes + match >::get() { + StoredState::PendingPause { scheduled_at, delay } => { + // signal change to pause + if block_number == scheduled_at { + Self::deposit_log(ConsensusLog::Pause(delay)); + } + + // enact change to paused state + if block_number == scheduled_at + delay { + >::put(StoredState::Paused); + Self::deposit_event(Event::Paused); + } + }, + StoredState::PendingResume { scheduled_at, delay } => { + // signal change to resume + if block_number == scheduled_at { + Self::deposit_log(ConsensusLog::Resume(delay)); + } + + // enact change to live state + if block_number == scheduled_at + delay { + >::put(StoredState::Live); + Self::deposit_event(Event::Resumed); + } + }, + _ => {}, + } } } } @@ -193,7 +227,37 @@ decl_module! { impl Module { /// Get the current set of authorities, along with their respective weights. pub fn grandpa_authorities() -> Vec<(AuthorityId, u64)> { - >::get() + Authorities::get() + } + + pub fn schedule_pause(in_blocks: T::BlockNumber) -> Result { + if let StoredState::Live = >::get() { + let scheduled_at = system::ChainContext::::default().current_height(); + >::put(StoredState::PendingPause { + delay: in_blocks, + scheduled_at, + }); + + Ok(()) + } else { + Err("Attempt to signal GRANDPA pause when the authority set isn't live \ + (either paused or already pending pause).") + } + } + + pub fn schedule_resume(in_blocks: T::BlockNumber) -> Result { + if let StoredState::Paused = >::get() { + let scheduled_at = system::ChainContext::::default().current_height(); + >::put(StoredState::PendingResume { + delay: in_blocks, + scheduled_at, + }); + + Ok(()) + } else { + Err("Attempt to signal GRANDPA resume when the authority set isn't paused \ + (either live or already pending resume).") + } } /// Schedule a change in the authorities. @@ -242,16 +306,16 @@ impl Module { } /// Deposit one of this module's logs. - fn deposit_log(log: Signal) { + fn deposit_log(log: ConsensusLog) { let log: DigestItem = DigestItem::Consensus(GRANDPA_ENGINE_ID, log.encode()); >::deposit_log(log.into()); } } impl Module { - pub fn grandpa_log(digest: &DigestOf) -> Option> { + pub fn grandpa_log(digest: &DigestOf) -> Option> { let id = OpaqueDigestItemId::Consensus(&GRANDPA_ENGINE_ID); - digest.convert_first(|l| l.try_to::>(id)) + digest.convert_first(|l| l.try_to::>(id)) } pub fn pending_change(digest: &DigestOf) @@ -265,11 +329,24 @@ impl Module { { Self::grandpa_log(digest).and_then(|signal| signal.try_into_forced_change()) } + + pub fn pending_pause(digest: &DigestOf) + -> Option + { + Self::grandpa_log(digest).and_then(|signal| signal.try_into_pause()) + } + + pub fn pending_resume(digest: &DigestOf) + -> Option + { + Self::grandpa_log(digest).and_then(|signal| signal.try_into_resume()) + } } impl session::OneSessionHandler for Module { type Key = AuthorityId; - fn on_new_session<'a, I: 'a>(changed: bool, validators: I) + + fn on_new_session<'a, I: 'a>(changed: bool, validators: I, _queued_validators: I) where I: Iterator { // instant changes @@ -286,8 +363,9 @@ impl session::OneSessionHandler for Module { } } } - fn on_disabled(_i: usize) { - // ignore? + + fn on_disabled(i: usize) { + Self::deposit_log(ConsensusLog::OnDisabled(i as u64)) } } diff --git a/srml/grandpa/src/mock.rs b/srml/grandpa/src/mock.rs index 80c99b9a3cfaa8cb37f25ef6b52f8ccc9963bcff..87845a7a4d05f8542fb1474463dcbf6d91706e85 100644 --- a/srml/grandpa/src/mock.rs +++ b/srml/grandpa/src/mock.rs @@ -18,24 +18,20 @@ #![cfg(test)] -use primitives::{ - BuildStorage, DigestItem, traits::IdentityLookup, testing::{Header, UintAuthorityId} -}; +use primitives::{DigestItem, traits::IdentityLookup, testing::{Header, UintAuthorityId}}; use runtime_io; -use srml_support::{impl_outer_origin, impl_outer_event}; +use srml_support::{impl_outer_origin, impl_outer_event, parameter_types}; use substrate_primitives::{H256, Blake2Hasher}; use parity_codec::{Encode, Decode}; -use crate::{AuthorityId, GenesisConfig, Trait, Module, Signal}; +use crate::{AuthorityId, GenesisConfig, Trait, Module, ConsensusLog}; use substrate_finality_grandpa_primitives::GRANDPA_ENGINE_ID; impl_outer_origin!{ pub enum Origin for Test {} } -impl From> for DigestItem { - fn from(log: Signal) -> DigestItem { - DigestItem::Consensus(GRANDPA_ENGINE_ID, log.encode()) - } +pub fn grandpa_log(log: ConsensusLog) -> DigestItem { + DigestItem::Consensus(GRANDPA_ENGINE_ID, log.encode()) } // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. @@ -45,6 +41,11 @@ impl Trait for Test { type Event = TestEvent; } +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; +} impl system::Trait for Test { type Origin = Origin; type Index = u64; @@ -54,7 +55,11 @@ 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; + type MaximumBlockLength = MaximumBlockLength; } mod grandpa { @@ -72,9 +77,8 @@ pub fn to_authorities(vec: Vec<(u64, u64)>) -> Vec<(AuthorityId, u64)> { } pub fn new_test_ext(authorities: Vec<(u64, u64)>) -> runtime_io::TestExternalities { - let mut t = system::GenesisConfig::::default().build_storage().unwrap().0; - t.extend(GenesisConfig:: { - _genesis_phantom_data: Default::default(), + let mut t = system::GenesisConfig::default().build_storage::().unwrap().0; + t.extend(GenesisConfig { authorities: to_authorities(authorities), }.build_storage().unwrap().0); t.into() diff --git a/srml/grandpa/src/tests.rs b/srml/grandpa/src/tests.rs index ab923f295b6d7475e6603ebd2b895527d70be9d4..11700fa99fa78618cf27a189d4847e6301d45f10 100644 --- a/srml/grandpa/src/tests.rs +++ b/srml/grandpa/src/tests.rs @@ -39,9 +39,9 @@ fn authorities_change_logged() { let header = System::finalize(); assert_eq!(header.digest, Digest { logs: vec![ - Signal::AuthoritiesChange( + grandpa_log(ConsensusLog::ScheduledChange( ScheduledChange { delay: 0, next_authorities: to_authorities(vec![(4, 1), (5, 1), (6, 1)]) } - ).into(), + )), ], }); @@ -64,9 +64,9 @@ fn authorities_change_logged_after_delay() { let header = System::finalize(); assert_eq!(header.digest, Digest { logs: vec![ - Signal::AuthoritiesChange( + grandpa_log(ConsensusLog::ScheduledChange( ScheduledChange { delay: 1, next_authorities: to_authorities(vec![(4, 1), (5, 1), (6, 1)]) } - ).into(), + )), ], }); @@ -202,3 +202,83 @@ fn dispatch_forced_change() { let _ = header; }); } + +#[test] +fn schedule_pause_only_when_live() { + with_externalities(&mut new_test_ext(vec![(1, 1), (2, 1), (3, 1)]), || { + // 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(); + + // we've switched to the pending pause state + assert_eq!( + Grandpa::state(), + StoredState::PendingPause { + scheduled_at: 1u64, + delay: 1, + }, + ); + + Grandpa::on_finalize(1); + let _ = System::finalize(); + + System::initialize(&2, &Default::default(), &Default::default(), &Default::default()); + + // signaling a pause now should fail + assert!(Grandpa::schedule_pause(1).is_err()); + + Grandpa::on_finalize(2); + let _ = System::finalize(); + + // after finalizing block 2 the set should have switched to paused state + assert_eq!( + Grandpa::state(), + StoredState::Paused, + ); + }); +} + +#[test] +fn schedule_resume_only_when_paused() { + with_externalities(&mut new_test_ext(vec![(1, 1), (2, 1), (3, 1)]), || { + System::initialize(&1, &Default::default(), &Default::default(), &Default::default()); + + // the set is currently live, resuming it is an error + assert!(Grandpa::schedule_resume(1).is_err()); + + assert_eq!( + Grandpa::state(), + StoredState::Live, + ); + + // we schedule a pause to be applied instantly + Grandpa::schedule_pause(0).unwrap(); + Grandpa::on_finalize(1); + let _ = System::finalize(); + + assert_eq!( + Grandpa::state(), + StoredState::Paused, + ); + + // we schedule the set to go back live in 2 blocks + System::initialize(&2, &Default::default(), &Default::default(), &Default::default()); + Grandpa::schedule_resume(2).unwrap(); + Grandpa::on_finalize(2); + let _ = System::finalize(); + + System::initialize(&3, &Default::default(), &Default::default(), &Default::default()); + Grandpa::on_finalize(3); + let _ = System::finalize(); + + System::initialize(&4, &Default::default(), &Default::default(), &Default::default()); + Grandpa::on_finalize(4); + let _ = System::finalize(); + + // it should be live at block 4 + assert_eq!( + Grandpa::state(), + StoredState::Live, + ); + }); +} diff --git a/srml/im-online/Cargo.toml b/srml/im-online/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..5ca5e0c533d19d92e0860dff42c4240ab96febd5 --- /dev/null +++ b/srml/im-online/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "srml-im-online" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +parity-codec = { version = "4.1.1", default-features = false, features = ["derive"] } +primitives = { package = "sr-primitives", path = "../../core/sr-primitives", default-features = false } +rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false } +serde = { version = "1.0", optional = true } +session = { package = "srml-session", path = "../session", default-features = false } +srml-support = { path = "../support", default-features = false } +sr-io = { package = "sr-io", path = "../../core/sr-io", default-features = false } +substrate_primitives = { package = "substrate-primitives", path = "../../core/primitives", default-features = false } +system = { package = "srml-system", path = "../system", default-features = false } + +[features] +default = ["std"] +std = [ + "parity-codec/std", + "primitives/std", + "rstd/std", + "serde", + "session/std", + "srml-support/std", + "sr-io/std", + "system/std", +] diff --git a/srml/im-online/src/lib.rs b/srml/im-online/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..e2b23f1dbded05b95428d40467276f3f19e4ea7e --- /dev/null +++ b/srml/im-online/src/lib.rs @@ -0,0 +1,424 @@ +// 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 . + +//! # I'm online Module +//! +//! If the local node is a validator (i.e. contains an authority key), this module +//! gossips a heartbeat transaction with each new session. The heartbeat functions +//! as a simple mechanism to signal that the node is online in the current era. +//! +//! Received heartbeats are tracked for one era and reset with each new era. The +//! module exposes two public functions to query if a heartbeat has been received +//! in the current era or session. +//! +//! The heartbeat is a signed transaction, which was signed using the session key +//! and includes the recent best block number of the local validators chain as well +//! as the [NetworkState](../../core/offchain/struct.NetworkState.html). +//! It is submitted as an Unsigned Transaction via off-chain workers. +//! +//! - [`im_online::Trait`](./trait.Trait.html) +//! - [`Call`](./enum.Call.html) +//! - [`Module`](./struct.Module.html) +//! +//! ## Interface +//! +//! ### Public Functions +//! +//! - `is_online_in_current_era` - True if the validator sent a heartbeat in the current era. +//! - `is_online_in_current_session` - True if the validator sent a heartbeat in the current session. +//! +//! ## Usage +//! +//! ``` +//! use srml_support::{decl_module, dispatch::Result}; +//! use system::ensure_signed; +//! use srml_im_online::{self as im_online}; +//! +//! pub trait Trait: im_online::Trait {} +//! +//! decl_module! { +//! pub struct Module for enum Call where origin: T::Origin { +//! pub fn is_online(origin, authority_id: T::AuthorityId) -> Result { +//! let _sender = ensure_signed(origin)?; +//! let _is_online = >::is_online_in_current_era(&authority_id); +//! Ok(()) +//! } +//! } +//! } +//! # fn main() { } +//! ``` +//! +//! ## Dependencies +//! +//! This module depends on the [Session module](../srml_session/index.html). + +// Ensure we're `no_std` when compiling for Wasm. +#![cfg_attr(not(feature = "std"), no_std)] + +use substrate_primitives::{ + crypto::TypedKey, offchain::CryptoKey, + offchain::OpaqueNetworkState, + offchain::StorageKind, + sr25519, ed25519, +}; +use parity_codec::{Encode, Decode}; +use primitives::{ + ApplyError, traits::{Member, IsMember, Extrinsic as ExtrinsicT}, + transaction_validity::{TransactionValidity, TransactionLongevity, ValidTransaction}, +}; +use rstd::prelude::*; +use session::SessionIndex; +use sr_io::Printable; +use srml_support::{ + Parameter, StorageValue, decl_module, decl_event, decl_storage, + traits::Get, StorageDoubleMap, print, +}; +use system::ensure_none; + +// The local storage database key under which the worker progress status +// is tracked. +const DB_KEY: &[u8] = b"srml/im-online-worker-status"; + +// It's important to persist the worker state, since e.g. the +// server could be restarted while starting the gossip process, but before +// finishing it. With every execution of the off-chain worker we check +// if we need to recover and resume gossipping or if there is already +// another off-chain worker in the process of gossipping. +#[derive(Encode, Decode, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(Debug))] +struct WorkerStatus { + done: bool, + gossipping_at: BlockNumber, +} + +// Error which may occur while executing the off-chain code. +enum OffchainErr { + DecodeAuthorityId, + DecodeWorkerStatus, + ExtrinsicCreation, + FailedSigning, + NetworkState, + SubmitTransaction, +} + +impl Printable for OffchainErr { + fn print(self) { + match self { + OffchainErr::DecodeAuthorityId => print("Offchain error: decoding AuthorityId failed!"), + OffchainErr::DecodeWorkerStatus => print("Offchain error: decoding WorkerStatus failed!"), + OffchainErr::ExtrinsicCreation => print("Offchain error: extrinsic creation failed!"), + OffchainErr::FailedSigning => print("Offchain error: signing failed!"), + OffchainErr::NetworkState => print("Offchain error: fetching network state failed!"), + OffchainErr::SubmitTransaction => print("Offchain error: submitting transaction failed!"), + } + } +} + +/// Heartbeat which is send/received. +#[derive(Encode, Decode, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(Debug))] +pub struct Heartbeat + where BlockNumber: PartialEq + Eq + Decode + Encode, +{ + block_number: BlockNumber, + network_state: OpaqueNetworkState, + session_index: session::SessionIndex, + authority_id: AuthorityId, +} + +pub trait Trait: system::Trait + session::Trait { + /// The overarching event type. + type Event: From> + Into<::Event>; + + /// The function call. + type Call: From>; + + /// A extrinsic right from the external world. This is unchecked and so + /// can contain a signature. + type UncheckedExtrinsic: ExtrinsicT + Encode + Decode; + + /// The identifier type for an authority. + type AuthorityId: Member + Parameter + Default + TypedKey + Decode + Encode + AsRef<[u8]>; + + /// Number of sessions per era. + type SessionsPerEra: Get; + + /// Determine if an `AuthorityId` is a valid authority. + type IsValidAuthorityId: IsMember; +} + +decl_event!( + pub enum Event where + ::BlockNumber, + ::AuthorityId + { + /// A new heartbeat was received at this `BlockNumber` from `AuthorityId` + HeartbeatReceived(BlockNumber, AuthorityId), + } +); + +decl_storage! { + trait Store for Module as ImOnline { + // The block number when we should gossip. + GossipAt get(gossip_at) config(): T::BlockNumber; + + // The session index when the last new era started. + LastNewEraStart get(last_new_era_start) config(): Option; + + // For each session index we keep a mapping of `AuthorityId` to + // `offchain::OpaqueNetworkState`. + ReceivedHeartbeats get(received_heartbeats): double_map session::SessionIndex, + blake2_256(T::AuthorityId) => Vec; + } +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + /// Number of sessions per era. + const SessionsPerEra: session::SessionIndex = T::SessionsPerEra::get(); + + fn deposit_event() = default; + + fn heartbeat( + origin, + heartbeat: Heartbeat, + _signature: Vec + ) { + ensure_none(origin)?; + + let current_session = >::current_index(); + let exists = >::exists(¤t_session, &heartbeat.authority_id); + if !exists { + let now = >::block_number(); + Self::deposit_event(RawEvent::HeartbeatReceived(now, heartbeat.authority_id.clone())); + + let network_state = heartbeat.network_state.encode(); + >::insert(¤t_session, &heartbeat.authority_id, &network_state); + } + } + + // Runs after every block. + fn offchain_worker(now: T::BlockNumber) { + fn gossip_at(block_number: T::BlockNumber) -> Result<(), OffchainErr> { + // we run only when a local authority key is configured + if let Ok(key) = sr_io::pubkey(CryptoKey::AuthorityKey) { + let authority_id = ::AuthorityId::decode(&mut &key[..]) + .ok_or(OffchainErr::DecodeAuthorityId)?; + let network_state = + sr_io::network_state().map_err(|_| OffchainErr::NetworkState)?; + let heartbeat_data = Heartbeat { + block_number, + network_state, + session_index: >::current_index(), + authority_id, + }; + + let signature = sr_io::sign(CryptoKey::AuthorityKey, &heartbeat_data.encode()) + .map_err(|_| OffchainErr::FailedSigning)?; + let call = Call::heartbeat(heartbeat_data, signature); + let ex = T::UncheckedExtrinsic::new_unsigned(call.into()) + .ok_or(OffchainErr::ExtrinsicCreation)?; + sr_io::submit_transaction(&ex) + .map_err(|_| OffchainErr::SubmitTransaction)?; + set_worker_status::(block_number, true); + } + Ok(()) + } + + fn set_worker_status(gossipping_at: T::BlockNumber, done: bool) { + let enc = WorkerStatus { + done, + gossipping_at, + }; + sr_io::local_storage_set(StorageKind::PERSISTENT, DB_KEY, &enc.encode()); + } + + fn was_not_yet_gossipped( + now: T::BlockNumber, + next_gossip: T::BlockNumber, + ) -> Result { + let last_gossip = sr_io::local_storage_get(StorageKind::PERSISTENT, DB_KEY); + match last_gossip { + Some(l) => { + let worker_status: WorkerStatus = Decode::decode(&mut &l[..]) + .ok_or(OffchainErr::DecodeWorkerStatus)?; + + let was_aborted = !worker_status.done && worker_status.gossipping_at < now; + + // another off-chain worker is currently in the process of submitting + let already_submitting = + !worker_status.done && worker_status.gossipping_at == now; + + let not_yet_gossipped = + worker_status.done && worker_status.gossipping_at < next_gossip; + + let ret = (was_aborted && !already_submitting) || not_yet_gossipped; + Ok(ret) + }, + None => Ok(true), + } + } + + let next_gossip = >::get(); + let not_yet_gossipped = match was_not_yet_gossipped::(now, next_gossip) { + Ok(v) => v, + Err(err) => { + print(err); + return; + }, + }; + if next_gossip < now && not_yet_gossipped { + set_worker_status::(now, false); + + match gossip_at::(now) { + Ok(_) => {}, + Err(err) => print(err), + } + } + } + } +} + +impl Module { + /// Returns `true` if a heartbeat has been received for `AuthorityId` + /// during the current era. Otherwise `false`. + pub fn is_online_in_current_era(authority_id: &T::AuthorityId) -> bool { + let curr = >::current_index(); + match LastNewEraStart::get() { + Some(start) => { + // iterate over every session + for index in start..curr { + if >::exists(&index, authority_id) { + return true; + } + } + false + }, + None => >::exists(&curr, authority_id), + } + } + + /// Returns `true` if a heartbeat has been received for `AuthorityId` + /// during the current session. Otherwise `false`. + pub fn is_online_in_current_session(authority_id: &T::AuthorityId) -> bool { + let current_session = >::current_index(); + >::exists(¤t_session, authority_id) + } + + /// Session has just changed. + fn new_session() { + let now = >::block_number(); + >::put(now); + + let current_session = >::current_index(); + + match LastNewEraStart::get() { + Some(last_new_era_start) => { + let sessions_per_era = T::SessionsPerEra::get(); + + let new_era = current_session - last_new_era_start > sessions_per_era; + if new_era { + LastNewEraStart::put(current_session); + Self::remove_heartbeats(); + } + }, + None => LastNewEraStart::put(current_session), + }; + } + + // Remove all stored heartbeats. + fn remove_heartbeats() { + let curr = >::current_index(); + match LastNewEraStart::get() { + Some(start) => { + for index in start..curr { + >::remove_prefix(&index); + } + }, + None => >::remove_prefix(&curr), + } + } +} + +impl session::OneSessionHandler for Module { + type Key = ::AuthorityId; + + fn on_new_session<'a, I: 'a>(_changed: bool, _validators: I, _next_validators: I) { + Self::new_session(); + } + + fn on_disabled(_i: usize) { + // ignore + } +} + +impl srml_support::unsigned::ValidateUnsigned for Module { + type Call = Call; + + fn validate_unsigned(call: &Self::Call) -> srml_support::unsigned::TransactionValidity { + if let Call::heartbeat(heartbeat, signature) = call { + // verify that the incoming (unverified) pubkey is actually an authority id + let is_authority = T::IsValidAuthorityId::is_member(&heartbeat.authority_id); + if !is_authority { + return TransactionValidity::Invalid(ApplyError::BadSignature as i8); + } + + if >::is_online_in_current_session(&heartbeat.authority_id) { + // we already received a heartbeat for this authority + return TransactionValidity::Invalid(ApplyError::BadSignature as i8); + } + + if signature.len() != 64 { + return TransactionValidity::Invalid(ApplyError::BadSignature as i8); + } + + let signature = { + let mut array = [0; 64]; + array.copy_from_slice(&signature); // panics if not enough, hence the check above + array + }; + + let encoded_heartbeat = heartbeat.encode(); + + let signature_valid = match ::KEY_TYPE { + ed25519::Public::KEY_TYPE => + sr_io::ed25519_verify(&signature, &encoded_heartbeat, &heartbeat.authority_id), + sr25519::Public::KEY_TYPE => + sr_io::sr25519_verify(&signature, &encoded_heartbeat, &heartbeat.authority_id), + _ => return TransactionValidity::Invalid(ApplyError::BadSignature as i8), + }; + + if !signature_valid { + return TransactionValidity::Invalid(ApplyError::BadSignature as i8); + } + + // check if session index from heartbeat is recent + let current_session = >::current_index(); + if heartbeat.session_index < current_session { + return TransactionValidity::Invalid(ApplyError::BadSignature as i8); + } + + return TransactionValidity::Valid(ValidTransaction { + priority: 0, + requires: vec![], + provides: vec![encoded_heartbeat], + longevity: TransactionLongevity::max_value(), + propagate: true, + }) + } + TransactionValidity::Invalid(0) + } +} diff --git a/srml/indices/Cargo.toml b/srml/indices/Cargo.toml index de496f20c6bbaab9a637275b123abe6f0ace3954..bc67132c86f1dc44531a48c83e25046c2455ad52 100644 --- a/srml/indices/Cargo.toml +++ b/srml/indices/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" [dependencies] serde = { version = "1.0", optional = true } safe-mix = { version = "1.0", default-features = false} -parity-codec = { version = "3.3", default-features = false, features = ["derive"] } +parity-codec = { version = "4.1.1", default-features = false, features = ["derive"] } substrate-keyring = { path = "../../core/keyring", optional = true } rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false } runtime-io = { package = "sr-io", path = "../../core/sr-io", default-features = false } diff --git a/srml/indices/src/mock.rs b/srml/indices/src/mock.rs index e2ea51e89d958dd893d8a56df7d78c714f342236..151a5186f79b62bb0537c3a447b138021e276663 100644 --- a/srml/indices/src/mock.rs +++ b/srml/indices/src/mock.rs @@ -20,10 +20,9 @@ use std::collections::HashSet; use ref_thread_local::{ref_thread_local, RefThreadLocal}; -use primitives::BuildStorage; use primitives::testing::Header; use substrate_primitives::{H256, Blake2Hasher}; -use srml_support::impl_outer_origin; +use srml_support::{impl_outer_origin, parameter_types}; use {runtime_io, system}; use crate::{GenesisConfig, Module, Trait, IsDeadAccount, OnNewAccount, ResolveHint}; @@ -65,6 +64,11 @@ impl ResolveHint for TestResolveHint { // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. #[derive(Clone, PartialEq, Eq, Debug)] pub struct Runtime; +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; +} impl system::Trait for Runtime { type Origin = Origin; type Index = u64; @@ -74,7 +78,11 @@ impl system::Trait for Runtime { type AccountId = u64; type Lookup = Indices; type Header = Header; + type WeightMultiplierUpdate = (); type Event = (); + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; } impl Trait for Runtime { type AccountIndex = u64; @@ -90,7 +98,7 @@ pub fn new_test_ext() -> runtime_io::TestExternalities { for i in 1..5 { h.insert(i); } } - let mut t = system::GenesisConfig::::default().build_storage().unwrap().0; + let mut t = system::GenesisConfig::default().build_storage::().unwrap().0; t.extend(GenesisConfig:: { ids: vec![1, 2, 3, 4] }.build_storage().unwrap().0); diff --git a/srml/metadata/Cargo.toml b/srml/metadata/Cargo.toml index 0e9ae66540bfa46ab8e11ca03a1ac9b2ead08b25..cdb7a41ff860dea57de64ac4bc12e4b00390b5d5 100644 --- a/srml/metadata/Cargo.toml +++ b/srml/metadata/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -parity-codec = { version = "3.3", default-features = false, features = ["derive"] } +parity-codec = { version = "4.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0", optional = true, features = ["derive"] } rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false } primitives = { package = "substrate-primitives", path = "../../core/primitives", default-features = false } diff --git a/srml/metadata/src/lib.rs b/srml/metadata/src/lib.rs index b1ab57b506878559b3a8dd07e2d37a1e00c2629b..fca2a1cfdcf0d5162a0bfe9d263dc987460ca73d 100644 --- a/srml/metadata/src/lib.rs +++ b/srml/metadata/src/lib.rs @@ -102,10 +102,7 @@ impl serde::Serialize for DecodeDifferent B: serde::Serialize + 'static, O: serde::Serialize + 'static, { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { + fn serialize(&self, serializer: S) -> Result where S: serde::Serializer { match self { DecodeDifferent::Encode(b) => b.serialize(serializer), DecodeDifferent::Decoded(o) => o.serialize(serializer), @@ -162,10 +159,7 @@ impl std::fmt::Debug for FnEncode { #[cfg(feature = "std")] impl serde::Serialize for FnEncode { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { + fn serialize(&self, serializer: S) -> Result where S: serde::Serializer { self.0().serialize(serializer) } } @@ -190,21 +184,24 @@ pub struct EventMetadata { pub documentation: DecodeDifferentArray<&'static str, StringBuf>, } -/// All the metadata about a storage. +/// All the metadata about one storage entry. #[derive(Clone, PartialEq, Eq, Encode)] #[cfg_attr(feature = "std", derive(Decode, Debug, Serialize))] -pub struct StorageMetadata { - pub functions: DecodeDifferentArray, +pub struct StorageEntryMetadata { + pub name: DecodeDifferentStr, + pub modifier: StorageEntryModifier, + pub ty: StorageEntryType, + pub default: ByteGetter, + pub documentation: DecodeDifferentArray<&'static str, StringBuf>, } -/// All the metadata about a storage function. +/// All the metadata about one module constant. #[derive(Clone, PartialEq, Eq, Encode)] #[cfg_attr(feature = "std", derive(Decode, Debug, Serialize))] -pub struct StorageFunctionMetadata { +pub struct ModuleConstantMetadata { pub name: DecodeDifferentStr, - pub modifier: StorageFunctionModifier, - pub ty: StorageFunctionType, - pub default: ByteGetter, + pub ty: DecodeDifferentStr, + pub value: ByteGetter, pub documentation: DecodeDifferentArray<&'static str, StringBuf>, } @@ -238,10 +235,7 @@ impl Eq for DefaultByteGetter { } #[cfg(feature = "std")] impl serde::Serialize for DefaultByteGetter { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { + fn serialize(&self, serializer: S) -> Result where S: serde::Serializer { self.0.default_byte().serialize(serializer) } } @@ -264,10 +258,10 @@ pub enum StorageHasher { Twox64Concat, } -/// A storage function type. +/// A storage entry type. #[derive(Clone, PartialEq, Eq, Encode)] #[cfg_attr(feature = "std", derive(Decode, Debug, Serialize))] -pub enum StorageFunctionType { +pub enum StorageEntryType { Plain(DecodeDifferentStr), Map { hasher: StorageHasher, @@ -284,10 +278,10 @@ pub enum StorageFunctionType { }, } -/// A storage function modifier. +/// A storage entry modifier. #[derive(Clone, PartialEq, Eq, Encode)] #[cfg_attr(feature = "std", derive(Decode, Debug, Serialize))] -pub enum StorageFunctionModifier { +pub enum StorageEntryModifier { Optional, Default, } @@ -313,8 +307,10 @@ pub enum RuntimeMetadata { V3(RuntimeMetadataDeprecated), /// Version 4 for runtime metadata. No longer used. V4(RuntimeMetadataDeprecated), - /// Version 5 for runtime metadata. - V5(RuntimeMetadataV5), + /// Version 5 for runtime metadata. No longer used. + V5(RuntimeMetadataDeprecated), + /// Version 6 for runtime metadata. + V6(RuntimeMetadataV6), } /// Enum that should fail. @@ -323,8 +319,7 @@ pub enum RuntimeMetadata { pub enum RuntimeMetadataDeprecated { } impl Encode for RuntimeMetadataDeprecated { - fn encode_to(&self, _dest: &mut W) { - } + fn encode_to(&self, _dest: &mut W) {} } #[cfg(feature = "std")] @@ -337,7 +332,7 @@ impl Decode for RuntimeMetadataDeprecated { /// The metadata of a runtime. #[derive(Eq, Encode, PartialEq)] #[cfg_attr(feature = "std", derive(Decode, Debug, Serialize))] -pub struct RuntimeMetadataV5 { +pub struct RuntimeMetadataV6 { pub modules: DecodeDifferentArray, } @@ -347,12 +342,14 @@ pub struct RuntimeMetadataV5 { pub struct ModuleMetadata { pub name: DecodeDifferentStr, pub prefix: DecodeDifferent, StringBuf>, - pub storage: ODFnA, + pub storage: ODFnA, pub calls: ODFnA, pub event: ODFnA, + pub constants: DFnA, } -type ODFnA = Option, Vec>>; +type ODFnA = Option>; +type DFnA = DecodeDifferent, Vec>; impl Into for RuntimeMetadataPrefixed { fn into(self) -> primitives::OpaqueMetadata { diff --git a/srml/session/Cargo.toml b/srml/session/Cargo.toml index 2c8c040ea1519408ac68cd6144b2725aa0fd4fac..51b613497f47a373a7414d8825914bb27f38c666 100644 --- a/srml/session/Cargo.toml +++ b/srml/session/Cargo.toml @@ -7,20 +7,22 @@ edition = "2018" [dependencies] serde = { version = "1.0", optional = true } safe-mix = { version = "1.0", default-features = false} -parity-codec = { version = "3.3", default-features = false, features = ["derive"] } +parity-codec = { version = "4.1.1", default-features = false, features = ["derive"] } rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false } primitives = { package = "sr-primitives", path = "../../core/sr-primitives", default-features = false } srml-support = { path = "../support", default-features = false } 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 } [dev-dependencies] substrate-primitives = { path = "../../core/primitives" } -runtime_io = { package = "sr-io", path = "../../core/sr-io" } lazy_static = "1.0" [features] -default = ["std"] +default = ["std", "historical"] +historical = ["substrate-trie"] std = [ "serde", "safe-mix/std", @@ -28,5 +30,6 @@ std = [ "rstd/std", "srml-support/std", "primitives/std", - "timestamp/std" + "timestamp/std", + "substrate-trie/std" ] diff --git a/srml/session/src/historical.rs b/srml/session/src/historical.rs new file mode 100644 index 0000000000000000000000000000000000000000..c6755c3ba3592a10c7eaf9dca2e9e33ddf7f8b08 --- /dev/null +++ b/srml/session/src/historical.rs @@ -0,0 +1,447 @@ +// 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 . + +//! An opt-in utility for tracking historical sessions in SRML-session. +//! +//! This is generally useful when implementing blockchains that require accountable +//! safety where validators from some amount f prior sessions must remain slashable. +//! +//! Rather than store the full session data for any given session, we instead commit +//! to the roots of merkle tries containing the session data. +//! +//! These roots and proofs of inclusion can be generated at any time during the current session. +//! Afterwards, the proofs can be fed to a consensus module when reporting misbehavior. + +use rstd::prelude::*; +use parity_codec::{Encode, Decode}; +use primitives::KeyTypeId; +use primitives::traits::{Convert, OpaqueKeys, Hash as HashT}; +use srml_support::{ + StorageValue, StorageMap, decl_module, decl_storage, +}; +use srml_support::{Parameter, print}; +use substrate_trie::{MemoryDB, Trie, TrieMut, TrieDBMut, TrieDB, Recorder}; + +use super::{SessionIndex, Module as SessionModule}; + +/// Trait necessary for the historical module. +pub trait Trait: super::Trait { + /// Full identification of the validator. + type FullIdentification: Parameter; + + /// A conversion from validator ID to full identification. + /// + /// This should contain any references to economic actors associated with the + /// validator, since they may be outdated by the time this is queried from a + /// historical trie. + /// + /// This mapping is expected to remain stable in between calls to + /// `Self::OnSessionEnding::on_session_ending` which return new validators. + type FullIdentificationOf: Convert>; +} + +decl_storage! { + trait Store for Module as Session { + /// Mapping from historical session indices to session-data root hash. + HistoricalSessions get(historical_root): map SessionIndex => Option; + /// Queued full identifications for queued sessions whose validators have become obsolete. + CachedObsolete get(cached_obsolete): map SessionIndex + => Option>; + /// The range of historical sessions we store. [first, last) + StoredRange: Option<(SessionIndex, SessionIndex)>; + } +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { } +} + +impl Module { + /// Prune historical stored session roots up to (but not including) + /// `up_to`. + pub fn prune_up_to(up_to: SessionIndex) { + ::StoredRange::mutate(|range| { + let (start, end) = match *range { + Some(range) => range, + None => return, // nothing to prune. + }; + + let up_to = rstd::cmp::min(up_to, end); + + if up_to < start { + return // out of bounds. harmless. + } + + (start..up_to).for_each(::HistoricalSessions::remove); + + let new_start = up_to; + *range = if new_start == end { + None // nothing is stored. + } else { + Some((new_start, end)) + } + }) + } +} + +/// Specialization of the crate-level `OnSessionEnding` which returns the old +/// set of full identification when changing the validator set. +pub trait OnSessionEnding: crate::OnSessionEnding { + /// Returns the set of new validators, if any, along with the old validators + /// and their full identifications. + fn on_session_ending(ending: SessionIndex, applied_at: SessionIndex) + -> Option<(Vec, Vec<(ValidatorId, FullIdentification)>)>; +} + +/// An `OnSessionEnding` implementation that wraps an inner `I` and also +/// sets the historical trie root of the ending session. +pub struct NoteHistoricalRoot(rstd::marker::PhantomData<(T, I)>); + +impl crate::OnSessionEnding for NoteHistoricalRoot + where I: OnSessionEnding +{ + fn on_session_ending(ending: SessionIndex, applied_at: SessionIndex) -> Option> { + StoredRange::mutate(|range| { + range.get_or_insert_with(|| (ending, ending)).1 = ending + 1; + }); + + // do all of this _before_ calling the other `on_session_ending` impl + // so that we have e.g. correct exposures from the _current_. + + match ProvingTrie::::generate_for(ending) { + Ok(trie) => >::insert(ending, &trie.root), + Err(reason) => { + print("Failed to generate historical ancestry-inclusion proof."); + print(reason); + } + }; + + // trie has been generated for this session, so it's no longer queued. + >::remove(&ending); + + let (new_validators, old_exposures) = >::on_session_ending(ending, applied_at)?; + + // every session from `ending+1 .. applied_at` now has obsolete `FullIdentification` + // now that a new validator election has occurred. + // we cache these in the trie until those sessions themselves end. + for obsolete in (ending + 1) .. applied_at { + >::insert(obsolete, &old_exposures); + } + + Some(new_validators) + } +} + +type HasherOf = <::Hashing as HashT>::Hasher; + +/// A tuple of the validator's ID and their full identification. +pub type IdentificationTuple = (::ValidatorId, ::FullIdentification); + +/// a trie instance for checking and generating proofs. +pub struct ProvingTrie { + db: MemoryDB>, + root: T::Hash, +} + +impl ProvingTrie { + fn generate_for(now: SessionIndex) -> Result { + let mut db = MemoryDB::default(); + let mut root = Default::default(); + + fn build(root: &mut T::Hash, db: &mut MemoryDB>, validators: I) + -> Result<(), &'static str> + where I: IntoIterator)> + { + let mut trie = TrieDBMut::new(db, root); + for (i, (validator, full_id)) in validators.into_iter().enumerate() { + let i = i as u32; + let keys = match >::load_keys(&validator) { + None => continue, + Some(k) => k, + }; + + let full_id = full_id.or_else(|| T::FullIdentificationOf::convert(validator.clone())); + let full_id = match full_id { + None => return Err("no full identification for a current validator"), + Some(full) => (validator, full), + }; + + // map each key to the owner index. + for key_id in T::Keys::key_ids() { + let key = keys.get_raw(key_id); + let res = (key_id, key).using_encoded(|k| + i.using_encoded(|v| + trie.insert(k, v) + ) + ); + + let _ = res.map_err(|_| "failed to insert into trie")?; + } + + // map each owner index to the full identification. + let _ = i.using_encoded(|k| full_id.using_encoded(|v| trie.insert(k, v))) + .map_err(|_| "failed to insert into trie")?; + } + + Ok(()) + } + + // if the current session's full identifications are obsolete but cached, + // use those. + if let Some(obsolete) = >::get(&now) { + build::(&mut root, &mut db, obsolete.into_iter().map(|(v, f)| (v, Some(f))))? + } else { + let validators = >::validators(); + build::(&mut root, &mut db, validators.into_iter().map(|v| (v, None)))? + } + + Ok(ProvingTrie { + db, + root, + }) + } + + fn from_nodes(root: T::Hash, nodes: &[Vec]) -> Self { + use substrate_trie::HashDBT; + + let mut memory_db = MemoryDB::default(); + for node in nodes { + HashDBT::insert(&mut memory_db, &[], &node[..]); + } + + ProvingTrie { + db: memory_db, + root, + } + } + + /// Prove the full verification data for a given key and key ID. + pub fn prove(&self, key_id: KeyTypeId, key_data: &[u8]) -> Option>> { + let trie = TrieDB::new(&self.db, &self.root).ok()?; + let mut recorder = Recorder::new(); + let val_idx = (key_id, key_data).using_encoded(|s| { + trie.get_with(s, &mut recorder) + .ok()? + .and_then(|raw| u32::decode(&mut &*raw)) + })?; + + val_idx.using_encoded(|s| { + trie.get_with(s, &mut recorder) + .ok()? + .and_then(|raw| >::decode(&mut &*raw)) + })?; + + Some(recorder.drain().into_iter().map(|r| r.data).collect()) + } + + /// Access the underlying trie root. + pub fn root(&self) -> &T::Hash { + &self.root + } + + // Check a proof contained within the current memory-db. Returns `None` if the + // nodes within the current `MemoryDB` are insufficient to query the item. + fn query(&self, key_id: KeyTypeId, key_data: &[u8]) -> Option> { + let trie = TrieDB::new(&self.db, &self.root).ok()?; + let val_idx = (key_id, key_data).using_encoded(|s| trie.get(s)) + .ok()? + .and_then(|raw| u32::decode(&mut &*raw))?; + + val_idx.using_encoded(|s| trie.get(s)) + .ok()? + .and_then(|raw| >::decode(&mut &*raw)) + } + +} + +/// Proof of ownership of a specific key. +#[derive(Encode, Decode, Clone)] +pub struct Proof { + session: SessionIndex, + trie_nodes: Vec>, +} + +impl> srml_support::traits::KeyOwnerProofSystem<(KeyTypeId, D)> + for Module +{ + type Proof = Proof; + type FullIdentification = IdentificationTuple; + + fn prove(key: (KeyTypeId, D)) -> Option { + let session = >::current_index(); + let trie = ProvingTrie::::generate_for(session).ok()?; + + let (id, data) = key; + + trie.prove(id, data.as_ref()).map(|trie_nodes| Proof { + session, + trie_nodes, + }) + } + + fn check_proof(key: (KeyTypeId, D), proof: Proof) -> Option> { + let (id, data) = key; + + if proof.session == >::current_index() { + >::key_owner(id, data.as_ref()).and_then(|owner| + T::FullIdentificationOf::convert(owner.clone()).map(move |id| (owner, id)) + ) + } else { + let root = >::get(&proof.session)?; + let trie = ProvingTrie::::from_nodes(root, &proof.trie_nodes); + + trie.query(id, data.as_ref()) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use runtime_io::with_externalities; + use substrate_primitives::Blake2Hasher; + use primitives::{ + traits::OnInitialize, + testing::{UintAuthorityId, UINT_DUMMY_KEY}, + }; + use crate::mock::{ + NEXT_VALIDATORS, force_new_session, + set_next_validators, Test, System, Session, + }; + use srml_support::traits::KeyOwnerProofSystem; + + type Historical = Module; + + fn new_test_ext() -> runtime_io::TestExternalities { + let mut t = system::GenesisConfig::default().build_storage::().unwrap().0; + let (storage, _child_storage) = crate::GenesisConfig:: { + keys: NEXT_VALIDATORS.with(|l| + l.borrow().iter().cloned().map(|i| (i, UintAuthorityId(i))).collect() + ), + }.build_storage().unwrap(); + t.extend(storage); + runtime_io::TestExternalities::new(t) + } + + #[test] + fn generated_proof_is_good() { + with_externalities(&mut new_test_ext(), || { + set_next_validators(vec![1, 2]); + force_new_session(); + + System::set_block_number(1); + Session::on_initialize(1); + + let encoded_key_1 = UintAuthorityId(1).encode(); + let proof = Historical::prove((UINT_DUMMY_KEY, &encoded_key_1[..])).unwrap(); + + // proof-checking in the same session is OK. + assert!( + Historical::check_proof( + (UINT_DUMMY_KEY, &encoded_key_1[..]), + proof.clone(), + ).is_some() + ); + + set_next_validators(vec![1, 2, 4]); + force_new_session(); + + assert!(Historical::cached_obsolete(&(proof.session + 1)).is_none()); + + System::set_block_number(2); + Session::on_initialize(2); + + assert!(Historical::cached_obsolete(&(proof.session + 1)).is_some()); + + assert!(Historical::historical_root(proof.session).is_some()); + assert!(Session::current_index() > proof.session); + + // proof-checking in the next session is also OK. + assert!( + Historical::check_proof( + (UINT_DUMMY_KEY, &encoded_key_1[..]), + proof.clone(), + ).is_some() + ); + + set_next_validators(vec![1, 2, 5]); + + force_new_session(); + System::set_block_number(3); + Session::on_initialize(3); + + assert!(Historical::cached_obsolete(&(proof.session + 1)).is_none()); + }); + } + + #[test] + fn prune_up_to_works() { + with_externalities(&mut new_test_ext(), || { + for i in 1..101u64 { + set_next_validators(vec![i]); + force_new_session(); + + System::set_block_number(i); + Session::on_initialize(i); + + } + + assert_eq!(StoredRange::get(), Some((0, 100))); + + for i in 1..100 { + assert!(Historical::historical_root(i).is_some()) + } + + Historical::prune_up_to(10); + assert_eq!(StoredRange::get(), Some((10, 100))); + + Historical::prune_up_to(9); + assert_eq!(StoredRange::get(), Some((10, 100))); + + for i in 10..100 { + assert!(Historical::historical_root(i).is_some()) + } + + Historical::prune_up_to(99); + assert_eq!(StoredRange::get(), Some((99, 100))); + + Historical::prune_up_to(100); + assert_eq!(StoredRange::get(), None); + + for i in 101..201u64 { + set_next_validators(vec![i]); + force_new_session(); + + System::set_block_number(i); + Session::on_initialize(i); + + } + + assert_eq!(StoredRange::get(), Some((100, 200))); + + for i in 101..200 { + assert!(Historical::historical_root(i).is_some()) + } + + Historical::prune_up_to(9999); + assert_eq!(StoredRange::get(), None); + + for i in 101..200 { + assert!(Historical::historical_root(i).is_none()) + } + }); + } +} diff --git a/srml/session/src/lib.rs b/srml/session/src/lib.rs index 3ae7c9801299be7026bcadfe4c66be07dc77b36c..d425c44e2eb506d4fb34232eb0aee7b30a65f679 100644 --- a/srml/session/src/lib.rs +++ b/srml/session/src/lib.rs @@ -29,13 +29,17 @@ //! //! //! - **Session:** A session is a period of time that has a constant set of validators. Validators can only join -//! or exit the validator set at a session change. It is measured in block numbers and set with `set_length` -//! during a session for use in subsequent sessions. +//! or exit the validator set at a session change. It is measured in block numbers. The block where a session is +//! ended is determined by the `ShouldSessionEnd` trait. When the session is ending, a new validator set +//! can be chosen by `OnSessionEnding` implementations. //! - **Session key:** A session key is actually several keys kept together that provide the various signing //! functions required by network authorities/validators in pursuit of their duties. +//! - **Validator ID:** Every account has an associated validator ID. For some simple staking systems, this +//! may just be the same as the account ID. For staking systems using a stash/controller model, +//! the validator ID would be the stash account ID of the controller. //! - **Session key configuration process:** A session key is set using `set_key` for use in the -//! next session. It is stored in `NextKeyFor`, a mapping between the caller's `AccountID` and the session -//! key provided. `set_key` allows users to set their session key prior to becoming a validator. +//! next session. It is stored in `NextKeyFor`, a mapping between the caller's `ValidatorId` and the session +//! keys provided. `set_key` allows users to set their session key prior to being selected as validator. //! It is a public call since it uses `ensure_signed`, which checks that the origin is a signed account. //! As such, the account ID of the origin stored in in `NextKeyFor` may not necessarily be associated with //! a block author or a validator. The session keys of accounts are removed once their account balance is zero. @@ -115,79 +119,114 @@ #![cfg_attr(not(feature = "std"), no_std)] -use rstd::{prelude::*, marker::PhantomData, ops::Rem}; -#[cfg(not(feature = "std"))] -use rstd::alloc::borrow::ToOwned; +use rstd::{prelude::*, marker::PhantomData, ops::{Sub, Rem}}; use parity_codec::Decode; -use primitives::traits::{Zero, Saturating, Member, OpaqueKeys}; -use srml_support::{StorageValue, StorageMap, for_each_tuple, decl_module, decl_event, decl_storage}; -use srml_support::{ensure, traits::{OnFreeBalanceZero, Get}, Parameter, print}; -use system::ensure_signed; +use primitives::KeyTypeId; +use primitives::traits::{Convert, Zero, Member, OpaqueKeys, TypedKey}; +use srml_support::{ + dispatch::Result, ConsensusEngineId, StorageValue, StorageDoubleMap, for_each_tuple, + decl_module, decl_event, decl_storage, +}; +use srml_support::{ensure, traits::{OnFreeBalanceZero, Get, FindAuthor}, Parameter}; +use system::{self, ensure_signed}; + +#[cfg(test)] +mod mock; + +#[cfg(feature = "historical")] +pub mod historical; /// Simple index type with which we can count sessions. pub type SessionIndex = u32; +/// Decides whether the session should be ended. pub trait ShouldEndSession { + /// Return `true` if the session should be ended. fn should_end_session(now: BlockNumber) -> bool; } +/// Ends the session after a fixed period of blocks. +/// +/// The first session will have length of `Offset`, and +/// the following sessions will have length of `Period`. +/// This may prove nonsensical if `Offset` >= `Period`. pub struct PeriodicSessions< Period, Offset, >(PhantomData<(Period, Offset)>); impl< - BlockNumber: Rem + Saturating + Zero, + BlockNumber: Rem + Sub + Zero + PartialOrd, Period: Get, Offset: Get, > ShouldEndSession for PeriodicSessions { fn should_end_session(now: BlockNumber) -> bool { - ((now.saturating_sub(Offset::get())) % Period::get()).is_zero() + let offset = Offset::get(); + now >= offset && ((now - offset) % Period::get()).is_zero() } } -pub trait OnSessionEnding { +/// An event handler for when the session is ending. +pub trait OnSessionEnding { /// Handle the fact that the session is ending, and optionally provide the new validator set. - fn on_session_ending(i: SessionIndex) -> Option>; + /// + /// `ending_index` is the index of the currently ending session. + /// The returned validator set, if any, will not be applied until `next_index`. + /// `next_index` is guaranteed to be at least `ending_index + 1`, since session indices don't + /// repeat. + fn on_session_ending(ending_index: SessionIndex, next_index: SessionIndex) -> Option>; } impl OnSessionEnding for () { - fn on_session_ending(_: SessionIndex) -> Option> { None } + fn on_session_ending(_: SessionIndex, _: SessionIndex) -> Option> { None } } /// Handler for when a session keys set changes. -pub trait SessionHandler { +pub trait SessionHandler { /// Session set has changed; act appropriately. - fn on_new_session(changed: bool, validators: &[(AccountId, Ks)]); + fn on_new_session( + changed: bool, + validators: &[(ValidatorId, Ks)], + queued_validators: &[(ValidatorId, Ks)], + ); /// A validator got disabled. Act accordingly until a new session begins. fn on_disabled(validator_index: usize); } -pub trait OneSessionHandler { - type Key: Decode + Default; - fn on_new_session<'a, I: 'a>(changed: bool, validators: I) - where I: Iterator, AccountId: 'a; +/// One session-key type handler. +pub trait OneSessionHandler { + /// The key type expected. + type Key: Decode + Default + TypedKey; + + fn on_new_session<'a, I: 'a>(changed: bool, validators: I, queued_validators: I) + where I: Iterator, ValidatorId: 'a; fn on_disabled(i: usize); } macro_rules! impl_session_handlers { () => ( impl SessionHandler for () { - fn on_new_session(_: bool, _: &[(AId, Ks)]) {} + fn on_new_session(_: bool, _: &[(AId, Ks)], _: &[(AId, Ks)]) {} fn on_disabled(_: usize) {} } ); ( $($t:ident)* ) => { impl ),*> SessionHandler for ( $( $t , )* ) { - fn on_new_session(changed: bool, validators: &[(AId, Ks)]) { - let mut i: usize = 0; + fn on_new_session( + changed: bool, + validators: &[(AId, Ks)], + queued_validators: &[(AId, Ks)], + ) { $( - i += 1; - let our_keys = validators.iter() - .map(|k| (&k.0, k.1.get::<$t::Key>(i - 1).unwrap_or_default())); - $t::on_new_session(changed, our_keys); + let our_keys: Box> = Box::new(validators.iter() + .map(|k| (&k.0, k.1.get::<$t::Key>(<$t::Key as TypedKey>::KEY_TYPE) + .unwrap_or_default()))); + let queued_keys: Box> = Box::new(queued_validators.iter() + .map(|k| (&k.0, k.1.get::<$t::Key>(<$t::Key as TypedKey>::KEY_TYPE) + .unwrap_or_default()))); + $t::on_new_session(changed, our_keys, queued_keys); )* } fn on_disabled(i: usize) { @@ -201,54 +240,115 @@ macro_rules! impl_session_handlers { for_each_tuple!(impl_session_handlers); +/// Handler for selecting the genesis validator set. +pub trait SelectInitialValidators { + /// Returns the initial validator set. If `None` is returned + /// all accounts that have session keys set in the genesis block + /// will be validators. + fn select_initial_validators() -> Option>; +} + +/// Implementation of `SelectInitialValidators` that does nothing. +impl SelectInitialValidators for () { + fn select_initial_validators() -> Option> { + None + } +} pub trait Trait: system::Trait { /// The overarching event type. type Event: From + Into<::Event>; + /// A stable ID for a validator. + type ValidatorId: Member + Parameter; + + /// A conversion to validator ID to account ID. + type ValidatorIdOf: Convert>; + /// Indicator for when to end the session. type ShouldEndSession: ShouldEndSession; /// Handler for when a session is about to end. - type OnSessionEnding: OnSessionEnding; + type OnSessionEnding: OnSessionEnding; /// Handler when a session has changed. - type SessionHandler: SessionHandler; + type SessionHandler: SessionHandler; /// The keys. type Keys: OpaqueKeys + Member + Parameter + Default; + + /// Select initial validators. + type SelectInitialValidators: SelectInitialValidators; } -type OpaqueKey = Vec; +const DEDUP_KEY_PREFIX: &[u8] = b":session:keys"; decl_storage! { trait Store for Module as Session { /// The current set of validators. - pub Validators get(validators) config(): Vec; + Validators get(validators): Vec; /// Current index of the session. - pub CurrentIndex get(current_index): SessionIndex; + CurrentIndex get(current_index): SessionIndex; /// True if anything has changed in this session. Changed: bool; - /// The next key for a given validator. - NextKeyFor build(|config: &GenesisConfig| { - config.keys.clone() - }): map T::AccountId => Option; - - /// The keys that are currently active. - Active build(|config: &GenesisConfig| { - (0..T::Keys::count()).map(|i| ( - i as u32, - config.keys.iter() - .map(|x| x.1.get_raw(i).to_vec()) - .collect::>(), - )).collect::)>>() - }): map u32 => Vec; + /// Queued keys changed. + QueuedChanged: bool; + + /// 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)>; + + /// The next session keys for a validator. + /// + /// The first key is always `DEDUP_KEY_PREFIX` to have all the data in the same branch of + /// the trie. Having all data in the same branch should prevent slowing down other queries. + NextKeys: double_map hasher(twox_64_concat) Vec, blake2_256(T::ValidatorId) => Option; + + /// The owner of a key. The second key is the `KeyTypeId` + the encoded key. + /// + /// The first key is always `DEDUP_KEY_PREFIX` to have all the data in the same branch of + /// the trie. Having all data in the same branch should prevent slowing down other queries. + KeyOwner: double_map hasher(twox_64_concat) Vec, blake2_256((KeyTypeId, Vec)) => Option; } add_extra_genesis { - config(keys): Vec<(T::AccountId, T::Keys)>; + config(keys): Vec<(T::ValidatorId, T::Keys)>; + build(| + storage: &mut primitives::StorageOverlay, + _: &mut primitives::ChildrenStorageOverlay, + config: &GenesisConfig + | { + runtime_io::with_storage(storage, || { + for (who, keys) in config.keys.iter().cloned() { + assert!( + >::load_keys(&who).is_none(), + "genesis config contained duplicate validator {:?}", who, + ); + + >::do_set_keys(&who, keys) + .expect("genesis config must not contain duplicates; qed"); + } + + let initial_validators = T::SelectInitialValidators::select_initial_validators() + .unwrap_or_else(|| config.keys.iter().map(|(ref v, _)| v.clone()).collect()); + + assert!(!initial_validators.is_empty(), "Empty validator set in genesis block!"); + + let queued_keys: Vec<_> = initial_validators + .iter() + .cloned() + .map(|v| ( + v.clone(), + >::load_keys(&v).unwrap_or_default(), + )) + .collect(); + + >::put(initial_validators); + >::put(queued_keys); + }); + }); } } @@ -262,6 +362,10 @@ decl_event!( decl_module! { pub struct Module for enum Call where origin: T::Origin { + /// Used as first key for `NextKeys` and `KeyOwner` to put all the data into the same branch + /// of the trie. + const DEDUP_KEY_PREFIX: &[u8] = DEDUP_KEY_PREFIX; + fn deposit_event() = default; /// Sets the session key(s) of the function caller to `key`. @@ -271,51 +375,25 @@ decl_module! { /// The dispatch origin of this function must be signed. /// /// # - /// - O(1). + /// - O(log n) in number of accounts. /// - One extra DB entry. /// # - fn set_keys(origin, keys: T::Keys, proof: Vec) { + fn set_keys(origin, keys: T::Keys, proof: Vec) -> Result { let who = ensure_signed(origin)?; ensure!(keys.ownership_proof_is_valid(&proof), "invalid ownership proof"); - let old_keys = >::get(&who); - let mut updates = vec![]; + let who = match T::ValidatorIdOf::convert(who) { + Some(val_id) => val_id, + None => return Err("no associated validator ID for account."), + }; - for i in 0..T::Keys::count() { - let new_key = keys.get_raw(i); - let maybe_old_key = old_keys.as_ref().map(|o| o.get_raw(i)); - if maybe_old_key == Some(new_key) { - // no change. - updates.push(None); - continue; - } - let mut active = >::get(i as u32); - match active.binary_search_by(|k| k[..].cmp(&new_key)) { - Ok(_) => return Err("duplicate key provided"), - Err(pos) => active.insert(pos, new_key.to_owned()), - } - if let Some(old_key) = maybe_old_key { - match active.binary_search_by(|k| k[..].cmp(&old_key)) { - Ok(pos) => { active.remove(pos); } - Err(_) => { - // unreachable as long as our state is valid. we don't want to panic if - // it isn't, though. - print("ERROR: active doesn't contain outgoing key"); - } - } - } - updates.push(Some((i, active))); - } + Self::do_set_keys(&who, keys)?; - // Update the active sets. - for (i, active) in updates.into_iter().filter_map(|x| x) { - >::insert(i as u32, active); - } - // Set new keys value for next session. - >::insert(who, keys); // Something changed. - >::put(true); + Changed::put(true); + + Ok(()) } /// Called when a block is finalized. Will rotate session if it is the last @@ -329,154 +407,183 @@ decl_module! { } impl Module { - /// Move on to next session: register the new authority set. + /// Move on to next session. Register new validator set and session keys. Changes + /// to the validator set have a session of delay to take effect. This allows for + /// equivocation punishment after a fork. pub fn rotate_session() { - // Increment current session index. - let session_index = >::get(); + let session_index = CurrentIndex::get(); - let mut changed = >::take(); + let changed = QueuedChanged::get(); + let mut next_changed = Changed::take(); + + // Get queued session keys and validators. + let session_keys = >::get(); + let validators = session_keys.iter() + .map(|(validator, _)| validator.clone()) + .collect::>(); + >::put(&validators); - // See if we have a new validator set. - let validators = if let Some(new) = T::OnSessionEnding::on_session_ending(session_index) { - changed = true; - >::put(&new); - new + let applied_at = session_index + 2; + + // Get next validator set. + let maybe_validators = T::OnSessionEnding::on_session_ending(session_index, applied_at); + let next_validators = if let Some(validators) = maybe_validators { + next_changed = true; + validators } else { >::get() }; + // Increment session index. let session_index = session_index + 1; - >::put(session_index); + CurrentIndex::put(session_index); + + // Queue next session keys. + let queued_amalgamated = next_validators.into_iter() + .map(|a| { let k = Self::load_keys(&a).unwrap_or_default(); (a, k) }) + .collect::>(); + + >::put(queued_amalgamated.clone()); + QueuedChanged::put(next_changed); // Record that this happened. Self::deposit_event(Event::NewSession(session_index)); // Tell everyone about the new session keys. - let amalgamated = validators.into_iter() - .map(|a| { let k = >::get(&a).unwrap_or_default(); (a, k) }) - .collect::>(); - T::SessionHandler::on_new_session::(changed, &amalgamated); + T::SessionHandler::on_new_session::(changed, &session_keys, &queued_amalgamated); } /// Disable the validator of index `i`. pub fn disable_index(i: usize) { T::SessionHandler::on_disabled(i); - >::put(true); + Changed::put(true); } /// Disable the validator identified by `c`. (If using with the staking module, this would be - /// their *controller* account.) - pub fn disable(c: &T::AccountId) -> rstd::result::Result<(), ()> { + /// their *stash* account.) + pub fn disable(c: &T::ValidatorId) -> rstd::result::Result<(), ()> { Self::validators().iter().position(|i| i == c).map(Self::disable_index).ok_or(()) } -} -impl OnFreeBalanceZero for Module { - fn on_free_balance_zero(who: &T::AccountId) { - >::remove(who); - } -} + // perform the set_key operation, checking for duplicates. + // does not set `Changed`. + fn do_set_keys(who: &T::ValidatorId, keys: T::Keys) -> Result { + let old_keys = Self::load_keys(&who); -#[cfg(test)] -mod tests { - use super::*; - use std::cell::RefCell; - use srml_support::{impl_outer_origin, assert_ok}; - use runtime_io::with_externalities; - use substrate_primitives::{H256, Blake2Hasher}; - use primitives::BuildStorage; - use primitives::traits::{BlakeTwo256, IdentityLookup, OnInitialize}; - use primitives::testing::{Header, UintAuthorityId}; + for id in T::Keys::key_ids() { + let key = keys.get_raw(id); - impl_outer_origin!{ - pub enum Origin for Test {} - } + // ensure keys are without duplication. + ensure!( + Self::key_owner(id, key).map_or(true, |owner| &owner == who), + "registered duplicate key" + ); - thread_local!{ - static NEXT_VALIDATORS: RefCell> = RefCell::new(vec![1, 2, 3]); - static AUTHORITIES: RefCell> = - RefCell::new(vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(3)]); - static FORCE_SESSION_END: RefCell = RefCell::new(false); - static SESSION_LENGTH: RefCell = RefCell::new(2); - } + if let Some(old) = old_keys.as_ref().map(|k| k.get_raw(id)) { + if key == old { + continue; + } + + Self::clear_key_owner(id, old); + } - pub struct TestShouldEndSession; - impl ShouldEndSession for TestShouldEndSession { - fn should_end_session(now: u64) -> bool { - let l = SESSION_LENGTH.with(|l| *l.borrow()); - now % l == 0 || FORCE_SESSION_END.with(|l| { let r = *l.borrow(); *l.borrow_mut() = false; r }) + Self::put_key_owner(id, key, &who); } + + Self::put_keys(&who, &keys); + + Ok(()) } - pub struct TestSessionHandler; - impl SessionHandler for TestSessionHandler { - fn on_new_session(_changed: bool, validators: &[(u64, T)]) { - AUTHORITIES.with(|l| - *l.borrow_mut() = validators.iter().map(|(_, id)| id.get::(0).unwrap_or_default()).collect() - ); + 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); + } + + Changed::put(true); } - fn on_disabled(_validator_index: usize) {} } - pub struct TestOnSessionEnding; - impl OnSessionEnding for TestOnSessionEnding { - fn on_session_ending(_: SessionIndex) -> Option> { - Some(NEXT_VALIDATORS.with(|l| l.borrow().clone())) - } + fn load_keys(v: &T::ValidatorId) -> Option { + >::get(DEDUP_KEY_PREFIX, v) } - fn authorities() -> Vec { - AUTHORITIES.with(|l| l.borrow().to_vec()) + fn take_keys(v: &T::ValidatorId) -> Option { + >::take(DEDUP_KEY_PREFIX, v) } - fn force_new_session() { - FORCE_SESSION_END.with(|l| *l.borrow_mut() = true ) + fn put_keys(v: &T::ValidatorId, keys: &T::Keys) { + >::insert(DEDUP_KEY_PREFIX, v, keys); } - fn set_session_length(x: u64) { - SESSION_LENGTH.with(|l| *l.borrow_mut() = x ) + fn key_owner(id: KeyTypeId, key_data: &[u8]) -> Option { + >::get(DEDUP_KEY_PREFIX, &(id, key_data.to_vec())) } - #[derive(Clone, Eq, PartialEq)] - pub struct Test; - impl system::Trait for Test { - type Origin = Origin; - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; - type Header = Header; - type Event = (); + fn put_key_owner(id: KeyTypeId, key_data: &[u8], v: &T::ValidatorId) { + >::insert(DEDUP_KEY_PREFIX, &(id, key_data.to_vec()), v) } - impl timestamp::Trait for Test { - type Moment = u64; - type OnTimestampSet = (); + + fn clear_key_owner(id: KeyTypeId, key_data: &[u8]) { + >::remove(DEDUP_KEY_PREFIX, &(id, key_data.to_vec())); } - impl Trait for Test { - type ShouldEndSession = TestShouldEndSession; - type OnSessionEnding = TestOnSessionEnding; - type SessionHandler = TestSessionHandler; - type Keys = UintAuthorityId; - type Event = (); +} + +impl OnFreeBalanceZero for Module { + fn on_free_balance_zero(who: &T::ValidatorId) { + Self::prune_dead_keys(who); } +} + +/// Wraps the author-scraping logic for consensus engines that can recover +/// the canonical index of an author. This then transforms it into the +/// registering account-ID of that session key index. +pub struct FindAccountFromAuthorIndex(rstd::marker::PhantomData<(T, Inner)>); + +impl> FindAuthor + for FindAccountFromAuthorIndex +{ + fn find_author<'a, I>(digests: I) -> Option + where I: 'a + IntoIterator + { + let i = Inner::find_author(digests)?; + + let validators = >::validators(); + validators.get(i as usize).map(|k| k.clone()) + } +} - type System = system::Module; - type Session = Module; +#[cfg(test)] +mod tests { + use super::*; + use srml_support::assert_ok; + use runtime_io::with_externalities; + use substrate_primitives::Blake2Hasher; + use 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, + }; fn new_test_ext() -> runtime_io::TestExternalities { - let mut t = system::GenesisConfig::::default().build_storage().unwrap().0; - t.extend(timestamp::GenesisConfig::{ - minimum_period: 5, - }.build_storage().unwrap().0); - t.extend(GenesisConfig::{ - validators: NEXT_VALIDATORS.with(|l| l.borrow().clone()), + let mut t = system::GenesisConfig::default().build_storage::().unwrap(); + GenesisConfig:: { keys: NEXT_VALIDATORS.with(|l| l.borrow().iter().cloned().map(|i| (i, UintAuthorityId(i))).collect() ), - }.build_storage().unwrap().0); - runtime_io::TestExternalities::new(t) + }.assimilate_storage(&mut t.0, &mut t.1).unwrap(); + runtime_io::TestExternalities::new_with_children(t) + } + + fn initialize_block(block: u64) { + SESSION_CHANGED.with(|l| *l.borrow_mut() = false); + System::set_block_number(block); + Session::on_initialize(block); } #[test] @@ -487,32 +594,75 @@ mod tests { }); } + #[test] + fn put_get_keys() { + with_externalities(&mut new_test_ext(), || { + Session::put_keys(&10, &UintAuthorityId(10)); + assert_eq!(Session::load_keys(&10), Some(UintAuthorityId(10))); + }) + } + + #[test] + fn keys_cleared_on_kill() { + let mut ext = new_test_ext(); + with_externalities(&mut ext, || { + assert_eq!(Session::validators(), vec![1, 2, 3]); + assert_eq!(Session::load_keys(&1), Some(UintAuthorityId(1))); + + let id = ::KEY_TYPE; + assert_eq!(Session::key_owner(id, UintAuthorityId(1).get_raw(id)), Some(1)); + + Session::on_free_balance_zero(&1); + assert_eq!(Session::load_keys(&1), None); + assert_eq!(Session::key_owner(id, UintAuthorityId(1).get_raw(id)), None); + + assert!(Changed::get()); + }) + } + #[test] fn authorities_should_track_validators() { with_externalities(&mut new_test_ext(), || { - NEXT_VALIDATORS.with(|v| *v.borrow_mut() = vec![1, 2]); + set_next_validators(vec![1, 2]); force_new_session(); + initialize_block(1); + assert_eq!(Session::queued_keys(), vec![ + (1, UintAuthorityId(1)), + (2, UintAuthorityId(2)), + ]); + assert_eq!(Session::validators(), vec![1, 2, 3]); + assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(3)]); - System::set_block_number(1); - Session::on_initialize(1); + force_new_session(); + initialize_block(2); + assert_eq!(Session::queued_keys(), vec![ + (1, UintAuthorityId(1)), + (2, UintAuthorityId(2)), + ]); assert_eq!(Session::validators(), vec![1, 2]); assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2)]); - NEXT_VALIDATORS.with(|v| *v.borrow_mut() = vec![1, 2, 4]); + set_next_validators(vec![1, 2, 4]); assert_ok!(Session::set_keys(Origin::signed(4), UintAuthorityId(4), vec![])); + force_new_session(); + initialize_block(3); + assert_eq!(Session::queued_keys(), vec![ + (1, UintAuthorityId(1)), + (2, UintAuthorityId(2)), + (4, UintAuthorityId(4)), + ]); + assert_eq!(Session::validators(), vec![1, 2]); + assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2)]); force_new_session(); - System::set_block_number(2); - Session::on_initialize(2); + initialize_block(4); + assert_eq!(Session::queued_keys(), vec![ + (1, UintAuthorityId(1)), + (2, UintAuthorityId(2)), + (4, UintAuthorityId(4)), + ]); assert_eq!(Session::validators(), vec![1, 2, 4]); assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(4)]); - - NEXT_VALIDATORS.with(|v| *v.borrow_mut() = vec![1, 2, 3]); - force_new_session(); - System::set_block_number(3); - Session::on_initialize(3); - assert_eq!(Session::validators(), vec![1, 2, 3]); - assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(3)]); }); } @@ -521,25 +671,20 @@ mod tests { with_externalities(&mut new_test_ext(), || { set_session_length(10); - System::set_block_number(1); - Session::on_initialize(1); + initialize_block(1); assert_eq!(Session::current_index(), 0); - System::set_block_number(2); - Session::on_initialize(2); + initialize_block(2); assert_eq!(Session::current_index(), 0); - force_new_session(); - System::set_block_number(3); - Session::on_initialize(3); + force_new_session(); + initialize_block(3); assert_eq!(Session::current_index(), 1); - System::set_block_number(9); - Session::on_initialize(9); + initialize_block(9); assert_eq!(Session::current_index(), 1); - System::set_block_number(10); - Session::on_initialize(10); + initialize_block(10); assert_eq!(Session::current_index(), 2); }); } @@ -548,25 +693,108 @@ mod tests { fn session_change_should_work() { with_externalities(&mut new_test_ext(), || { // Block 1: No change - System::set_block_number(1); - Session::on_initialize(1); + initialize_block(1); assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(3)]); // Block 2: Session rollover, but no change. - System::set_block_number(2); - Session::on_initialize(2); + initialize_block(2); assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(3)]); // Block 3: Set new key for validator 2; no visible change. - System::set_block_number(3); - Session::on_initialize(3); + initialize_block(3); assert_ok!(Session::set_keys(Origin::signed(2), UintAuthorityId(5), vec![])); assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(3)]); - // Block 4: Session rollover, authority 2 changes. - System::set_block_number(4); - Session::on_initialize(4); + // Block 4: Session rollover; no visible change. + initialize_block(4); + assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(3)]); + + // Block 5: No change. + initialize_block(5); + assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(3)]); + + // Block 6: Session rollover; authority 2 changes. + initialize_block(6); assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(5), UintAuthorityId(3)]); }); } + + #[test] + fn duplicates_are_not_allowed() { + with_externalities(&mut new_test_ext(), || { + System::set_block_number(1); + Session::on_initialize(1); + assert!(Session::set_keys(Origin::signed(4), UintAuthorityId(1), vec![]).is_err()); + assert!(Session::set_keys(Origin::signed(1), UintAuthorityId(10), vec![]).is_ok()); + + // is fine now that 1 has migrated off. + assert!(Session::set_keys(Origin::signed(4), UintAuthorityId(1), vec![]).is_ok()); + }); + } + + #[test] + fn session_changed_flag_works() { + with_externalities(&mut new_test_ext(), || { + TEST_SESSION_CHANGED.with(|l| *l.borrow_mut() = true); + + force_new_session(); + initialize_block(1); + assert!(!session_changed()); + + force_new_session(); + initialize_block(2); + assert!(!session_changed()); + + Session::disable_index(0); + force_new_session(); + initialize_block(3); + assert!(!session_changed()); + + force_new_session(); + initialize_block(4); + assert!(session_changed()); + + force_new_session(); + initialize_block(5); + assert!(!session_changed()); + + assert_ok!(Session::set_keys(Origin::signed(2), UintAuthorityId(5), vec![])); + force_new_session(); + initialize_block(6); + assert!(!session_changed()); + + force_new_session(); + initialize_block(7); + assert!(session_changed()); + }); + } + + #[test] + fn periodic_session_works() { + struct Period; + struct Offset; + + impl Get for Period { + fn get() -> u64 { 10 } + } + + impl Get for Offset { + fn get() -> u64 { 3 } + } + + + type P = PeriodicSessions; + + for i in 0..3 { + assert!(!P::should_end_session(i)); + } + + assert!(P::should_end_session(3)); + + for i in (1..10).map(|i| 3 + i) { + assert!(!P::should_end_session(i)); + } + + assert!(P::should_end_session(13)); + } } diff --git a/srml/session/src/mock.rs b/srml/session/src/mock.rs new file mode 100644 index 0000000000000000000000000000000000000000..b5cb7e4278fc75678ef147f6374f33c3761a9590 --- /dev/null +++ b/srml/session/src/mock.rs @@ -0,0 +1,163 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Mock helpers for Session. + +use super::*; +use std::cell::RefCell; +use srml_support::{impl_outer_origin, parameter_types}; +use substrate_primitives::H256; +use primitives::{ + traits::{BlakeTwo256, IdentityLookup, ConvertInto}, + testing::{Header, UintAuthorityId} +}; + + +impl_outer_origin! { + pub enum Origin for Test {} +} + +thread_local! { + pub static NEXT_VALIDATORS: RefCell> = RefCell::new(vec![1, 2, 3]); + pub static AUTHORITIES: RefCell> = + RefCell::new(vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(3)]); + pub static FORCE_SESSION_END: RefCell = RefCell::new(false); + pub static SESSION_LENGTH: RefCell = RefCell::new(2); + pub static SESSION_CHANGED: RefCell = RefCell::new(false); + pub static TEST_SESSION_CHANGED: RefCell = RefCell::new(false); +} + +pub struct TestShouldEndSession; +impl ShouldEndSession for TestShouldEndSession { + fn should_end_session(now: u64) -> bool { + let l = SESSION_LENGTH.with(|l| *l.borrow()); + now % l == 0 || FORCE_SESSION_END.with(|l| { let r = *l.borrow(); *l.borrow_mut() = false; r }) + } +} + +pub struct TestSessionHandler; +impl SessionHandler for TestSessionHandler { + fn on_new_session( + changed: bool, + validators: &[(u64, T)], + _queued_validators: &[(u64, T)], + ) { + SESSION_CHANGED.with(|l| *l.borrow_mut() = changed); + AUTHORITIES.with(|l| + *l.borrow_mut() = validators.iter().map(|(_, id)| id.get::(0).unwrap_or_default()).collect() + ); + } + fn on_disabled(_validator_index: usize) {} +} + +pub struct TestOnSessionEnding; +impl OnSessionEnding for TestOnSessionEnding { + fn on_session_ending(_: SessionIndex, _: SessionIndex) -> Option> { + if !TEST_SESSION_CHANGED.with(|l| *l.borrow()) { + Some(NEXT_VALIDATORS.with(|l| l.borrow().clone())) + } else { + None + } + } +} + +#[cfg(feature = "historical")] +impl crate::historical::OnSessionEnding for TestOnSessionEnding { + fn on_session_ending(_: SessionIndex, _: SessionIndex) + -> Option<(Vec, Vec<(u64, u64)>)> + { + if !TEST_SESSION_CHANGED.with(|l| *l.borrow()) { + let last_validators = Session::validators(); + let last_identifications = last_validators.into_iter().map(|v| (v, v)).collect(); + Some((NEXT_VALIDATORS.with(|l| l.borrow().clone()), last_identifications)) + } else { + None + } + } +} + +pub fn authorities() -> Vec { + AUTHORITIES.with(|l| l.borrow().to_vec()) +} + +pub fn force_new_session() { + FORCE_SESSION_END.with(|l| *l.borrow_mut() = true ) +} + +pub fn set_session_length(x: u64) { + SESSION_LENGTH.with(|l| *l.borrow_mut() = x ) +} + +pub fn session_changed() -> bool { + SESSION_CHANGED.with(|l| *l.borrow()) +} + +pub fn set_next_validators(next: Vec) { + NEXT_VALIDATORS.with(|v| *v.borrow_mut() = next); +} + +#[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 MinimumPeriod: u64 = 5; +} +impl system::Trait for Test { + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type WeightMultiplierUpdate = (); + type Event = (); + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; +} +impl timestamp::Trait for Test { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; +} + + +impl Trait for Test { + type ShouldEndSession = TestShouldEndSession; + #[cfg(feature = "historical")] + type OnSessionEnding = crate::historical::NoteHistoricalRoot; + #[cfg(not(feature = "historical"))] + type OnSessionEnding = TestOnSessionEnding; + type SessionHandler = TestSessionHandler; + type ValidatorId = u64; + type ValidatorIdOf = ConvertInto; + type Keys = UintAuthorityId; + type Event = (); + type SelectInitialValidators = (); +} + +#[cfg(feature = "historical")] +impl crate::historical::Trait for Test { + type FullIdentification = u64; + type FullIdentificationOf = primitives::traits::ConvertInto; +} + +pub type System = system::Module; +pub type Session = Module; diff --git a/srml/staking/Cargo.toml b/srml/staking/Cargo.toml index 1b95f30f32350b4135c85860c0d4ebd37dfef1ac..74384495315cc536b150779752c53174c4c97248 100644 --- a/srml/staking/Cargo.toml +++ b/srml/staking/Cargo.toml @@ -7,14 +7,14 @@ edition = "2018" [dependencies] serde = { version = "1.0", optional = true } safe-mix = { version = "1.0", default-features = false} -parity-codec = { version = "3.3", default-features = false, features = ["derive"] } +parity-codec = { version = "4.1.1", default-features = false, features = ["derive"] } substrate-keyring = { path = "../../core/keyring", optional = true } rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false } runtime_io = { package = "sr-io", path = "../../core/sr-io", default-features = false } primitives = { package = "sr-primitives", path = "../../core/sr-primitives", default-features = false } srml-support = { path = "../support", default-features = false } system = { package = "srml-system", path = "../system", default-features = false } -session = { package = "srml-session", path = "../session", default-features = false } +session = { package = "srml-session", path = "../session", default-features = false, features = ["historical"] } [dev-dependencies] substrate-primitives = { path = "../../core/primitives" } @@ -23,8 +23,9 @@ balances = { package = "srml-balances", path = "../balances" } rand = "0.6.5" [features] +equalize = [] bench = [] -default = ["std"] +default = ["std", "equalize"] std = [ "serde", "safe-mix/std", diff --git a/srml/staking/src/benches.rs b/srml/staking/src/benches.rs index e3ee00b9e94802658a05364b1b0b34a1ceee79ff..6e79ee70a47a26dedd201494e5e92adfb8943293 100644 --- a/srml/staking/src/benches.rs +++ b/srml/staking/src/benches.rs @@ -35,7 +35,15 @@ const EDGES: u64 = 2; const TO_ELECT: usize = 100; const STAKE: u64 = 1000; -fn do_phragmen(b: &mut Bencher, num_vals: u64, num_noms: u64, count: usize, votes_per: u64) { +fn do_phragmen( + b: &mut Bencher, + num_vals: u64, + num_noms: u64, + count: usize, + votes_per: u64, + eq_iters: usize, + eq_tolerance: u128, +) { with_externalities(&mut ExtBuilder::default().nominate(false).build(), || { assert!(num_vals > votes_per); let rr = |a, b| rand::thread_rng().gen_range(a as usize, b as usize) as u64; @@ -53,62 +61,135 @@ fn do_phragmen(b: &mut Bencher, num_vals: u64, num_noms: u64, count: usize, vote let mut stashes_to_vote = (1 ..= 2*num_vals) .step_by(2) .map(|ctrl| ctrl + 1) - .collect::>(); + .collect::>(); let votes = (0 .. votes_per) .map(|_| { stashes_to_vote.remove(rr(0, stashes_to_vote.len()) as usize) }) - .collect::>(); + .collect::>(); bond_nominator(acc, STAKE + rr(10, 50), votes); }); b.iter(|| { - let _ = phragmen::elect::( + let r = phragmen::elect::( count, 1_usize, >::enumerate(), >::enumerate(), Staking::slashable_balance_of - ); + ).unwrap(); + + // Do the benchmarking with equalize. + if eq_iters > 0 { + let elected_stashes = r.0; + let assignments = r.1; + + let to_balance = |b: ExtendedBalance| + <::CurrencyToVote as Convert>::convert(b); + let to_votes = |b: Balance| + <::CurrencyToVote as Convert>::convert(b) as ExtendedBalance; + let ratio_of = |b, p| (p as ExtendedBalance).saturating_mul(to_votes(b)) / ACCURACY; + + let assignments_with_stakes = assignments.into_iter().map(|(n, a)|( + n, + Staking::slashable_balance_of(&n), + a.into_iter().map(|(acc, r)| ( + acc.clone(), + r, + to_balance(ratio_of(Staking::slashable_balance_of(&n), r)), + )) + .collect::>>() + )).collect::>)>>(); + + let mut exposures = >::new(); + elected_stashes + .into_iter() + .map(|e| (e, Staking::slashable_balance_of(&e))) + .for_each(|(e, s)| { + let item = Exposure { own: s, total: s, ..Default::default() }; + exposures.insert(e, item); + }); + + for (n, _, assignment) in &assignments_with_stakes { + for (c, _, s) in assignment { + if let Some(expo) = exposures.get_mut(c) { + expo.total = expo.total.saturating_add(*s); + expo.others.push( IndividualExposure { who: n.clone(), value: *s } ); + } + } + } + + let mut assignments_with_votes = assignments_with_stakes.into_iter() + .map(|a| ( + a.0, a.1, + a.2.into_iter() + .map(|e| (e.0, e.1, to_votes(e.2))) + .collect::>() + )) + .collect:: + )>>(); + equalize::(&mut assignments_with_votes, &mut exposures, eq_tolerance, eq_iters); + } }) }) } macro_rules! phragmen_benches { ($($name:ident: $tup:expr,)*) => { - $( + $( #[bench] fn $name(b: &mut Bencher) { - let (v, n, t, e) = $tup; + let (v, n, t, e, eq_iter, eq_tol) = $tup; println!(""); println!( - "++ Benchmark: {} Validators // {} Nominators // {} Edges-per-nominator // {} total edges // electing {}", - v, n, e, e * n, t + r#" +++ Benchmark: {} Validators // {} Nominators // {} Edges-per-nominator // {} total edges // +electing {} // Equalize: {} iterations -- {} tolerance"#, + v, n, e, e * n, t, eq_iter, eq_tol, ); - do_phragmen(b, v, n, t, e); + do_phragmen(b, v, n, t, e, eq_iter, eq_tol); } )* } } phragmen_benches! { - bench_1_1: (VALIDATORS, NOMINATORS, TO_ELECT, EDGES), - bench_1_2: (VALIDATORS*2, NOMINATORS, TO_ELECT, EDGES), - bench_1_3: (VALIDATORS*4, NOMINATORS, TO_ELECT, EDGES), - bench_1_4: (VALIDATORS*8, NOMINATORS, TO_ELECT, EDGES), - - bench_0_1: (VALIDATORS, NOMINATORS, TO_ELECT, EDGES), - bench_0_2: (VALIDATORS, NOMINATORS, TO_ELECT * 4, EDGES), - bench_0_3: (VALIDATORS, NOMINATORS, TO_ELECT * 8, EDGES), - bench_0_4: (VALIDATORS, NOMINATORS, TO_ELECT * 16, EDGES), - - bench_2_1: (VALIDATORS, NOMINATORS, TO_ELECT, EDGES), - bench_2_2: (VALIDATORS, NOMINATORS*2, TO_ELECT, EDGES), - bench_2_3: (VALIDATORS, NOMINATORS*4, TO_ELECT, EDGES), - bench_2_4: (VALIDATORS, NOMINATORS*8, TO_ELECT, EDGES), - - bench_3_1: (VALIDATORS, NOMINATORS, TO_ELECT, EDGES), - bench_3_2: (VALIDATORS, NOMINATORS, TO_ELECT, EDGES*2), - bench_3_3: (VALIDATORS, NOMINATORS, TO_ELECT, EDGES*4), - bench_3_4: (VALIDATORS, NOMINATORS, TO_ELECT, EDGES*8), + bench_1_1: (VALIDATORS, NOMINATORS, TO_ELECT, EDGES, 0, 0), + bench_1_2: (VALIDATORS*2, NOMINATORS, TO_ELECT, EDGES, 0, 0), + bench_1_3: (VALIDATORS*4, NOMINATORS, TO_ELECT, EDGES, 0, 0), + bench_1_4: (VALIDATORS*8, NOMINATORS, TO_ELECT, EDGES, 0, 0), + bench_1_1_eq: (VALIDATORS, NOMINATORS, TO_ELECT, EDGES, 2, 0), + bench_1_2_eq: (VALIDATORS*2, NOMINATORS, TO_ELECT, EDGES, 2, 0), + bench_1_3_eq: (VALIDATORS*4, NOMINATORS, TO_ELECT, EDGES, 2, 0), + bench_1_4_eq: (VALIDATORS*8, NOMINATORS, TO_ELECT, EDGES, 2, 0), + + bench_0_1: (VALIDATORS, NOMINATORS, TO_ELECT, EDGES, 0, 0), + bench_0_2: (VALIDATORS, NOMINATORS, TO_ELECT * 4, EDGES, 0, 0), + bench_0_3: (VALIDATORS, NOMINATORS, TO_ELECT * 8, EDGES, 0, 0), + bench_0_4: (VALIDATORS, NOMINATORS, TO_ELECT * 16, EDGES , 0, 0), + bench_0_1_eq: (VALIDATORS, NOMINATORS, TO_ELECT, EDGES, 2, 0), + bench_0_2_eq: (VALIDATORS, NOMINATORS, TO_ELECT * 4, EDGES, 2, 0), + bench_0_3_eq: (VALIDATORS, NOMINATORS, TO_ELECT * 8, EDGES, 2, 0), + bench_0_4_eq: (VALIDATORS, NOMINATORS, TO_ELECT * 16, EDGES , 2, 0), + + bench_2_1: (VALIDATORS, NOMINATORS, TO_ELECT, EDGES, 0, 0), + bench_2_2: (VALIDATORS, NOMINATORS*2, TO_ELECT, EDGES, 0, 0), + bench_2_3: (VALIDATORS, NOMINATORS*4, TO_ELECT, EDGES, 0, 0), + bench_2_4: (VALIDATORS, NOMINATORS*8, TO_ELECT, EDGES, 0, 0), + bench_2_1_eq: (VALIDATORS, NOMINATORS, TO_ELECT, EDGES, 2, 0), + bench_2_2_eq: (VALIDATORS, NOMINATORS*2, TO_ELECT, EDGES, 2, 0), + bench_2_3_eq: (VALIDATORS, NOMINATORS*4, TO_ELECT, EDGES, 2, 0), + bench_2_4_eq: (VALIDATORS, NOMINATORS*8, TO_ELECT, EDGES, 2, 0), + + bench_3_1: (VALIDATORS, NOMINATORS, TO_ELECT, EDGES, 0, 0 ), + bench_3_2: (VALIDATORS, NOMINATORS, TO_ELECT, EDGES*2, 0, 0), + bench_3_3: (VALIDATORS, NOMINATORS, TO_ELECT, EDGES*4, 0, 0), + bench_3_4: (VALIDATORS, NOMINATORS, TO_ELECT, EDGES*8, 0, 0), + bench_3_1_eq: (VALIDATORS, NOMINATORS, TO_ELECT, EDGES, 2, 0), + bench_3_2_eq: (VALIDATORS, NOMINATORS, TO_ELECT, EDGES*2, 2, 0), + bench_3_3_eq: (VALIDATORS, NOMINATORS, TO_ELECT, EDGES*4, 2, 0), + bench_3_4_eq: (VALIDATORS, NOMINATORS, TO_ELECT, EDGES*8, 2, 0), } diff --git a/srml/staking/src/lib.rs b/srml/staking/src/lib.rs index c1b42a8b3bcc05f6036afe7a08b6a1e37ce666d6..c72aa36e8bc8cb6845c8eb04570bb234cf2bc0d7 100644 --- a/srml/staking/src/lib.rs +++ b/srml/staking/src/lib.rs @@ -278,19 +278,19 @@ use srml_support::{ StorageValue, StorageMap, EnumerableStorageMap, decl_module, decl_event, decl_storage, ensure, traits::{ Currency, OnFreeBalanceZero, OnDilution, LockIdentifier, LockableCurrency, - WithdrawReasons, OnUnbalanced, Imbalance, Get + WithdrawReasons, WithdrawReason, OnUnbalanced, Imbalance, Get } }; -use session::{OnSessionEnding, SessionIndex}; +use session::{historical::OnSessionEnding, SelectInitialValidators, SessionIndex}; use primitives::Perbill; use primitives::traits::{ - Convert, Zero, One, StaticLookup, CheckedSub, CheckedShl, Saturating, Bounded + Convert, Zero, One, StaticLookup, CheckedSub, CheckedShl, Saturating, Bounded, }; #[cfg(feature = "std")] use primitives::{Serialize, Deserialize}; -use system::ensure_signed; +use system::{ensure_signed, ensure_root}; -use phragmen::{elect, ACCURACY, ExtendedBalance}; +use phragmen::{elect, ACCURACY, ExtendedBalance, equalize}; const RECENT_OFFLINE_COUNT: usize = 32; const DEFAULT_MINIMUM_VALIDATOR_COUNT: u32 = 4; @@ -429,7 +429,7 @@ pub struct Exposure { pub others: Vec>, } -type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; +pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; type PositiveImbalanceOf = <::Currency as Currency<::AccountId>>::PositiveImbalance; type NegativeImbalanceOf = @@ -442,7 +442,46 @@ type ExpoMap = BTreeMap< Exposure<::AccountId, BalanceOf> >; -pub trait Trait: system::Trait + session::Trait { +pub const DEFAULT_SESSIONS_PER_ERA: u32 = 3; +pub const DEFAULT_BONDING_DURATION: u32 = 1; + +/// Means for interacting with a specialized version of the `session` trait. +/// +/// This is needed because `Staking` sets the `ValidatorIdOf` of the `session::Trait` +pub trait SessionInterface: system::Trait { + /// Disable a given validator by stash ID. + fn disable_validator(validator: &AccountId) -> Result<(), ()>; + /// Get the validators from session. + fn validators() -> Vec; + /// Prune historical session tries up to but not including the given index. + fn prune_historical_up_to(up_to: session::SessionIndex); +} + +impl SessionInterface<::AccountId> for T where + T: session::Trait::AccountId>, + T: session::historical::Trait< + FullIdentification = Exposure<::AccountId, BalanceOf>, + FullIdentificationOf = ExposureOf, + >, + T::SessionHandler: session::SessionHandler<::AccountId>, + T::OnSessionEnding: session::OnSessionEnding<::AccountId>, + T::SelectInitialValidators: session::SelectInitialValidators<::AccountId>, + T::ValidatorIdOf: Convert<::AccountId, Option<::AccountId>> +{ + fn disable_validator(validator: &::AccountId) -> Result<(), ()> { + >::disable(validator) + } + + fn validators() -> Vec<::AccountId> { + >::validators() + } + + fn prune_historical_up_to(up_to: session::SessionIndex) { + >::prune_up_to(up_to); + } +} + +pub trait Trait: system::Trait { /// The staking balance. type Currency: LockableCurrency; @@ -470,6 +509,9 @@ pub trait Trait: system::Trait + session::Trait { /// Number of eras that staked funds must remain bonded for. type BondingDuration: Get; + + /// Interface for interacting with a session module. + type SessionInterface: self::SessionInterface; } decl_storage! { @@ -513,15 +555,6 @@ decl_storage! { /// This is keyed by the stash account. pub Stakers get(stakers): map T::AccountId => Exposure>; - // The historical validators and their nominations for a given era. Stored as a trie root - // of the mapping `T::AccountId` => `Exposure>`, which is just - // the contents of `Stakers`, under a key that is the `era`. - // - // Every era change, this will be appended with the trie root of the contents of `Stakers`, - // and the oldest entry removed down to a specific number of entries (probably around 90 for - // a 3 month history). - // pub HistoricalStakers get(historical_stakers): map T::BlockNumber => Option; - /// The currently elected validator set keyed by stash account ID. pub CurrentElected get(current_elected): Vec; @@ -552,6 +585,9 @@ decl_storage! { /// True if the next session change will be a new era regardless of index. pub ForceNewEra get(forcing_new_era): bool; + + /// A mapping from still-bonded eras to the first session index of that era. + BondedEras: Vec<(EraIndex, SessionIndex)>; } add_extra_genesis { config(stakers): @@ -563,7 +599,10 @@ decl_storage! { | { with_storage(storage, || { for &(ref stash, ref controller, balance, ref status) in &config.stakers { - assert!(T::Currency::free_balance(&stash) >= balance); + assert!( + T::Currency::free_balance(&stash) >= balance, + "Stash does not have enough balance to bond." + ); let _ = >::bond( T::Origin::from(Some(stash.clone()).into()), T::Lookup::unlookup(controller.clone()), @@ -584,10 +623,6 @@ decl_storage! { }, _ => Ok(()) }; } - - if let (_, Some(validators)) = >::select_validators() { - >::put(&validators); - } }); }); } @@ -607,10 +642,18 @@ decl_event!( decl_module! { pub struct Module for enum Call where origin: T::Origin { + /// Number of sessions per era. + const SessionsPerEra: SessionIndex = T::SessionsPerEra::get(); + + /// Number of eras that staked funds must remain bonded for. + const BondingDuration: EraIndex = T::BondingDuration::get(); + fn deposit_event() = default; /// Take the origin account as a stash and lock up `value` of its balance. `controller` will - /// be the account that controls it. + /// be the account that controls it. + /// + /// `value` must be more than the `existential_deposit` defined in the Balances module. /// /// The dispatch origin for this call must be _Signed_ by the stash account. /// @@ -621,10 +664,6 @@ decl_module! { /// /// NOTE: Two of the storage writes (`Self::bonded`, `Self::payee`) are _never_ cleaned unless /// the `origin` falls below _existential deposit_ and gets removed as dust. - /// - /// NOTE: At the moment, there are no financial restrictions to bond - /// (which creates a bunch of storage items for an account). In essence, nothing prevents many accounts from - /// spamming `Staking` storage by bonding 1 UNIT. See test case: `bond_with_no_staked_value`. /// # fn bond(origin, controller: ::Source, @@ -643,6 +682,11 @@ decl_module! { return Err("controller already paired") } + // reject a bond which is considered to be _dust_. + if value < T::Currency::minimum_balance() { + return Err("can not bond with value less than minimum balance") + } + // You're auto-bonded forever, here. We might improve this by only bonding when // you actually validate/nominate and remove once you unbond __everything__. >::insert(&stash, controller.clone()); @@ -655,9 +699,11 @@ decl_module! { } /// Add some extra amount that have appeared in the stash `free_balance` into the balance up - /// for staking. + /// for staking. /// /// Use this if there are additional funds in your stash account that you wish to bond. + /// Unlike [`bond`] or [`unbond`] this function does not impose any limitation on the amount + /// that can be added. /// /// The dispatch origin for this call must be _Signed_ by the stash, not the controller. /// @@ -740,9 +786,9 @@ decl_module! { /// See also [`Call::unbond`]. /// /// # - /// - Could be dependent on the `origin` argument and how much `unlocking` chunks exist. It implies - /// `consolidate_unlocked` which loops over `Ledger.unlocking`, which is indirectly - /// user-controlled. See [`unbond`] for more detail. + /// - Could be dependent on the `origin` argument and how much `unlocking` chunks exist. + /// It implies `consolidate_unlocked` which loops over `Ledger.unlocking`, which is + /// indirectly user-controlled. See [`unbond`] for more detail. /// - Contains a limited number of reads, yet the size of which could be large based on `ledger`. /// - Writes are limited to the `origin` account key. /// # @@ -750,7 +796,20 @@ decl_module! { let controller = ensure_signed(origin)?; let ledger = Self::ledger(&controller).ok_or("not a controller")?; let ledger = ledger.consolidate_unlocked(Self::current_era()); - Self::update_ledger(&controller, &ledger); + + if ledger.unlocking.is_empty() && ledger.active.is_zero() { + // This account must have called `unbond()` with some value that caused the active + // portion to fall below existential deposit + will have no more unlocking chunks + // left. We can now safely remove this. + let stash = ledger.stash; + // remove the lock. + T::Currency::remove_lock(STAKING_ID, &stash); + // remove all staking-related information. + Self::kill_stash(&stash); + } else { + // This was the consequence of a partial unbond. just update the ledger and move on. + Self::update_ledger(&controller, &ledger); + } } /// Declare the desire to validate for the origin controller. @@ -865,8 +924,9 @@ decl_module! { } /// The ideal number of validators. - fn set_validator_count(#[compact] new: u32) { - >::put(new); + fn set_validator_count(origin, #[compact] new: u32) { + ensure_root(origin)?; + ValidatorCount::put(new); } // ----- Root calls. @@ -879,17 +939,20 @@ decl_module! { /// - Triggers the Phragmen election. Expensive but not user-controlled. /// - Depends on state: `O(|edges| * |validators|)`. /// # - fn force_new_era() { + fn force_new_era(origin) { + ensure_root(origin)?; Self::apply_force_new_era() } /// Set the offline slash grace period. - fn set_offline_slash_grace(#[compact] new: u32) { - >::put(new); + fn set_offline_slash_grace(origin, #[compact] new: u32) { + ensure_root(origin)?; + OfflineSlashGrace::put(new); } /// Set the validators who cannot be slashed (if any). - fn set_invulnerables(validators: Vec) { + fn set_invulnerables(origin, validators: Vec) { + ensure_root(origin)?; >::put(validators); } } @@ -906,7 +969,8 @@ impl Module { // MUTABLES (DANGEROUS) - /// Update the ledger for a controller. This will also update the stash lock. + /// Update the ledger for a controller. This will also update the stash lock. The lock will + /// will lock the entire funds except paying for further transactions. fn update_ledger( controller: &T::AccountId, ledger: &StakingLedger> @@ -916,7 +980,7 @@ impl Module { &ledger.stash, ledger.total, T::BlockNumber::max_value(), - WithdrawReasons::all() + WithdrawReasons::except(WithdrawReason::TransactionPayment), ); >::insert(controller, ledger); } @@ -998,14 +1062,22 @@ impl Module { T::Reward::on_unbalanced(imbalance); } - /// Session has just ended. Provide the validator set for the next session if it's an era-end. - fn new_session(session_index: SessionIndex) -> Option> { + /// Session has just ended. Provide the validator set for the next session if it's an era-end, along + /// with the exposure of the prior validator set. + fn new_session(session_index: SessionIndex) + -> Option<(Vec, Vec<(T::AccountId, Exposure>)>)> + { // accumulate good session reward let reward = Self::current_session_reward(); >::mutate(|r| *r += reward); - if >::take() || session_index % T::SessionsPerEra::get() == 0 { - Self::new_era() + if ForceNewEra::take() || session_index % T::SessionsPerEra::get() == 0 { + let validators = T::SessionInterface::validators(); + let prior = validators.into_iter() + .map(|v| { let e = Self::stakers(&v); (v, e) }) + .collect(); + + Self::new_era(session_index).map(move |new| (new, prior)) } else { None } @@ -1015,7 +1087,7 @@ impl Module { /// /// NOTE: This always happens immediately before a session change to ensure that new validators /// get a chance to set their session keys. - fn new_era() -> Option> { + fn new_era(start_session_index: SessionIndex) -> Option> { // Payout let reward = >::take(); if !reward.is_zero() { @@ -1032,7 +1104,26 @@ impl Module { } // Increment current era. - >::mutate(|s| *s += 1); + let current_era = CurrentEra::mutate(|s| { *s += 1; *s }); + let bonding_duration = T::BondingDuration::get(); + + if current_era > bonding_duration { + let first_kept = current_era - bonding_duration; + BondedEras::mutate(|bonded| { + bonded.push((current_era, start_session_index)); + + // prune out everything that's from before the first-kept index. + let n_to_prune = bonded.iter() + .take_while(|&&(era_idx, _)| era_idx < first_kept) + .count(); + + bonded.drain(..n_to_prune); + + if let Some(&(_, first_session)) = bonded.first() { + T::SessionInterface::prune_historical_up_to(first_session); + } + }) + } // Reassign all Stakers. let (slot_stake, maybe_new_validators) = Self::select_validators(); @@ -1049,7 +1140,7 @@ impl Module { /// Select a new validator set from the assembled stakers and their role preferences. /// - /// Returns the new `SlotStake` value. + /// Returns the new `SlotStake` value and a set of newly selected _stash_ IDs. fn select_validators() -> (BalanceOf, Option>) { let maybe_elected_set = elect::( Self::validator_count() as usize, @@ -1077,7 +1168,7 @@ impl Module { let ratio_of = |b, p| (p as ExtendedBalance).saturating_mul(to_votes(b)) / ACCURACY; // Compute the actual stake from nominator's ratio. - let mut assignments_with_stakes = assignments.iter().map(|(n, a)|( + let assignments_with_stakes = assignments.iter().map(|(n, a)|( n.clone(), Self::slashable_balance_of(n), a.iter().map(|(acc, r)| ( @@ -1111,17 +1202,22 @@ impl Module { } } - // This optimization will most likely be only applied off-chain. - let do_equalize = false; - if do_equalize { - let tolerance = 10 as u128; - let iterations = 10 as usize; - phragmen::equalize::( - &mut assignments_with_stakes, - &mut exposures, - tolerance, - iterations - ); + if cfg!(feature = "equalize") { + let tolerance = 0_u128; + let iterations = 2_usize; + let mut assignments_with_votes = assignments_with_stakes.iter() + .map(|a| ( + a.0.clone(), a.1, + a.2.iter() + .map(|e| (e.0.clone(), e.1, to_votes(e.2))) + .collect::>() + )) + .collect::, + Vec<(T::AccountId, ExtendedBalance, ExtendedBalance)> + )>>(); + equalize::(&mut assignments_with_votes, &mut exposures, tolerance, iterations); } // Clear Stakers and reduce their slash_count. @@ -1141,14 +1237,14 @@ impl Module { } >::insert(c.clone(), e.clone()); } + + // Update slot stake. >::put(&slot_stake); - // Set the new validator set. + // Set the new validator set in sessions. >::put(&elected_stashes); - let validators = elected_stashes.into_iter() - .map(|s| Self::bonded(s).unwrap_or_default()) - .collect::>(); - (slot_stake, Some(validators)) + + (slot_stake, Some(elected_stashes)) } else { // There were not enough candidates for even our minimal level of functionality. // This is bad. @@ -1161,7 +1257,22 @@ impl Module { } fn apply_force_new_era() { - >::put(true); + ForceNewEra::put(true); + } + + /// Remove all associated data of a stash account from the staking system. + /// + /// This is called : + /// - Immediately when an account's balance falls below existential deposit. + /// - after a `withdraw_unbond()` call that frees all of a stash's bonded balance. + fn kill_stash(stash: &T::AccountId) { + if let Some(controller) = >::take(stash) { + >::remove(&controller); + } + >::remove(stash); + >::remove(stash); + >::remove(stash); + >::remove(stash); } /// Call when a validator is determined to be offline. `count` is the @@ -1210,7 +1321,7 @@ impl Module { .map(|x| x.min(slash_exposure)) .unwrap_or(slash_exposure); let _ = Self::slash_validator(&stash, slash); - let _ = >::disable(&controller); + let _ = T::SessionInterface::disable_validator(&stash); RawEvent::OfflineSlash(stash.clone(), slash) } else { @@ -1222,20 +1333,50 @@ impl Module { } } -impl OnSessionEnding for Module { - fn on_session_ending(i: SessionIndex) -> Option> { - Self::new_session(i + 1) +impl session::OnSessionEnding for Module { + fn on_session_ending(_ending: SessionIndex, start_session: SessionIndex) -> Option> { + Self::new_session(start_session - 1).map(|(new, _old)| new) + } +} + +impl OnSessionEnding>> for Module { + fn on_session_ending(_ending: SessionIndex, start_session: SessionIndex) + -> Option<(Vec, Vec<(T::AccountId, Exposure>)>)> + { + Self::new_session(start_session - 1) } } impl OnFreeBalanceZero for Module { fn on_free_balance_zero(stash: &T::AccountId) { - if let Some(controller) = >::take(stash) { - >::remove(&controller); - } - >::remove(stash); - >::remove(stash); - >::remove(stash); - >::remove(stash); + Self::kill_stash(stash); + } +} + +/// A `Convert` implementation that finds the stash of the given controller account, +/// if any. +pub struct StashOf(rstd::marker::PhantomData); + +impl Convert> for StashOf { + fn convert(controller: T::AccountId) -> Option { + >::ledger(&controller).map(|l| l.stash) + } +} + +/// A typed conversion from stash account ID to the current exposure of nominators +/// on that account. +pub struct ExposureOf(rstd::marker::PhantomData); + +impl Convert>>> + for ExposureOf +{ + fn convert(validator: T::AccountId) -> Option>> { + Some(>::stakers(&validator)) + } +} + +impl SelectInitialValidators for Module { + fn select_initial_validators() -> Option> { + >::select_validators().1 } } diff --git a/srml/staking/src/mock.rs b/srml/staking/src/mock.rs index ea4fcbd8f9f012995875ec0a58baf3420fd73655..f0151bf9154998fb0b8cdf8f2a3d8d7a76ae80f2 100644 --- a/srml/staking/src/mock.rs +++ b/srml/staking/src/mock.rs @@ -17,17 +17,21 @@ //! Test utilities use std::{collections::HashSet, cell::RefCell}; -use primitives::{BuildStorage, Perbill}; +use primitives::Perbill; use primitives::traits::{IdentityLookup, Convert, OpaqueKeys, OnInitialize}; use primitives::testing::{Header, UintAuthorityId}; use substrate_primitives::{H256, Blake2Hasher}; use runtime_io; -use srml_support::{impl_outer_origin, parameter_types, assert_ok, traits::Currency}; -use crate::{EraIndex, GenesisConfig, Module, Trait, StakerStatus, ValidatorPrefs, RewardDestination}; +use srml_support::{assert_ok, impl_outer_origin, parameter_types, EnumerableStorageMap}; +use srml_support::traits::{Currency, Get}; +use crate::{EraIndex, GenesisConfig, Module, Trait, StakerStatus, + ValidatorPrefs, RewardDestination, Nominators +}; /// The AccountId alias in this test module. pub type AccountId = u64; pub type BlockNumber = u64; +pub type Balance = u64; /// Simple structure that exposes how u64 currency can be represented as... u64. pub struct CurrencyToVoteHandler; @@ -42,11 +46,16 @@ impl Convert for CurrencyToVoteHandler { thread_local! { static SESSION: RefCell<(Vec, HashSet)> = RefCell::new(Default::default()); + static EXISTENTIAL_DEPOSIT: RefCell = RefCell::new(0); } pub struct TestSessionHandler; impl session::SessionHandler for TestSessionHandler { - fn on_new_session(_changed: bool, validators: &[(AccountId, Ks)]) { + fn on_new_session( + _changed: bool, + validators: &[(AccountId, Ks)], + _queued_validators: &[(AccountId, Ks)], + ) { SESSION.with(|x| *x.borrow_mut() = (validators.iter().map(|x| x.0.clone()).collect(), HashSet::new()) ); @@ -56,14 +65,21 @@ impl session::SessionHandler for TestSessionHandler { SESSION.with(|d| { let mut d = d.borrow_mut(); let value = d.0[validator_index]; - println!("on_disabled {} -> {}", validator_index, value); d.1.insert(value); }) } } pub fn is_disabled(validator: AccountId) -> bool { - SESSION.with(|d| d.borrow().1.contains(&validator)) + let stash = Staking::ledger(&validator).unwrap().stash; + SESSION.with(|d| d.borrow().1.contains(&stash)) +} + +pub struct ExistentialDeposit; +impl Get for ExistentialDeposit { + fn get() -> u64 { + EXISTENTIAL_DEPOSIT.with(|v| *v.borrow()) + } } impl_outer_origin!{ @@ -73,6 +89,11 @@ impl_outer_origin!{ // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. #[derive(Clone, PartialEq, Eq, Debug)] pub struct Test; +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; +} impl system::Trait for Test { type Origin = Origin; type Index = u64; @@ -82,31 +103,59 @@ impl system::Trait for Test { type AccountId = AccountId; type Lookup = IdentityLookup; type Header = Header; + type WeightMultiplierUpdate = (); type Event = (); + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; +} +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 = u64; + 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; } parameter_types! { pub const Period: BlockNumber = 1; pub const Offset: BlockNumber = 0; } impl session::Trait for Test { - type OnSessionEnding = Staking; + type OnSessionEnding = session::historical::NoteHistoricalRoot; type Keys = UintAuthorityId; type ShouldEndSession = session::PeriodicSessions; type SessionHandler = TestSessionHandler; type Event = (); + type ValidatorId = AccountId; + type ValidatorIdOf = crate::StashOf; + type SelectInitialValidators = Staking; +} + +impl session::historical::Trait for Test { + type FullIdentification = crate::Exposure; + type FullIdentificationOf = crate::ExposureOf; +} + +parameter_types! { + pub const MinimumPeriod: u64 = 5; } impl timestamp::Trait for Test { type Moment = u64; type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; } parameter_types! { pub const SessionsPerEra: session::SessionIndex = 3; @@ -121,30 +170,31 @@ impl Trait for Test { type Reward = (); type SessionsPerEra = SessionsPerEra; type BondingDuration = BondingDuration; + type SessionInterface = Self; } pub struct ExtBuilder { existential_deposit: u64, - current_era: EraIndex, reward: u64, validator_pool: bool, nominate: bool, validator_count: u32, minimum_validator_count: u32, fair: bool, + num_validators: Option, } impl Default for ExtBuilder { fn default() -> Self { Self { existential_deposit: 0, - current_era: 0, reward: 10, validator_pool: false, nominate: true, validator_count: 2, minimum_validator_count: 0, - fair: true + fair: true, + num_validators: None, } } } @@ -154,10 +204,6 @@ impl ExtBuilder { self.existential_deposit = existential_deposit; self } - pub fn _current_era(mut self, current_era: EraIndex) -> Self { - self.current_era = current_era; - self - } pub fn validator_pool(mut self, validator_pool: bool) -> Self { self.validator_pool = validator_pool; self @@ -178,19 +224,27 @@ impl ExtBuilder { self.fair = is_fair; self } + pub fn num_validators(mut self, num_validators: u32) -> Self { + self.num_validators = Some(num_validators); + self + } + pub fn set_associated_consts(&self) { + EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); + } pub fn build(self) -> runtime_io::TestExternalities { - let (mut t, mut c) = system::GenesisConfig::::default().build_storage().unwrap(); + self.set_associated_consts(); + let (mut t, mut c) = system::GenesisConfig::default().build_storage::().unwrap(); let balance_factor = if self.existential_deposit > 0 { 256 } else { 1 }; - let validators = if self.validator_pool { vec![10, 20, 30, 40] } else { vec![10, 20] }; - let _ = session::GenesisConfig::{ - // NOTE: if config.nominate == false then 100 is also selected in the initial round. - validators, - keys: vec![], - }.assimilate_storage(&mut t, &mut c); + + let num_validators = self.num_validators.unwrap_or(self.validator_count); + let validators = (0..num_validators) + .map(|x| ((x + 1) * 10 + 1) as u64) + .collect::>(); + let _ = balances::GenesisConfig::{ balances: vec![ (1, 10 * balance_factor), @@ -208,13 +262,9 @@ impl ExtBuilder { (100, 2000 * balance_factor), (101, 2000 * balance_factor), ], - transaction_base_fee: 0, - transaction_byte_fee: 0, - existential_deposit: self.existential_deposit, - transfer_fee: 0, - creation_fee: 0, vesting: vec![], }.assimilate_storage(&mut t, &mut c); + let stake_21 = if self.fair { 1000 } else { 2000 }; let stake_31 = if self.validator_pool { balance_factor * 1000 } else { 1 }; let status_41 = if self.validator_pool { @@ -224,7 +274,7 @@ impl ExtBuilder { }; let nominated = if self.nominate { vec![11, 21] } else { vec![] }; let _ = GenesisConfig::{ - current_era: self.current_era, + current_era: 0, stakers: vec![ (11, 10, balance_factor * 1000, StakerStatus::::Validator), (21, 20, stake_21, StakerStatus::::Validator), @@ -241,9 +291,11 @@ impl ExtBuilder { offline_slash_grace: 0, invulnerables: vec![], }.assimilate_storage(&mut t, &mut c); - let _ = timestamp::GenesisConfig::{ - minimum_period: 5, + + let _ = session::GenesisConfig:: { + keys: validators.iter().map(|x| (*x, UintAuthorityId(*x))).collect(), }.assimilate_storage(&mut t, &mut c); + let mut ext = t.into(); runtime_io::with_externalities(&mut ext, || { let validators = Session::validators(); @@ -261,20 +313,52 @@ pub type Session = session::Module; pub type Timestamp = timestamp::Module; pub type Staking = Module; -pub fn check_exposure(acc: u64) { - let expo = Staking::stakers(&acc); - assert_eq!(expo.total as u128, expo.own as u128 + expo.others.iter().map(|e| e.value as u128).sum::()); -} - pub fn check_exposure_all() { Staking::current_elected().into_iter().for_each(|acc| check_exposure(acc)); } -pub fn assert_total_expo(acc: u64, val: u64) { - let expo = Staking::stakers(&acc); +pub fn check_nominator_all() { + >::enumerate().for_each(|(acc, _)| check_nominator_exposure(acc)); +} + +/// Check for each selected validator: expo.total = Sum(expo.other) + expo.own +pub fn check_exposure(stash: u64) { + assert_is_stash(stash); + let expo = Staking::stakers(&stash); + assert_eq!( + expo.total as u128, expo.own as u128 + expo.others.iter().map(|e| e.value as u128).sum::(), + "wrong total exposure for {:?}: {:?}", stash, expo, + ); +} + +/// Check that for each nominator: slashable_balance > sum(used_balance) +/// Note: we might not consume all of a nominator's balance, but we MUST NOT over spend it. +pub fn check_nominator_exposure(stash: u64) { + assert_is_stash(stash); + let mut sum = 0; + Staking::current_elected() + .iter() + .map(|v| Staking::stakers(v)) + .for_each(|e| e.others.iter() + .filter(|i| i.who == stash) + .for_each(|i| sum += i.value)); + let nominator_stake = Staking::slashable_balance_of(&stash); + // a nominator cannot over-spend. + assert!( + nominator_stake >= sum, + "failed: Nominator({}) stake({}) >= sum divided({})", stash, nominator_stake, sum, + ); +} + +pub fn assert_total_expo(stash: u64, val: u64) { + let expo = Staking::stakers(&stash); assert_eq!(expo.total, val); } +pub fn assert_is_stash(acc: u64) { + assert!(Staking::bonded(&acc).is_some(), "Not a stash."); +} + pub fn bond_validator(acc: u64, val: u64) { // a = controller // a + 1 = stash @@ -292,10 +376,13 @@ pub fn bond_nominator(acc: u64, val: u64, target: Vec) { } pub fn start_session(session_index: session::SessionIndex) { + // Compensate for session delay + let session_index = session_index + 1; for i in 0..(session_index - Session::current_index()) { System::set_block_number((i + 1).into()); Session::on_initialize(System::block_number()); } + assert_eq!(Session::current_index(), session_index); } @@ -303,3 +390,7 @@ pub fn start_era(era_index: EraIndex) { start_session((era_index * 3).into()); assert_eq!(Staking::current_era(), era_index); } + +pub fn validator_controllers() -> Vec { + Session::validators().into_iter().map(|s| Staking::bonded(&s).expect("no controller for validator")).collect() +} diff --git a/srml/staking/src/phragmen.rs b/srml/staking/src/phragmen.rs index 50f63323bb3fb043e24f6adaa2f1ad2ea1f01507..39480bf689e7b60a3f672d4c41bbf1e656b6a5a2 100644 --- a/srml/staking/src/phragmen.rs +++ b/srml/staking/src/phragmen.rs @@ -19,7 +19,7 @@ use rstd::{prelude::*, collections::btree_map::BTreeMap}; use primitives::{PerU128}; use primitives::traits::{Zero, Convert, Saturating}; -use crate::{BalanceOf, Assignment, RawAssignment, ExpoMap, Trait, ValidatorPrefs}; +use crate::{BalanceOf, RawAssignment, ExpoMap, Trait, ValidatorPrefs, IndividualExposure}; type Fraction = PerU128; /// Wrapper around the type used as the _safe_ wrapper around a `balance`. @@ -275,7 +275,7 @@ pub fn elect( /// /// No value is returned from the function and the `expo_map` parameter is updated. pub fn equalize( - assignments: &mut Vec<(T::AccountId, BalanceOf, Vec>)>, + assignments: &mut Vec<(T::AccountId, BalanceOf, Vec<(T::AccountId, ExtendedBalance, ExtendedBalance)>)>, expo_map: &mut ExpoMap, tolerance: ExtendedBalance, iterations: usize, @@ -297,19 +297,19 @@ pub fn equalize( fn do_equalize( nominator: &T::AccountId, budget_balance: BalanceOf, - elected_edges_balance: &mut Vec>, + elected_edges: &mut Vec<(T::AccountId, ExtendedBalance, ExtendedBalance)>, expo_map: &mut ExpoMap, tolerance: ExtendedBalance ) -> ExtendedBalance { - let to_votes = |b: BalanceOf| , u64>>::convert(b) as ExtendedBalance; - let to_balance = |v: ExtendedBalance| >>::convert(v); + let to_votes = |b: BalanceOf| + , u64>>::convert(b) as ExtendedBalance; + let to_balance = |v: ExtendedBalance| + >>::convert(v); let budget = to_votes(budget_balance); - // Convert all stakes to extended. Result is Vec<(Acc, Ratio, Balance)> - let mut elected_edges = elected_edges_balance - .into_iter() - .map(|e| (e.0.clone(), e.1, to_votes(e.2))) - .collect::>(); + // Nothing to do. This nominator had nothing useful. + // Defensive only. Assignment list should always be populated. + if elected_edges.is_empty() { return 0; } let stake_used = elected_edges .iter() @@ -350,18 +350,20 @@ fn do_equalize( elected_edges.iter_mut().for_each(|e| { if let Some(expo) = expo_map.get_mut(&e.0) { expo.total = expo.total.saturating_sub(to_balance(e.2)); + expo.others.retain(|i_expo| i_expo.who != *nominator); } e.2 = 0; }); - elected_edges.sort_unstable_by_key(|e| e.2); + elected_edges.sort_unstable_by_key(|e| + if let Some(e) = expo_map.get(&e.0) { e.total } else { Zero::zero() } + ); let mut cumulative_stake: ExtendedBalance = 0; let mut last_index = elected_edges.len() - 1; elected_edges.iter_mut().enumerate().for_each(|(idx, e)| { if let Some(expo) = expo_map.get_mut(&e.0) { let stake: ExtendedBalance = to_votes(expo.total); - let stake_mul = stake.saturating_mul(idx as ExtendedBalance); let stake_sub = stake_mul.saturating_sub(cumulative_stake); if stake_sub > budget { @@ -383,14 +385,9 @@ fn do_equalize( .saturating_add(last_stake) .saturating_sub(to_votes(expo.total)); expo.total = expo.total.saturating_add(to_balance(e.2)); - if let Some(i_expo) = expo.others.iter_mut().find(|i| i.who == nominator.clone()) { - i_expo.value = to_balance(e.2); - } + expo.others.push(IndividualExposure { who: nominator.clone(), value: to_balance(e.2)}); } }); - // Store back the individual edge weights. - elected_edges.iter().enumerate().for_each(|(idx, e)| elected_edges_balance[idx].2 = to_balance(e.2)); - difference } diff --git a/srml/staking/src/tests.rs b/srml/staking/src/tests.rs index 0643cd15cf5661c07b2a6126bc8e21591e43c882..5987d9800d963d79b70413c93b2c605617fa6e53 100644 --- a/srml/staking/src/tests.rs +++ b/srml/staking/src/tests.rs @@ -52,10 +52,30 @@ fn basic_setup_works() { assert_eq!(Staking::ledger(100), Some(StakingLedger { stash: 101, total: 500, active: 500, unlocking: vec![] })); assert_eq!(Staking::nominators(101), vec![11, 21]); - // Account 10 is exposed by 1000 * balance_factor from their own stash in account 11 + the default nominator vote - assert_eq!(Staking::stakers(11), Exposure { total: 1125, own: 1000, others: vec![ IndividualExposure { who: 101, value: 125 }] }); - // Account 20 is exposed by 1000 * balance_factor from their own stash in account 21 + the default nominator vote - assert_eq!(Staking::stakers(21), Exposure { total: 1375, own: 1000, others: vec![ IndividualExposure { who: 101, value: 375 }] }); + if cfg!(feature = "equalize") { + assert_eq!( + Staking::stakers(11), + Exposure { total: 1250, own: 1000, others: vec![ IndividualExposure { who: 101, value: 250 }] } + ); + assert_eq!( + Staking::stakers(21), + Exposure { total: 1250, own: 1000, others: vec![ IndividualExposure { who: 101, value: 250 }] } + ); + // initial slot_stake + assert_eq!(Staking::slot_stake(), 1250); + } else { + assert_eq!( + Staking::stakers(11), + Exposure { total: 1125, own: 1000, others: vec![ IndividualExposure { who: 101, value: 125 }] } + ); + assert_eq!( + Staking::stakers(21), + Exposure { total: 1375, own: 1000, others: vec![ IndividualExposure { who: 101, value: 375 }] } + ); + // initial slot_stake + assert_eq!(Staking::slot_stake(), 1125); + } + // The number of validators required. assert_eq!(Staking::validator_count(), 2); @@ -67,8 +87,6 @@ fn basic_setup_works() { // initial rewards assert_eq!(Staking::current_session_reward(), 10); - // initial slot_stake - assert_eq!(Staking::slot_stake(), 1125); // Naive // initial slash_count of validators assert_eq!(Staking::slash_count(&11), 0); @@ -76,6 +94,7 @@ fn basic_setup_works() { // All exposures must be correct. check_exposure_all(); + check_nominator_all(); }); } @@ -127,7 +146,7 @@ fn invulnerability_should_work() { with_externalities(&mut ExtBuilder::default().build(), || { // Make account 11 invulnerable - assert_ok!(Staking::set_invulnerables(vec![11])); + assert_ok!(Staking::set_invulnerables(Origin::ROOT, vec![11])); // Give account 11 some funds let _ = Balances::make_free_balance_be(&11, 70); // There is no slash grace -- slash immediately. @@ -194,12 +213,15 @@ fn offline_grace_should_delay_slashing() { // Set offline slash grace let offline_slash_grace = 1; - assert_ok!(Staking::set_offline_slash_grace(offline_slash_grace)); + assert_ok!(Staking::set_offline_slash_grace(Origin::ROOT, offline_slash_grace)); assert_eq!(Staking::offline_slash_grace(), 1); // Check unstake_threshold is 3 (default) let default_unstake_threshold = 3; - assert_eq!(Staking::validators(&11), ValidatorPrefs { unstake_threshold: default_unstake_threshold, validator_payment: 0 }); + assert_eq!( + Staking::validators(&11), + ValidatorPrefs { unstake_threshold: default_unstake_threshold, validator_payment: 0 } + ); // Check slash count is zero assert_eq!(Staking::slash_count(&11), 0); @@ -257,7 +279,7 @@ fn max_unstake_threshold_works() { validator_payment: 0, }); - >::put(Perbill::from_fraction(0.0001)); + OfflineSlash::put(Perbill::from_fraction(0.0001)); // Report each user 1 more than the max_unstake_threshold Staking::on_offline_validator(10, MAX_UNSTAKE_THRESHOLD as usize + 1); @@ -389,18 +411,18 @@ fn multi_era_reward_should_work() { // Set payee to controller assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Controller)); - start_session(1); + start_session(0); // session triggered: the reward value stashed should be 10 assert_eq!(Staking::current_session_reward(), session_reward); assert_eq!(Staking::current_era_reward(), session_reward); - start_session(2); + start_session(1); assert_eq!(Staking::current_session_reward(), session_reward); assert_eq!(Staking::current_era_reward(), 2*session_reward); - start_session(3); + start_session(2); // 1 + sum of of the session rewards accumulated let recorded_balance = 1 + 3*session_reward; @@ -411,13 +433,13 @@ fn multi_era_reward_should_work() { assert_eq!(Staking::current_session_reward(), new_session_reward); // fast forward to next era: - start_session(5); + start_session(4); // intermediate test. assert_eq!(Staking::current_era_reward(), 2*new_session_reward); // new era is triggered here. - start_session(6); + start_session(5); // pay time assert_eq!(Balances::total_balance(&10), 3*new_session_reward + recorded_balance); @@ -436,70 +458,52 @@ fn staking_should_work() { .build(), || { // remember + compare this along with the test. - assert_eq_uvec!(Session::validators(), vec![20, 10]); + 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); } // --- Block 1: - System::set_block_number(1); - Session::on_initialize(System::block_number()); - assert_eq!(Staking::current_era(), 0); - + 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!(Session::validators(), vec![20, 10]); + assert_eq_uvec!(validator_controllers(), vec![20, 10]); // --- Block 2: - System::set_block_number(2); - Session::on_initialize(System::block_number()); - assert_eq!(Staking::current_era(), 0); + start_session(2); // No effects will be seen so far. Era has not been yet triggered. - assert_eq_uvec!(Session::validators(), vec![20, 10]); - + assert_eq_uvec!(validator_controllers(), vec![20, 10]); - // --- Block 3: the validators will now change. - System::set_block_number(3); - Session::on_initialize(System::block_number()); - // 2 only voted for 4 and 20 - assert_eq!(Session::validators().len(), 2); - assert_eq_uvec!(Session::validators(), vec![20, 4]); + // --- 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); + assert_eq_uvec!(validator_controllers(), vec![20, 4]); // --- Block 4: Unstake 4 as a validator, freeing up the balance stashed in 3 - System::set_block_number(4); - Session::on_initialize(System::block_number()); - // 4 will chill Staking::chill(Origin::signed(4)).unwrap(); - // nothing should be changed so far. - assert_eq_uvec!(Session::validators(), vec![20, 4]); - assert_eq!(Staking::current_era(), 1); - - // --- Block 5: nothing. 4 is still there. - System::set_block_number(5); - Session::on_initialize(System::block_number()); - assert_eq_uvec!(Session::validators(), vec![20, 4]); - assert_eq!(Staking::current_era(), 1); - + start_session(5); + assert_eq_uvec!(validator_controllers(), vec![20, 4]); // --- Block 6: 4 will not be a validator. - System::set_block_number(6); - Session::on_initialize(System::block_number()); - assert_eq!(Staking::current_era(), 2); - assert_eq!(Session::validators().contains(&4), false); - assert_eq_uvec!(Session::validators(), vec![20, 10]); + 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![] })); + 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)); @@ -512,22 +516,24 @@ fn less_than_needed_candidates_works() { .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!(Session::validators(), vec![30, 20, 10]); + 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!(Session::validators(), vec![30, 20, 10]); + 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(); }); } @@ -538,14 +544,19 @@ fn no_candidate_emergency_condition() { with_externalities(&mut ExtBuilder::default() .minimum_validator_count(10) .validator_count(15) + .num_validators(4) .validator_pool(true) .nominate(false) .build(), || { - assert_eq!(Staking::validator_count(), 15); // initial validators - assert_eq_uvec!(Session::validators(), vec![10, 20, 30, 40]); + 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); let _ = Staking::chill(Origin::signed(10)); @@ -554,7 +565,7 @@ fn no_candidate_emergency_condition() { Session::on_initialize(System::block_number()); // Previous ones are elected. chill is invalidates. TODO: #2494 - assert_eq_uvec!(Session::validators(), vec![10, 20, 30, 40]); + assert_eq_uvec!(validator_controllers(), vec![10, 20, 30, 40]); assert_eq!(Staking::current_elected().len(), 0); }); } @@ -597,14 +608,13 @@ 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() .nominate(false) .validator_pool(true) .build(), || { // initial validators -- everyone is actually even. - assert_eq_uvec!(Session::validators(), vec![40, 30]); + assert_eq_uvec!(validator_controllers(), vec![40, 30]); // Set payee to controller assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Controller)); @@ -633,7 +643,7 @@ fn nominating_and_rewards_should_work() { start_era(1); // 10 and 20 have more votes, they will be chosen by phragmen. - assert_eq_uvec!(Session::validators(), vec![20, 10]); + assert_eq_uvec!(validator_controllers(), vec![20, 10]); // OLD validators must have already received some rewards. assert_eq!(Balances::total_balance(&40), 1 + 3 * session_reward); @@ -641,18 +651,57 @@ fn nominating_and_rewards_should_work() { // ------ check the staked value of all parties. - // 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!(Staking::stakers(21).total, 1000 + 1198); - // 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![599, 599]); - assert_eq!(Staking::stakers(21).others.iter().map(|e| e.who).collect::>>(), vec![3, 1]); + 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!(Staking::stakers(11).total, 1000 + 999); + // 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![599, 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!(Staking::stakers(21).total, 1000 + 999); + // 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, 599] + ); + 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!(Staking::stakers(21).total, 1000 + 1198); + // 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![599, 599] + ); + 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); @@ -662,28 +711,35 @@ fn nominating_and_rewards_should_work() { start_era(2); // next session reward. let new_session_reward = Staking::session_reward() * 3 * Staking::slot_stake(); - // nothing else will happen, era ends and rewards are paid again, - // it is expected that nominators will also be paid. See below - - // Approximation resulting from Perbill conversion - let approximation = 1; - // Nominator 2: has [400/1800 ~ 2/9 from 10] + [600/2200 ~ 3/11 from 20]'s reward. ==> 2/9 + 3/11 - assert_eq!( - Balances::total_balance(&2), - initial_balance + (2*new_session_reward/9 + 3*new_session_reward/11) - 1 - approximation - ); - // Nominator 4: has [400/1800 ~ 2/9 from 10] + [600/2200 ~ 3/11 from 20]'s reward. ==> 2/9 + 3/11 - assert_eq!( - Balances::total_balance(&4), - initial_balance + (2*new_session_reward/9 + 3*new_session_reward/11) - 1 - approximation - ); - // 10 got 800 / 1800 external stake => 8/18 =? 4/9 => Validator's share = 5/9 - assert_eq!(Balances::total_balance(&10), initial_balance + 5*new_session_reward/9 - approximation); - // 10 got 1200 / 2200 external stake => 12/22 =? 6/11 => Validator's share = 5/11 - assert_eq!(Balances::total_balance(&20), initial_balance + 5*new_session_reward/11+ 2); + // NOTE: some addition or substraction (-2, -3, +1) are due to arithmetic approximations + if cfg!(feature = "equalize") { + // Both have: has [400/2000 ~ 1/5 from 10] + [600/2000 ~ 3/10 from 20]'s reward. ==> 1/5 + 3/10 = 1/2 + assert_eq!(Balances::total_balance(&2), initial_balance + new_session_reward/2 - 3); + assert_eq!(Balances::total_balance(&4), initial_balance + new_session_reward/2 - 3); + // Rest for validators. + assert_eq!(Balances::total_balance(&10), initial_balance + new_session_reward/2 + 1); + assert_eq!(Balances::total_balance(&20), initial_balance + new_session_reward/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!( + Balances::total_balance(&2), + initial_balance + (2*new_session_reward/9 + 3*new_session_reward/11) - 2 + ); + // Nominator 4: has [400/1800 ~ 2/9 from 10] + [600/2200 ~ 3/11 from 20]'s reward. ==> 2/9 + 3/11 + assert_eq!( + Balances::total_balance(&4), + initial_balance + (2*new_session_reward/9 + 3*new_session_reward/11) - 2 + ); + + // 10 got 800 / 1800 external stake => 8/18 =? 4/9 => Validator's share = 5/9 + assert_eq!(Balances::total_balance(&10), initial_balance + 5*new_session_reward/9 - 1); + // 10 got 1200 / 2200 external stake => 12/22 =? 6/11 => Validator's share = 5/11 + assert_eq!(Balances::total_balance(&20), initial_balance + 5*new_session_reward/11 + 2); + } check_exposure_all(); + check_nominator_all(); }); } @@ -696,7 +752,7 @@ fn nominators_also_get_slashed() { assert_eq!(Staking::offline_slash_grace(), 0); // Account 10 has not been reported offline assert_eq!(Staking::slash_count(&10), 0); - >::put(Perbill::from_percent(12)); + OfflineSlash::put(Perbill::from_percent(12)); // Set payee to controller assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Controller)); @@ -730,6 +786,7 @@ fn nominators_also_get_slashed() { assert_eq!(Balances::total_balance(&10), initial_balance + 30 - validator_slash); assert_eq!(Balances::total_balance(&2), initial_balance - nominator_slash); check_exposure_all(); + check_nominator_all(); // Because slashing happened. assert!(is_disabled(10)); }); @@ -746,9 +803,15 @@ fn double_staking_should_fail() { || { let arbitrary_value = 5; // 2 = controller, 1 stashed => ok - assert_ok!(Staking::bond(Origin::signed(1), 2, arbitrary_value, RewardDestination::default())); + 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"); + 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. @@ -779,37 +842,37 @@ fn session_and_eras_work() { assert_eq!(Staking::current_era(), 0); // Block 1: No change. - start_session(1); + start_session(0); assert_eq!(Session::current_index(), 1); assert_eq!(Staking::current_era(), 0); // Block 2: Simple era change. - start_session(3); + start_session(2); assert_eq!(Session::current_index(), 3); assert_eq!(Staking::current_era(), 1); // Block 3: Schedule an era length change; no visible changes. - start_session(4); + start_session(3); assert_eq!(Session::current_index(), 4); assert_eq!(Staking::current_era(), 1); // Block 4: Era change kicks in. - start_session(6); + start_session(5); assert_eq!(Session::current_index(), 6); assert_eq!(Staking::current_era(), 2); // Block 5: No change. - start_session(7); + start_session(6); assert_eq!(Session::current_index(), 7); assert_eq!(Staking::current_era(), 2); // Block 6: No change. - start_session(8); + start_session(7); assert_eq!(Session::current_index(), 8); assert_eq!(Staking::current_era(), 2); // Block 7: Era increment. - start_session(9); + start_session(8); assert_eq!(Session::current_index(), 9); assert_eq!(Staking::current_era(), 3); }); @@ -1033,6 +1096,7 @@ fn validator_payment_prefs_work() { assert_eq!(Balances::total_balance(&2), 500 + shared_cut/2); check_exposure_all(); + check_nominator_all(); }); } @@ -1248,6 +1312,7 @@ fn slot_stake_is_least_staked_validator_and_exposure_defines_maximum_punishment( assert_eq!(Balances::free_balance(&11), 1000 + 30 - 51 /*5% of 1030*/ * 8 /*2**3*/); check_exposure_all(); + check_nominator_all(); }); } @@ -1411,15 +1476,13 @@ fn phragmen_poc_works() { // 10 with stake 166.66666666666674 20 with stake 333.33333333333326 30 with stake 0 // 4 has load 0.00075 and supported // 10 with stake 333.3333333333333 20 with stake 166.66666666666666 40 with stake 0.0 - - with_externalities(&mut ExtBuilder::default() .nominate(false) .validator_pool(true) .build(), || { // We don't really care about this. At this point everything is even. - assert_eq_uvec!(Session::validators(), vec![40, 30]); + assert_eq_uvec!(validator_controllers(), vec![40, 30]); // Set payees to Controller assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Controller)); @@ -1443,23 +1506,60 @@ fn phragmen_poc_works() { // New era => election algorithm will trigger start_era(1); - assert_eq_uvec!(Session::validators(), vec![20, 10]); + assert_eq_uvec!(validator_controllers(), vec![20, 10]); - // with stake 1666 and 1333 respectively assert_eq!(Staking::stakers(11).own, 1000); - assert_eq!(Staking::stakers(11).total, 1000 + 332); assert_eq!(Staking::stakers(21).own, 1000); - assert_eq!(Staking::stakers(21).total, 1000 + 666); + + if cfg!(feature = "equalize") { + assert_eq!(Staking::stakers(11).total, 1000 + 499); + assert_eq!(Staking::stakers(21).total, 1000 + 499); + } else { + assert_eq!(Staking::stakers(11).total, 1000 + 332); + assert_eq!(Staking::stakers(21).total, 1000 + 666); + } // Nominator's stake distribution. - assert_eq!(Staking::stakers(11).others.iter().map(|e| e.value).collect::>>(), vec![166, 166]); - assert_eq!(Staking::stakers(11).others.iter().map(|e| e.value).sum::>(), 332); assert_eq!(Staking::stakers(11).others.iter().map(|e| e.who).collect::>>(), vec![3, 1]); - - assert_eq!(Staking::stakers(21).others.iter().map(|e| e.value).collect::>>(), vec![333, 333]); - assert_eq!(Staking::stakers(21).others.iter().map(|e| e.value).sum::>(), 666); assert_eq!(Staking::stakers(21).others.iter().map(|e| e.who).collect::>>(), vec![3, 1]); + + if cfg!(feature = "equalize") { + assert_eq_uvec!( + Staking::stakers(11).others.iter().map(|e| e.value).collect::>>(), + vec![333, 166] + ); + assert_eq!( + Staking::stakers(11).others.iter().map(|e| e.value).sum::>(), + 499 + ); + assert_eq_uvec!( + Staking::stakers(21).others.iter().map(|e| e.value).collect::>>(), + vec![333, 166] + ); + assert_eq!( + Staking::stakers(21).others.iter().map(|e| e.value).sum::>(), + 499 + ); + } else { + assert_eq_uvec!( + Staking::stakers(11).others.iter().map(|e| e.value).collect::>>(), + vec![166, 166] + ); + assert_eq!( + Staking::stakers(11).others.iter().map(|e| e.value).sum::>(), + 332 + ); + assert_eq_uvec!( + Staking::stakers(21).others.iter().map(|e| e.value).collect::>>(), + vec![333, 333] + ); + assert_eq!( + Staking::stakers(21).others.iter().map(|e| e.value).sum::>(), + 666 + ); + } check_exposure_all(); + check_nominator_all(); }); } @@ -1478,7 +1578,7 @@ fn phragmen_poc_2_works() { // 30 is elected with stake 1344.2622950819673 and score 0.0007439024390243903 with_externalities(&mut ExtBuilder::default().nominate(false).build(), || { // initial setup of 10 and 20, both validators - assert_eq_uvec!(Session::validators(), vec![20, 10]); + assert_eq_uvec!(validator_controllers(), vec![20, 10]); // Bond [30, 31] as the third validator assert_ok!(Staking::bond_extra(Origin::signed(31), 999)); @@ -1510,6 +1610,7 @@ fn phragmen_poc_2_works() { (1, vec![(11, 4294967296)]), ]); check_exposure_all(); + check_nominator_all(); }) } @@ -1523,7 +1624,7 @@ fn switching_roles() { // Reset reward destination for i in &[10, 20] { assert_ok!(Staking::set_payee(Origin::signed(*i), RewardDestination::Controller)); } - assert_eq_uvec!(Session::validators(), vec![20, 10]); + assert_eq_uvec!(validator_controllers(), vec![20, 10]); // put some money in account that we'll use. for i in 1..7 { let _ = Balances::deposit_creating(&i, 5000); } @@ -1540,48 +1641,44 @@ fn switching_roles() { assert_ok!(Staking::validate(Origin::signed(6), ValidatorPrefs::default())); // new block - System::set_block_number(1); - Session::on_initialize(System::block_number()); + start_session(1); // no change - assert_eq_uvec!(Session::validators(), vec![20, 10]); + assert_eq_uvec!(validator_controllers(), vec![20, 10]); // new block - System::set_block_number(2); - Session::on_initialize(System::block_number()); + start_session(2); // no change - assert_eq_uvec!(Session::validators(), vec![20, 10]); + assert_eq_uvec!(validator_controllers(), vec![20, 10]); // new block --> ne era --> new validators - System::set_block_number(3); - Session::on_initialize(System::block_number()); + start_session(3); // with current nominators 10 and 5 have the most stake - assert_eq_uvec!(Session::validators(), vec![6, 10]); + assert_eq_uvec!(validator_controllers(), vec![6, 10]); // 2 decides to be a validator. Consequences: assert_ok!(Staking::validate(Origin::signed(2), ValidatorPrefs::default())); // new stakes: // 10: 1000 self vote - // 20: 1000 self vote + 500 vote + // 20: 1000 self vote + 250 vote // 6 : 1000 self vote - // 2 : 2000 self vote + 500 vote. + // 2 : 2000 self vote + 250 vote. // Winners: 20 and 2 - System::set_block_number(4); - Session::on_initialize(System::block_number()); - assert_eq_uvec!(Session::validators(), vec![6, 10]); + start_session(4); + assert_eq_uvec!(validator_controllers(), vec![6, 10]); - System::set_block_number(5); - Session::on_initialize(System::block_number()); - assert_eq_uvec!(Session::validators(), vec![6, 10]); + start_session(5); + assert_eq_uvec!(validator_controllers(), vec![6, 10]); // ne era - System::set_block_number(6); - Session::on_initialize(System::block_number()); - assert_eq_uvec!(Session::validators(), vec![2, 20]); + start_session(6); + assert_eq_uvec!(validator_controllers(), vec![2, 20]); + check_exposure_all(); + check_nominator_all(); }); } @@ -1592,7 +1689,7 @@ fn wrong_vote_is_null() { .validator_pool(true) .build(), || { - assert_eq_uvec!(Session::validators(), vec![40, 30]); + assert_eq_uvec!(validator_controllers(), vec![40, 30]); // put some money in account that we'll use. for i in 1..3 { let _ = Balances::deposit_creating(&i, 5000); } @@ -1607,7 +1704,7 @@ fn wrong_vote_is_null() { // new block start_era(1); - assert_eq_uvec!(Session::validators(), vec![20, 10]); + assert_eq_uvec!(validator_controllers(), vec![20, 10]); }); } @@ -1617,59 +1714,46 @@ fn bond_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(), || { - // setup - assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Controller)); - let _ = Balances::deposit_creating(&3, 1000); - let initial_balance_2 = Balances::free_balance(&2); - let initial_balance_4 = Balances::free_balance(&4); + // 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); - // Stingy validator. - assert_ok!(Staking::bond(Origin::signed(1), 2, 1, RewardDestination::Controller)); - assert_ok!(Staking::validate(Origin::signed(2), ValidatorPrefs::default())); + // 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); - - assert_eq_uvec!(Session::validators(), vec![30, 20, 10]); - - // min of 10, 20 and 30 (30 got a payout into staking so it raised it from 1 to 31). - assert_eq!(Staking::slot_stake(), 31); - - // make the stingy one elected. - assert_ok!(Staking::bond(Origin::signed(3), 4, 500, RewardDestination::Controller)); - assert_ok!(Staking::nominate(Origin::signed(4), vec![1])); - - // no rewards paid to 2 and 4 yet - assert_eq!(Balances::free_balance(&2), initial_balance_2); - assert_eq!(Balances::free_balance(&4), initial_balance_4); - start_era(2); - // Stingy one is selected - assert_eq_uvec!(Session::validators(), vec![20, 10, 2]); - assert_eq!(Staking::stakers(1), Exposure { - own: 1, - total: 501, - others: vec![IndividualExposure { who: 3, value: 500}], - }); - // New slot stake. - assert_eq!(Staking::slot_stake(), 501); - - // no rewards paid to 2 and 4 yet - assert_eq!(Balances::free_balance(&2), initial_balance_2); - assert_eq!(Balances::free_balance(&4), initial_balance_4); + // 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); - // Approximation resulting from Perbill conversion - let approximation = 1; - let reward = Staking::current_session_reward() * 3; - // 2 will not get a reward of only 1 - // 4 will get the rest - assert_eq!(Balances::free_balance(&2), initial_balance_2 + 3 - approximation); - assert_eq!(Balances::free_balance(&4), initial_balance_4 + reward - 3 - approximation); + // 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); + }); } @@ -1698,7 +1782,7 @@ fn bond_with_little_staked_value_bounded_by_slot_stake() { // 2 is elected. // and fucks up the slot stake. - assert_eq_uvec!(Session::validators(), vec![20, 10, 2]); + assert_eq_uvec!(validator_controllers(), vec![20, 10, 2]); assert_eq!(Staking::slot_stake(), 1); // Old ones are rewarded. @@ -1708,7 +1792,7 @@ fn bond_with_little_staked_value_bounded_by_slot_stake() { start_era(2); - assert_eq_uvec!(Session::validators(), vec![20, 10, 2]); + assert_eq_uvec!(validator_controllers(), vec![20, 10, 2]); assert_eq!(Staking::slot_stake(), 1); let reward = Staking::current_session_reward(); @@ -1717,12 +1801,13 @@ fn bond_with_little_staked_value_bounded_by_slot_stake() { // same for 10 assert_eq!(Balances::free_balance(&10), initial_balance_10 + 30 + reward.max(3)); check_exposure_all(); + check_nominator_all(); }); } +#[cfg(feature = "equalize")] #[test] -#[ignore] // Enable this once post-processing is on. fn phragmen_linear_worse_case_equalize() { with_externalities(&mut ExtBuilder::default() .nominate(false) @@ -1730,7 +1815,6 @@ fn phragmen_linear_worse_case_equalize() { .fair(true) .build(), || { - for i in &[10, 20, 30, 40] { assert_ok!(Staking::set_payee(Origin::signed(*i), RewardDestination::Controller)); } bond_validator(50, 1000); bond_validator(60, 1000); @@ -1744,32 +1828,27 @@ fn phragmen_linear_worse_case_equalize() { bond_nominator(120, 1000, vec![51, 61]); bond_nominator(130, 1000, vec![61, 71]); - assert_eq_uvec!(Session::validators(), vec![40, 30]); - assert_ok!(Staking::set_validator_count(7)); - - System::set_block_number(1); - Session::on_initialize(System::block_number()); + for i in &[10, 20, 30, 40, 50, 60, 70] { + assert_ok!(Staking::set_payee(Origin::signed(*i), RewardDestination::Controller)); + } - assert_eq_uvec!(Session::validators(), vec![10, 60, 40, 20, 50, 30, 70]); + assert_eq_uvec!(validator_controllers(), vec![40, 30]); + assert_ok!(Staking::set_validator_count(Origin::ROOT, 7)); - // Sequential Phragmén with post processing gives - // 10 is elected with stake 3000.0 and score 0.00025 - // 20 is elected with stake 2017.7421569824219 and score 0.0005277777777777777 - // 30 is elected with stake 2008.8712884829595 and score 0.0003333333333333333 - // 40 is elected with stake 2000.0001049958742 and score 0.0005555555555555556 - // 50 is elected with stake 2000.0001049958742 and score 0.0003333333333333333 - // 60 is elected with stake 1991.128921508789 and score 0.0004444444444444444 - // 70 is elected with stake 1982.2574230340813 and score 0.0007222222222222222 + start_era(1); - check_exposure_all(); + assert_eq_uvec!(validator_controllers(), vec![10, 60, 40, 20, 50, 30, 70]); assert_eq!(Staking::stakers(11).total, 3000); - assert_eq!(Staking::stakers(21).total, 2209); - assert_eq!(Staking::stakers(31).total, 2027); - assert_eq!(Staking::stakers(41).total, 2010); - assert_eq!(Staking::stakers(51).total, 2010); - assert_eq!(Staking::stakers(61).total, 1998); - assert_eq!(Staking::stakers(71).total, 1983); + assert_eq!(Staking::stakers(21).total, 2254); + assert_eq!(Staking::stakers(31).total, 2254); + assert_eq!(Staking::stakers(41).total, 1926); + assert_eq!(Staking::stakers(51).total, 1871); + assert_eq!(Staking::stakers(61).total, 1892); + assert_eq!(Staking::stakers(71).total, 1799); + + check_exposure_all(); + check_nominator_all(); }) } @@ -1783,13 +1862,14 @@ fn phragmen_chooses_correct_number_of_validators() { .build(), || { assert_eq!(Staking::validator_count(), 1); - assert_eq!(Session::validators().len(), 1); + assert_eq!(validator_controllers().len(), 1); System::set_block_number(1); Session::on_initialize(System::block_number()); - assert_eq!(Session::validators().len(), 1); + assert_eq!(validator_controllers().len(), 1); check_exposure_all(); + check_nominator_all(); }) } @@ -1807,8 +1887,9 @@ fn phragmen_score_should_be_accurate_on_large_stakes() { start_era(1); - assert_eq!(Session::validators(), vec![4, 2]); + assert_eq!(validator_controllers(), vec![4, 2]); check_exposure_all(); + check_nominator_all(); }) } @@ -1829,7 +1910,7 @@ fn phragmen_should_not_overflow_validators() { start_era(1); - assert_eq_uvec!(Session::validators(), vec![4, 2]); + assert_eq_uvec!(validator_controllers(), vec![4, 2]); // This test will fail this. Will saturate. // check_exposure_all(); @@ -1855,7 +1936,7 @@ fn phragmen_should_not_overflow_nominators() { start_era(1); - assert_eq_uvec!(Session::validators(), vec![4, 2]); + assert_eq_uvec!(validator_controllers(), vec![4, 2]); // Saturate. assert_eq!(Staking::stakers(3).total, u64::max_value()); @@ -1877,7 +1958,7 @@ fn phragmen_should_not_overflow_ultimate() { start_era(1); - assert_eq_uvec!(Session::validators(), vec![4, 2]); + assert_eq_uvec!(validator_controllers(), vec![4, 2]); // Saturate. assert_eq!(Staking::stakers(3).total, u64::max_value()); @@ -1929,32 +2010,8 @@ fn phragmen_large_scale_test() { start_era(1); - // For manual inspection - println!("Validators are {:?}", Session::validators()); - println!("Validators are {:#?}", - Session::validators() - .iter() - .map(|v| (v.clone(), Staking::stakers(v+1))) - .collect::)>>() - ); - - // Each exposure => total == own + sum(others) check_exposure_all(); - - // aside from some error, stake must be divided correctly - let individual_expo_sum: u128 = Session::validators() - .iter() - .map(|v| Staking::stakers(v+1)) - .fold(0u128, |s, v| if v.others.len() > 0 { s + v.others[0].value as u128 } else { s }); - assert!( - 990000000000000000 - individual_expo_sum < 100, - format!( - "Nominator stake = {} / SUM(individual expo) = {} / diff = {}", - 990000000000000000u64, - individual_expo_sum, - 990000000000000000 - individual_expo_sum - ) - ); + check_nominator_all(); }) } @@ -1980,6 +2037,7 @@ fn phragmen_large_scale_test_2() { // Each exposure => total == own + sum(others) check_exposure_all(); + check_nominator_all(); assert_total_expo(3, nom_budget / 2 + c_budget); assert_total_expo(5, nom_budget / 2 + c_budget); diff --git a/srml/sudo/Cargo.toml b/srml/sudo/Cargo.toml index 29ef579516fd58d7fe52f7611a3a678753b539ed..7a25075390d890307a996f096138d6392670c7a1 100644 --- a/srml/sudo/Cargo.toml +++ b/srml/sudo/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" [dependencies] serde = { version = "1.0", optional = true } -parity-codec = { version = "3.3", default-features = false, features = ["derive"] } +parity-codec = { version = "4.1.1", default-features = false, features = ["derive"] } sr-std = { path = "../../core/sr-std", default-features = false } sr-io = { path = "../../core/sr-io", default-features = false } sr-primitives = { path = "../../core/sr-primitives", default-features = false } diff --git a/srml/support/Cargo.toml b/srml/support/Cargo.toml index 22635106dfa4e6204329f5066d850b7fef328e57..6abe1fb336481f4be2babd3c5a404f6665b4bd5a 100644 --- a/srml/support/Cargo.toml +++ b/srml/support/Cargo.toml @@ -6,11 +6,12 @@ edition = "2018" [dependencies] serde = { version = "1.0", optional = true, features = ["derive"] } -codec = { package = "parity-codec", version = "3.5.1", default-features = false, features = ["derive"] } +codec = { package = "parity-codec", version = "4.1.1", default-features = false, features = ["derive"] } srml-metadata = { path = "../metadata", default-features = false } 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 } +substrate-primitives = { path = "../../core/primitives", default-features = false } inherents = { package = "substrate-inherents", path = "../../core/inherents", default-features = false } srml-support-procedural = { path = "./procedural" } paste = "0.1" @@ -19,7 +20,7 @@ bitmask = { version = "0.5", default-features = false } [dev-dependencies] pretty_assertions = "0.6.1" -srml-system = { path = "../system", default-features = false } +srml-system = { path = "../system" } [features] default = ["std"] diff --git a/srml/support/procedural/src/lib.rs b/srml/support/procedural/src/lib.rs index 280d2a317d14e51070cdb3c5c409c3d93cb7654d..78051ee8f2735e4fe72b8482bf9dfcdf139bbd42 100644 --- a/srml/support/procedural/src/lib.rs +++ b/srml/support/procedural/src/lib.rs @@ -18,7 +18,7 @@ //! Proc macro of Support code for the runtime. // end::description[] -#![recursion_limit="256"] +#![recursion_limit="512"] extern crate proc_macro; @@ -83,6 +83,14 @@ use proc_macro::TokenStream; /// If the second key is untrusted, a cryptographic `hasher` such as `blake2_256` must be used. /// Otherwise, other items in storage with the same first key can be compromised. /// +/// Supported hashers (ordered from least to best security): +/// +/// * `twox_64_concat` - TwoX with 64bit + key concatenated. +/// * `twox_128` - TwoX with 128bit. +/// * `twox_256` - TwoX with with 256bit. +/// * `blake2_128` - Blake2 with 128bit. +/// * `blake2_256` - Blake2 with 256bit. +/// /// Basic storage can be extended as such: /// /// `#vis #name get(#getter) config(#field_name) build(#closure): #type = #default;` @@ -98,7 +106,7 @@ use proc_macro::TokenStream; /// /// Storage items are accessible in multiple ways: /// -/// * The structure: `Foo::` +/// * The structure: `Foo` or `Foo::` depending if the value type is generic or not. /// * The `Store` trait structure: ` as Store>::Foo` /// * The getter on the module that calls get on the structure: `Module::::foo()` /// @@ -136,9 +144,36 @@ use proc_macro::TokenStream; /// trait Store for Module, I: Instance=DefaultInstance> as Example {} /// ``` /// -/// Then the genesis config is generated with two generic parameters (i.e. `GenesisConfig`) -/// and storage items are accessible using two generic parameters, e.g.: -/// `>::get()` or `Dummy::::get()`. +/// Accessing the structure no requires the instance as generic parameter: +/// * `Foo::` if the value type is not generic +/// * `Foo::` if the value type is generic +/// +/// ## Where clause +/// +/// This macro supports a where clause which will be replicated to all generated types. +/// +/// ```nocompile +/// trait Store for Module as Example where T::AccountId: std::fmt::Display {} +/// ``` +/// +/// ## Limitations +/// +/// # Instancing and generic `GenesisConfig` +/// +/// If your module supports instancing and you see an error like `parameter `I` is never used` for +/// your `decl_storage!`, you are hitting a limitation of the current implementation. You probably +/// try to use an associated type of a non-instantiable trait. To solve this, add the following to +/// your macro call: +/// +/// ```nocompile +/// add_extra_genesis { +/// config(phantom): std::marker::PhantomData, +/// } +/// ... +/// +/// This adds a field to your `GenesisConfig` with the name `phantom` that you can initialize with +/// `Default::default()`. +/// #[proc_macro] pub fn decl_storage(input: TokenStream) -> TokenStream { storage::transformation::decl_storage_impl(input) diff --git a/srml/support/procedural/src/storage/impls.rs b/srml/support/procedural/src/storage/impls.rs index b481a4242053954cb66d2ee5dbf05a47d0a0c729..45d7fada9fcaf082783727d7e15b4b69a0889ece 100644 --- a/srml/support/procedural/src/storage/impls.rs +++ b/srml/support/procedural/src/storage/impls.rs @@ -14,10 +14,12 @@ // 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; +use syn::Ident; use quote::quote; -use crate::storage::transformation::{DeclStorageTypeInfos, InstanceOpts}; pub fn option_unwrap(is_option: bool) -> TokenStream2 { if !is_option { @@ -45,6 +47,7 @@ pub(crate) struct Impls<'a, I: Iterator> { 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> { @@ -60,6 +63,7 @@ impl<'a, I: Iterator> Impls<'a, I> { prefix, name, attrs, + where_clause, .. } = self; let DeclStorageTypeInfos { typ, value_type, is_option, .. } = type_infos; @@ -79,7 +83,6 @@ impl<'a, I: Iterator> Impls<'a, I> { }; let InstanceOpts { - comma_instance, equal_default_instance, bound_instantiable, instance, @@ -87,20 +90,39 @@ impl<'a, I: Iterator> Impls<'a, I> { } = 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()); + 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!{ + quote! { #( #[ #attrs ] )* - #visibility struct #name<#traitinstance: #traittype, #instance #bound_instantiable #equal_default_instance> - (#scrate::rstd::marker::PhantomData<(#traitinstance #comma_instance)>); + #visibility struct #name<#struct_trait>( + #scrate::rstd::marker::PhantomData<(#trait_and_instance)> + ) #where_clause; - impl<#traitinstance: #traittype, #instance #bound_instantiable> - #scrate::storage::hashed::generator::StorageValue<#typ> for #name<#traitinstance, #instance> + impl<#impl_trait> #scrate::storage::hashed::generator::StorageValue<#typ> + for #name<#trait_and_instance> #where_clause { type Query = #value_type; @@ -149,6 +171,7 @@ impl<'a, I: Iterator> Impls<'a, I> { prefix, name, attrs, + where_clause, .. } = self; let DeclStorageTypeInfos { typ, value_type, is_option, .. } = type_infos; @@ -170,7 +193,6 @@ impl<'a, I: Iterator> Impls<'a, I> { }; let InstanceOpts { - comma_instance, equal_default_instance, bound_instantiable, instance, @@ -184,14 +206,34 @@ impl<'a, I: Iterator> Impls<'a, I> { 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<#traitinstance: #traittype, #instance #bound_instantiable #equal_default_instance> - (#scrate::rstd::marker::PhantomData<(#traitinstance #comma_instance)>); + #visibility struct #name<#struct_trait>( + #scrate::rstd::marker::PhantomData<(#trait_and_instance)> + ) #where_clause; - impl<#traitinstance: #traittype, #instance #bound_instantiable> - #scrate::storage::hashed::generator::StorageMap<#kty, #typ> for #name<#traitinstance, #instance> + impl<#impl_trait> #scrate::storage::hashed::generator::StorageMap<#kty, #typ> + for #name<#trait_and_instance> #where_clause { type Query = #value_type; @@ -235,8 +277,8 @@ impl<'a, I: Iterator> Impls<'a, I> { } } - impl<#traitinstance: 'static + #traittype, #instance #bound_instantiable> - #scrate::storage::hashed::generator::AppendableStorageMap<#kty, #typ> for #name<#traitinstance, #instance> + impl<#impl_trait> #scrate::storage::hashed::generator::AppendableStorageMap<#kty, #typ> + for #name<#trait_and_instance> #where_clause {} } } @@ -253,11 +295,11 @@ impl<'a, I: Iterator> Impls<'a, I> { prefix, name, attrs, + where_clause, .. } = self; let InstanceOpts { - comma_instance, equal_default_instance, bound_instantiable, instance, @@ -265,7 +307,9 @@ impl<'a, I: Iterator> Impls<'a, I> { } = 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()); + 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() } @@ -273,7 +317,9 @@ impl<'a, I: Iterator> Impls<'a, I> { // make sure to use different prefix for head and elements. let final_head_key = if let Some(instance) = instance { - let const_name = syn::Ident::new(&format!("{}{}", HEAD_KEY_FOR, name.to_string()), proc_macro2::Span::call_site()); + 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 final_head_key = format!("head of {}", prefix); @@ -283,8 +329,10 @@ impl<'a, I: Iterator> Impls<'a, I> { let DeclStorageTypeInfos { typ, value_type, is_option, .. } = type_infos; let option_simple_1 = option_unwrap(is_option); let name_lowercase = name.to_string().to_lowercase(); - let inner_module = syn::Ident::new(&format!("__linked_map_details_for_{}_do_not_use", name_lowercase), name.span()); - let linkage = syn::Ident::new(&format!("__LinkageFor{}DoNotUse", name), name.span()); + let inner_module = Ident::new( + &format!("__linked_map_details_for_{}_do_not_use", name_lowercase), name.span() + ); + let linkage = Ident::new(&format!("__LinkageFor{}DoNotUse", name), name.span()); let phantom_data = quote! { #scrate::rstd::marker::PhantomData }; let as_map = quote!{ > }; let put_or_insert = quote! { @@ -304,6 +352,38 @@ impl<'a, I: Iterator> Impls<'a, I> { } }; + let trait_required = ext::type_contains_ident(value_type, traitinstance) + || ext::type_contains_ident(kty, traitinstance); + + let (struct_trait, impl_trait, trait_and_instance) = if trait_required { + ( + quote!(#traitinstance: #traittype, #instance #bound_instantiable #equal_default_instance), + quote!(#traitinstance: #traittype, #instance #bound_instantiable), + quote!(#traitinstance, #instance), + ) + } else { + ( + quote!(#instance #bound_instantiable #equal_default_instance), + quote!(#instance #bound_instantiable), + quote!(#instance), + ) + }; + + let (where_clause, trait_where_clause) = if trait_required { + ( + where_clause.clone(), + where_clause.clone().map(|mut wc| { + wc.predicates.push(syn::parse_quote!(#traitinstance: 'static)); + wc + }).or_else(|| syn::parse_quote!(where #traitinstance: 'static)), + ) + } else { + ( + None, + None, + ) + }; + // generator for linked map let helpers = quote! { /// Linkage data of an element (it's successor and predecessor) @@ -337,15 +417,14 @@ impl<'a, I: Iterator> Impls<'a, I> { pub _data: #phantom_data, } - impl<'a, S: #scrate::HashedStorage<#scrate::#hasher>, #traitinstance: #traittype, #instance #bound_instantiable> - Iterator for Enumerator<'a, S, #kty, (#typ, #traitinstance, #instance)> - where #traitinstance: 'a + impl<'a, S: #scrate::HashedStorage<#scrate::#hasher>, #impl_trait> Iterator + for Enumerator<'a, S, #kty, (#typ, #trait_and_instance)> #where_clause { type Item = (#kty, #typ); fn next(&mut self) -> Option { let next = self.next.take()?; - let key_for = + let key_for = as #scrate::storage::hashed::generator::StorageMap<#kty, #typ>>::key_for(&next); let (val, linkage): (#typ, Linkage<#kty>) = self.storage.get(&*key_for) @@ -355,7 +434,7 @@ impl<'a, I: Iterator> Impls<'a, I> { } } - pub(crate) trait Utils<#traitinstance: #traittype, #instance #bound_instantiable> { + pub(crate) trait Utils<#struct_trait> { /// Update linkage when this element is removed. /// /// Takes care of updating previous and next elements points @@ -388,9 +467,11 @@ impl<'a, I: Iterator> Impls<'a, I> { let structure = quote! { #( #[ #attrs ] )* - #visibility struct #name<#traitinstance: #traittype, #instance #bound_instantiable #equal_default_instance>(#phantom_data<(#traitinstance #comma_instance)>); + #visibility struct #name<#struct_trait>(#phantom_data<(#trait_and_instance)>); - impl<#traitinstance: #traittype, #instance #bound_instantiable> self::#inner_module::Utils<#traitinstance, #instance> for #name<#traitinstance, #instance> { + impl<#impl_trait> self::#inner_module::Utils<#trait_and_instance> + for #name<#trait_and_instance> #where_clause + { fn remove_linkage>( linkage: self::#inner_module::Linkage<#kty>, storage: &mut S, @@ -477,8 +558,8 @@ impl<'a, I: Iterator> Impls<'a, I> { #structure - impl<#traitinstance: #traittype, #instance #bound_instantiable> - #scrate::storage::hashed::generator::StorageMap<#kty, #typ> for #name<#traitinstance, #instance> + impl<#impl_trait> #scrate::storage::hashed::generator::StorageMap<#kty, #typ> + for #name<#trait_and_instance> #where_clause { type Query = #value_type; @@ -553,8 +634,8 @@ impl<'a, I: Iterator> Impls<'a, I> { } } - impl<#traitinstance: 'static + #traittype, #instance #bound_instantiable> - #scrate::storage::hashed::generator::EnumerableStorageMap<#kty, #typ> for #name<#traitinstance, #instance> + impl<#impl_trait> #scrate::storage::hashed::generator::EnumerableStorageMap<#kty, #typ> + for #name<#trait_and_instance> #trait_where_clause { fn head>(storage: &S) -> Option<#kty> { use self::#inner_module::Utils; @@ -562,18 +643,20 @@ impl<'a, I: Iterator> Impls<'a, I> { Self::read_head(storage) } - fn enumerate<'a, S>(storage: &'a S) -> #scrate::rstd::boxed::Box + 'a> - where - S: #scrate::HashedStorage<#scrate::#hasher>, - #kty: 'a, - #typ: 'a, + fn enumerate<'a, S>( + storage: &'a S + ) -> #scrate::rstd::boxed::Box + 'a> + where + S: #scrate::HashedStorage<#scrate::#hasher>, + #kty: 'a, + #typ: 'a, { use self::#inner_module::{Utils, Enumerator}; #scrate::rstd::boxed::Box::new(Enumerator { next: Self::read_head(storage), storage, - _data: #phantom_data::<(#typ, #traitinstance, #instance)>::default(), + _data: #phantom_data::<(#typ, #trait_and_instance)>::default(), }) } } @@ -598,29 +681,36 @@ impl<'a, I: Iterator> Impls<'a, I> { name, attrs, instance_opts, + where_clause, .. } = self; let DeclStorageTypeInfos { typ, value_type, is_option, .. } = type_infos; let option_simple_1 = option_unwrap(is_option); - let as_double_map = quote!{ > }; + let as_double_map = quote!{ + > + }; let mutate_impl = if !is_option { quote!{ - #as_double_map::insert(key1, key2, &val, storage) + #as_double_map::insert(k1, k2, &val, storage) } } else { quote!{ match val { - Some(ref val) => #as_double_map::insert(key1, key2, &val, storage), - None => #as_double_map::remove(key1, key2, storage), + Some(ref val) => #as_double_map::insert::( + k1, + k2, + val, + storage, + ), + None => #as_double_map::remove(k1, k2, storage), } } }; let InstanceOpts { - comma_instance, equal_default_instance, bound_instantiable, instance, @@ -628,24 +718,48 @@ impl<'a, I: Iterator> Impls<'a, I> { } = 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()); + 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<#traitinstance: #traittype, #instance #bound_instantiable #equal_default_instance> - (#scrate::rstd::marker::PhantomData<(#traitinstance #comma_instance)>); + #visibility struct #name<#struct_trait> + (#scrate::rstd::marker::PhantomData<(#trait_and_instance)>); - impl<#traitinstance: #traittype, #instance #bound_instantiable> - #scrate::storage::unhashed::generator::StorageDoubleMap<#k1ty, #k2ty, #typ> for #name<#traitinstance, #instance> + impl<#impl_trait> #scrate::storage::unhashed::generator::StorageDoubleMap<#k1ty, #k2ty, #typ> + for #name<#trait_and_instance> #where_clause { type Query = #value_type; - fn prefix_for(k1: &#k1ty) -> Vec { + fn prefix_for(k1: &KArg1) -> #scrate::rstd::vec::Vec where + KArg1: ?Sized + #scrate::codec::Encode, + #k1ty: #scrate::rstd::borrow::Borrow, + { use #scrate::storage::hashed::generator::StorageHasher; let mut key = #as_double_map::prefix().to_vec(); @@ -657,7 +771,15 @@ impl<'a, I: Iterator> Impls<'a, I> { #final_prefix } - fn key_for(k1: &#k1ty, k2: &#k2ty) -> Vec { + fn key_for( + k1: &KArg1, + k2: &KArg2, + ) -> #scrate::rstd::vec::Vec where + #k1ty: #scrate::rstd::borrow::Borrow, + #k2ty: #scrate::rstd::borrow::Borrow, + KArg1: ?Sized + #scrate::codec::Encode, + KArg2: ?Sized + #scrate::codec::Encode, + { use #scrate::storage::hashed::generator::StorageHasher; let mut key = #as_double_map::prefix_for(k1); @@ -665,30 +787,53 @@ impl<'a, I: Iterator> Impls<'a, I> { key } - fn get(key1: &#k1ty, key2: &#k2ty, storage: &S) -> Self::Query { - let key = #as_double_map::key_for(key1, key2); + fn get( + k1: &KArg1, + k2: &KArg2, + storage: &S, + ) -> Self::Query where + #k1ty: #scrate::rstd::borrow::Borrow, + #k2ty: #scrate::rstd::borrow::Borrow, + KArg1: ?Sized + #scrate::codec::Encode, + KArg2: ?Sized + #scrate::codec::Encode, + { + let key = #as_double_map::key_for(k1, k2); storage.get(&key).#option_simple_1(|| #fielddefault) } - fn take(key1: &#k1ty, key2: &#k2ty, storage: &mut S) -> Self::Query { - let key = #as_double_map::key_for(key1, key2); + fn take( + k1: &KArg1, + k2: &KArg2, + storage: &mut S, + ) -> Self::Query where + #k1ty: #scrate::rstd::borrow::Borrow, + #k2ty: #scrate::rstd::borrow::Borrow, + KArg1: ?Sized + #scrate::codec::Encode, + KArg2: ?Sized + #scrate::codec::Encode, + { + let key = #as_double_map::key_for(k1, k2); storage.take(&key).#option_simple_1(|| #fielddefault) } - fn mutate(key1: &#k1ty, key2: &#k2ty, f: F, storage: &mut S) -> R - where + fn mutate( + k1: &KArg1, + k2: &KArg2, + f: F, + storage: &mut S, + ) -> R where + #k1ty: #scrate::rstd::borrow::Borrow, + #k2ty: #scrate::rstd::borrow::Borrow, + KArg1: ?Sized + #scrate::codec::Encode, + KArg2: ?Sized + #scrate::codec::Encode, F: FnOnce(&mut Self::Query) -> R, - S: #scrate::UnhashedStorage, { - let mut val = #as_double_map::get(key1, key2, storage); + let mut val = #as_double_map::get(k1, k2, storage); let ret = f(&mut val); - #mutate_impl ; + #mutate_impl; ret } - } } - } } diff --git a/srml/support/procedural/src/storage/mod.rs b/srml/support/procedural/src/storage/mod.rs index 742c47d259350649319910376ea61fd5bc6ce937..4253206f44da064d5903362c3ed72aedbb51a0c7 100644 --- a/srml/support/procedural/src/storage/mod.rs +++ b/srml/support/procedural/src/storage/mod.rs @@ -65,9 +65,9 @@ struct StorageDefinition { 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, - pub extra_genesis_skip_phantom_data_field: ext::Opt, } #[derive(Parse, ToTokens, Debug)] @@ -82,12 +82,6 @@ struct AddExtraGenesis { pub content: ext::Braces, } -#[derive(Parse, ToTokens, Debug)] -struct ExtraGenesisSkipPhantomDataField { - pub genesis_phantom_keyword: keyword::extra_genesis_skip_phantom_data_field, - pub token: Token![;], -} - #[derive(Parse, ToTokens, Debug)] struct AddExtraGenesisContent { pub lines: ext::Punctuated, diff --git a/srml/support/procedural/src/storage/transformation.rs b/srml/support/procedural/src/storage/transformation.rs index 2827259420991ddd454b1d1c715599b3d854c33a..ae0531c57d4b438776f48f141bb085a377db37b8 100644 --- a/srml/support/procedural/src/storage/transformation.rs +++ b/srml/support/procedural/src/storage/transformation.rs @@ -14,19 +14,20 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -// tag::description[] //! `decl_storage` macro transformation -// end::description[] use srml_support_procedural_tools::syn_ext as ext; -use srml_support_procedural_tools::{generate_crate_access, generate_hidden_includes, clean_type_string}; +use srml_support_procedural_tools::{ + generate_crate_access, generate_hidden_includes, clean_type_string +}; use proc_macro::TokenStream; -use proc_macro2::TokenStream as TokenStream2; +use proc_macro2::{TokenStream as TokenStream2, Span}; use syn::{ Ident, GenericParam, + WhereClause, spanned::Spanned, parse::{ Error, @@ -39,6 +40,9 @@ use quote::quote; 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 ) => { @@ -65,11 +69,15 @@ pub fn decl_storage_impl(input: TokenStream) -> TokenStream { crate_ident: cratename, content: ext::Braces { content: storage_lines, ..}, extra_genesis, - extra_genesis_skip_phantom_data_field, + where_clause, .. } = def; - let instance_opts = match get_instance_opts(mod_instance, mod_instantiable, mod_default_instance) { + 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(), }; @@ -104,7 +112,7 @@ pub fn decl_storage_impl(input: TokenStream) -> TokenStream { &instance_opts, &storage_lines, &extra_genesis.inner, - extra_genesis_skip_phantom_data_field.inner.is_some(), + &where_clause, )); let decl_storage_items = decl_storage_items( &scrate, @@ -113,7 +121,9 @@ pub fn decl_storage_impl(input: TokenStream) -> TokenStream { &instance_opts, &cratename, &storage_lines, + &where_clause, ); + let decl_store_items = decl_store_items( &storage_lines, ); @@ -134,6 +144,7 @@ pub fn decl_storage_impl(input: TokenStream) -> TokenStream { &traittype, &instance_opts, &storage_lines, + &where_clause, ); let InstanceOpts { @@ -150,19 +161,17 @@ pub fn decl_storage_impl(input: TokenStream) -> TokenStream { #decl_store_items } #store_default_struct - impl<#traitinstance: #traittype, #instance #bound_instantiable> #storetype for #module_ident<#traitinstance, #instance> { + 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> { + impl<#traitinstance: 'static + #traittype, #instance #bound_instantiable> + #module_ident<#traitinstance, #instance> #where_clause + { #impl_store_fns #[doc(hidden)] - pub fn store_metadata() -> #scrate::metadata::StorageMetadata { - #scrate::metadata::StorageMetadata { - functions: #scrate::metadata::DecodeDifferent::Encode(#store_functions_to_metadata) , - } - } - #[doc(hidden)] - pub fn store_metadata_functions() -> &'static [#scrate::metadata::StorageFunctionMetadata] { + pub fn store_metadata_functions() -> &'static [#scrate::metadata::StorageEntryMetadata] { #store_functions_to_metadata } #[doc(hidden)] @@ -172,7 +181,6 @@ pub fn decl_storage_impl(input: TokenStream) -> TokenStream { } #extra_genesis - }; expanded.into() @@ -185,11 +193,10 @@ fn decl_store_extra_genesis( instance_opts: &InstanceOpts, storage_lines: &ext::Punctuated, extra_genesis: &Option, - extra_genesis_skip_phantom_data_field: bool, + where_clause: &Option, ) -> Result { let InstanceOpts { - comma_instance, equal_default_instance, bound_instantiable, instance, @@ -197,13 +204,14 @@ fn decl_store_extra_genesis( } = instance_opts; let mut is_trait_needed = false; - let mut has_trait_field = 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(); - for sline in storage_lines.inner.iter() { + 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, @@ -217,9 +225,17 @@ fn decl_store_extra_genesis( let type_infos = get_type_infos(storage_type); - let opt_build; + 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 - if let Some(ref config) = config.inner { + 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 { @@ -234,19 +250,37 @@ fn decl_store_extra_genesis( ) ); }; - if type_infos.kind.is_simple() && ext::has_parametric_type(type_infos.value_type, traitinstance) { + + if ext::type_contains_ident(type_infos.value_type, traitinstance) { is_trait_needed = true; - has_trait_field = 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 )), + 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()); + } }, _ => {}, } @@ -270,8 +304,6 @@ fn decl_store_extra_genesis( quote!( #( #[ #attrs ] )* pub #ident: Vec<(#key1_type, #key2_type, #storage_type)>, ) }, }); - opt_build = Some(build.inner.as_ref().map(|b| &b.expr.content).map(|b|quote!( #b )) - .unwrap_or_else(|| quote!( (|config: &GenesisConfig<#traitinstance, #instance>| config.#ident.clone()) ))); let fielddefault = default_value.inner.as_ref().map(|d| &d.expr).map(|d| if type_infos.is_option { @@ -281,48 +313,74 @@ fn decl_store_extra_genesis( }).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 = build.inner.as_ref().map(|b| &b.expr.content).map(|b| quote!( #b )); - } + opt_build + }; let typ = type_infos.typ; - if let Some(builder) = opt_build { - is_trait_needed = true; + if let Some(builder) = builder { builders.extend(match type_infos.kind { DeclStorageTypeInfosKind::Simple => { - quote!{{ - use #scrate::rstd::{cell::RefCell, marker::PhantomData}; - use #scrate::codec::{Encode, Decode}; + 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<#traitinstance, #instance> as #scrate::storage::hashed::generator::StorageValue<#typ>>::put(&v, storage); + < + #name<#struct_trait #instance> as + #scrate::storage::hashed::generator::StorageValue<#typ> + >::put(&v, storage); }} }, DeclStorageTypeInfosKind::Map { key_type, .. } => { - quote!{{ - use #scrate::rstd::{cell::RefCell, marker::PhantomData}; - use #scrate::codec::{Encode, Decode}; + 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!() + }; + quote!{{ let data = (#builder)(&self); - for (k, v) in data.into_iter() { - <#name<#traitinstance, #instance> as #scrate::storage::hashed::generator::StorageMap<#key_type, #typ>>::insert(&k, &v, storage); - } + data.into_iter().for_each(|(k, v)| { + < + #name<#struct_trait #instance> as + #scrate::storage::hashed::generator::StorageMap<#key_type, #typ> + >::insert(&k, &v, storage); + }); }} }, DeclStorageTypeInfosKind::DoubleMap { key1_type, key2_type, .. } => { - quote!{{ - use #scrate::rstd::{cell::RefCell, marker::PhantomData}; - use #scrate::codec::{Encode, Decode}; + 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); - for (k1, k2, v) in data.into_iter() { - <#name<#traitinstance, #instance> as #scrate::storage::unhashed::generator::StorageDoubleMap<#key1_type, #key2_type, #typ>>::insert(&k1, &k2, &v, storage); - } + data.into_iter().for_each(|(k1, k2, v)| { + < + #name<#struct_trait #instance> as + #scrate::storage::unhashed::generator::StorageDoubleMap<#key1_type, #key2_type, #typ> + >::insert(&k1, &k2, &v, storage); + }); }} }, }); } - } let mut has_scall = false; @@ -341,9 +399,8 @@ fn decl_store_extra_genesis( default_value, .. }) => { - if ext::has_parametric_type(&extra_type, traitinstance) { + if ext::type_contains_ident(&extra_type, traitinstance) { is_trait_needed = true; - has_trait_field = true; } serde_complete_bound.push(quote!( #extra_type )); @@ -362,6 +419,7 @@ fn decl_store_extra_genesis( 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!( ( #content ) ); has_scall = true; @@ -370,7 +428,6 @@ fn decl_store_extra_genesis( } } - let serde_bug_bound = if !serde_complete_bound.is_empty() { let mut b_ser = String::new(); let mut b_dser = String::new(); @@ -393,55 +450,81 @@ fn decl_store_extra_genesis( || !config_field.is_empty() || !genesis_extrafields.is_empty() || !builders.is_empty(); - Ok(if is_extra_genesis_needed { - let (fparam_struct, fparam_impl, sparam, ph_field, ph_default) = if is_trait_needed { - if (has_trait_field && instance.is_none()) || extra_genesis_skip_phantom_data_field { - // no phantom data required - ( - quote!(<#traitinstance: #traittype, #instance #bound_instantiable #equal_default_instance>), - quote!(<#traitinstance: #traittype, #instance #bound_instantiable>), - quote!(<#traitinstance, #instance>), - quote!(), - quote!(), - ) - } else { - // need phantom data - ( - quote!(<#traitinstance: #traittype, #instance #bound_instantiable #equal_default_instance>), - quote!(<#traitinstance: #traittype, #instance #bound_instantiable>), - quote!(<#traitinstance, #instance>), + 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), + ) + }; - quote!{ - #[serde(skip)] - pub _genesis_phantom_data: #scrate::rstd::marker::PhantomData<(#traitinstance #comma_instance)>, - }, - quote!{ - _genesis_phantom_data: Default::default(), - }, - ) - } + 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!(), quote!()) + ( + quote!(), + quote!(), + quote!(), + quote!(<#traitinstance: #traittype, #inherent_instance #inherent_bound_instantiable>), + ) }; - quote!{ + 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 { - #ph_field + pub struct GenesisConfig#fparam_struct #genesis_where_clause { #config_field #genesis_extrafields } #[cfg(feature = "std")] - impl#fparam_impl Default for GenesisConfig#sparam { + impl#fparam_impl Default for GenesisConfig#sparam #genesis_where_clause { fn default() -> Self { GenesisConfig { - #ph_default #config_field_default #genesis_extrafields_default } @@ -449,23 +532,83 @@ fn decl_store_extra_genesis( } #[cfg(feature = "std")] - impl#fparam_impl #scrate::runtime_primitives::BuildStorage for GenesisConfig#sparam { - fn assimilate_storage(self, r: &mut #scrate::runtime_primitives::StorageOverlay, c: &mut #scrate::runtime_primitives::ChildrenStorageOverlay) -> ::std::result::Result<(), String> { + impl#fparam_impl GenesisConfig#sparam #genesis_where_clause { + pub fn build_storage #fn_generic (self) -> std::result::Result< + ( + #scrate::runtime_primitives::StorageOverlay, + #scrate::runtime_primitives::ChildrenStorageOverlay, + ), + String + > #fn_where_clause { + let mut storage = Default::default(); + let mut child_storage = Default::default(); + self.assimilate_storage::<#fn_traitinstance>(&mut storage, &mut child_storage)?; + Ok((storage, child_storage)) + } + + /// Assimilate the storage for this module into pre-existing overlays. + pub fn assimilate_storage #fn_generic ( + self, + r: &mut #scrate::runtime_primitives::StorageOverlay, + c: &mut #scrate::runtime_primitives::ChildrenStorageOverlay, + ) -> std::result::Result<(), String> #fn_where_clause { let storage = r; #builders - let r = storage; - - #scall(r, c, &self); + #scall(storage, c, &self); Ok(()) } } - } + + #[cfg(feature = "std")] + impl#build_storage_impl #scrate::runtime_primitives::#impl_trait + for GenesisConfig#sparam #build_storage_where_clause + { + fn build_module_genesis_storage( + self, + r: &mut #scrate::runtime_primitives::StorageOverlay, + c: &mut #scrate::runtime_primitives::ChildrenStorageOverlay, + ) -> std::result::Result<(), String> { + self.assimilate_storage::<#fn_traitinstance> (r, c) + } + } + }; + + Ok(res) } else { - quote!() - }) + Ok(quote!()) + } +} + +fn create_and_impl_instance( + prefix: &str, + ident: &Ident, + doc: &TokenStream2, + const_names: &[(Ident, String)], + scrate: &TokenStream2, + instantiable: &Ident, +) -> TokenStream2 { + let mut const_impls = TokenStream2::new(); + + for (const_name, partial_const_value) in const_names { + let const_value = format!("{}{}", partial_const_value, prefix); + const_impls.extend(quote! { + const #const_name: &'static str = #const_value; + }); + } + + 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_impls + } + } } fn decl_storage_items( @@ -475,6 +618,7 @@ fn decl_storage_items( instance_opts: &InstanceOpts, cratename: &Ident, storage_lines: &ext::Punctuated, + where_clause: &Option, ) -> TokenStream2 { let mut impls = TokenStream2::new(); @@ -489,80 +633,101 @@ fn decl_storage_items( let build_prefix = |cratename, name| format!("{} {}", cratename, name); // Build Instantiable trait - if instance.is_some() { - let mut const_names = vec![]; + let mut const_names = vec![]; - for sline in storage_lines.inner.iter() { - let DeclStorageLine { - storage_type, - name, - .. - } = sline; + for sline in storage_lines.inner.iter() { + let DeclStorageLine { + storage_type, + name, + .. + } = sline; - let prefix = build_prefix(cratename, name); + let prefix = build_prefix(cratename, name); - let type_infos = get_type_infos(storage_type); + 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()); - let partial_const_value = prefix.clone(); - const_names.push((const_name, partial_const_value)); + let const_name = syn::Ident::new( + &format!("{}{}", impls::PREFIX_FOR, name.to_string()), proc_macro2::Span::call_site() + ); + let partial_const_value = prefix.clone(); + const_names.push((const_name, partial_const_value)); - 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()); - let partial_const_value = format!("head of {}", prefix); - const_names.push((const_name, partial_const_value)); - } + 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() + ); + let partial_const_value = format!("head of {}", prefix); + const_names.push((const_name, partial_const_value)); } + } - // 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 instantiable = instantiable + .clone() + .unwrap_or_else(|| Ident::new(DEFAULT_INSTANTIABLE_TRAIT_NAME, Span::call_site())); - impls.extend(quote! { - /// Tag a type as an instance of a module. - /// - /// Defines storage prefixes, they must be unique. - pub trait #instantiable: 'static { - #const_impls - } + // 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 { + #const_impls + } + }); + } + + if instance.is_some() { let instances = (0..NUMBER_OF_INSTANCE) .map(|i| { let name = format!("Instance{}", i); - let ident = syn::Ident::new(&name, proc_macro2::Span::call_site()); + 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"]}))); + .chain( + default_instance + .clone() + .map(|ident| + (String::new(), ident, quote! {#[doc=r"Default module instance"]}) + ) + ); // Impl Instance trait for instances for (prefix, ident, doc) in instances { - let mut const_impls = TokenStream2::new(); + impls.extend( + create_and_impl_instance(&prefix, &ident, &doc, &const_names, scrate, &instantiable) + ); + } + } - for (const_name, partial_const_value) in &const_names { - let const_value = format!("{}{}", partial_const_value, prefix); - const_impls.extend(quote! { - const #const_name: &'static str = #const_value; - }); - } + // The name of the inherently available instance. + let inherent_instance = Ident::new(INHERENT_INSTANCE_NAME, Span::call_site()); - impls.extend(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_impls - } - }); - } + 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 + ) + ); } for sline in storage_lines.inner.iter() { @@ -593,6 +758,7 @@ fn decl_storage_items( prefix: build_prefix(cratename, name), name, attrs, + where_clause, }; let implementation = match kind { @@ -630,15 +796,39 @@ fn impl_store_items( instance: &Option, storage_lines: &ext::Punctuated, ) -> TokenStream2 { - storage_lines.inner.iter().map(|sline| &sline.name) - .fold(TokenStream2::new(), |mut items, name| { + 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<#traitinstance, #instance>; + type #name = #name<#struct_trait #instance>; ) ); items - }) + }) } fn impl_store_fns( @@ -669,29 +859,63 @@ fn impl_store_fns( 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<#traitinstance, #instance> as #scrate::storage::hashed::generator::StorageValue<#typ>> :: get(&#scrate::storage::RuntimeStorage) + <#name<#struct_trait #instance> as + #scrate::storage::hashed::generator::StorageValue<#typ>> :: get( + &#scrate::storage::RuntimeStorage + ) } } }, DeclStorageTypeInfosKind::Map { key_type, .. } => { + let struct_trait = if ext::type_contains_ident(&type_infos.value_type, traitinstance) + || ext::type_contains_ident(key_type, traitinstance) + { + quote!(#traitinstance,) + } else { + quote!() + }; + quote!{ #( #[ #attrs ] )* pub fn #get_fn>(key: K) -> #value_type { - <#name<#traitinstance, #instance> as #scrate::storage::hashed::generator::StorageMap<#key_type, #typ>> :: get(key.borrow(), &#scrate::storage::RuntimeStorage) + < + #name<#struct_trait #instance> as + #scrate::storage::hashed::generator::StorageMap<#key_type, #typ> + >::get(key.borrow(), &#scrate::storage::RuntimeStorage) } } } 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 + pub fn #get_fn(k1: &KArg1, k2: &KArg2) -> #value_type where - KArg1: #scrate::rstd::borrow::Borrow<#key1_type>, - KArg2: #scrate::rstd::borrow::Borrow<#key2_type>, + #key1_type: #scrate::rstd::borrow::Borrow, + #key2_type: #scrate::rstd::borrow::Borrow, + KArg1: ?Sized + #scrate::codec::Encode, + KArg2: ?Sized + #scrate::codec::Encode, { - <#name<#traitinstance> as #scrate::storage::unhashed::generator::StorageDoubleMap<#key1_type, #key2_type, #typ>> :: get(k1.borrow(), k2.borrow(), &#scrate::storage::RuntimeStorage) + < + #name<#struct_trait #instance> as + #scrate::storage::unhashed::generator::StorageDoubleMap<#key1_type, #key2_type, #typ> + >::get(k1, k2, &#scrate::storage::RuntimeStorage) } } } @@ -708,6 +932,7 @@ fn store_functions_to_metadata ( traittype: &syn::TypeParamBound, instance_opts: &InstanceOpts, storage_lines: &ext::Punctuated, + where_clause: &Option, ) -> (TokenStream2, TokenStream2) { let InstanceOpts { @@ -737,7 +962,7 @@ fn store_functions_to_metadata ( let stype = match type_infos.kind { DeclStorageTypeInfosKind::Simple => { quote!{ - #scrate::metadata::StorageFunctionType::Plain( + #scrate::metadata::StorageEntryType::Plain( #scrate::metadata::DecodeDifferent::Encode(#styp), ) } @@ -746,7 +971,7 @@ fn store_functions_to_metadata ( let hasher = hasher.into_metadata(); let kty = clean_type_string("e!(#key_type).to_string()); quote!{ - #scrate::metadata::StorageFunctionType::Map { + #scrate::metadata::StorageEntryType::Map { hasher: #scrate::metadata::#hasher, key: #scrate::metadata::DecodeDifferent::Encode(#kty), value: #scrate::metadata::DecodeDifferent::Encode(#styp), @@ -760,7 +985,7 @@ fn store_functions_to_metadata ( let k2ty = clean_type_string("e!(#key2_type).to_string()); let k2_hasher = key2_hasher.into_metadata(); quote!{ - #scrate::metadata::StorageFunctionType::DoubleMap { + #scrate::metadata::StorageEntryType::DoubleMap { hasher: #scrate::metadata::#hasher, key1: #scrate::metadata::DecodeDifferent::Encode(#k1ty), key2: #scrate::metadata::DecodeDifferent::Encode(#k2ty), @@ -772,11 +997,11 @@ fn store_functions_to_metadata ( }; let modifier = if type_infos.is_option { quote!{ - #scrate::metadata::StorageFunctionModifier::Optional + #scrate::metadata::StorageEntryModifier::Optional } } else { quote!{ - #scrate::metadata::StorageFunctionModifier::Default + #scrate::metadata::StorageEntryModifier::Default } }; let default = default_value.inner.as_ref().map(|d| &d.expr) @@ -799,8 +1024,9 @@ fn store_functions_to_metadata ( 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::StorageFunctionMetadata { + #scrate::metadata::StorageEntryMetadata { name: #scrate::metadata::DecodeDifferent::Encode(#str_name), modifier: #modifier, ty: #stype, @@ -813,14 +1039,21 @@ fn store_functions_to_metadata ( }, }; 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)>); + 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::INIT; + #[cfg(feature = "std")] - impl<#traitinstance: #traittype, #instance #bound_instantiable> #scrate::metadata::DefaultByte for #struct_name<#traitinstance, #instance> { + 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(|| { @@ -829,8 +1062,11 @@ fn store_functions_to_metadata ( }).clone() } } + #[cfg(not(feature = "std"))] - impl<#traitinstance: #traittype, #instance #bound_instantiable> #scrate::metadata::DefaultByte for #struct_name<#traitinstance, #instance> { + 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; @@ -838,6 +1074,7 @@ fn store_functions_to_metadata ( } } }; + default_getter_struct_def.extend(def_get); } (default_getter_struct_def, quote!{ @@ -874,15 +1111,6 @@ enum DeclStorageTypeInfosKind<'a> { } } -impl<'a> DeclStorageTypeInfosKind<'a> { - fn is_simple(&self) -> bool { - match *self { - DeclStorageTypeInfosKind::Simple => true, - _ => false, - } - } -} - fn get_type_infos(storage_type: &DeclStorageType) -> DeclStorageTypeInfos { let (value_type, kind) = match storage_type { DeclStorageType::Simple(ref st) => (st, DeclStorageTypeInfosKind::Simple), @@ -919,29 +1147,29 @@ fn get_type_infos(storage_type: &DeclStorageType) -> DeclStorageTypeInfos { #[derive(Default)] pub(crate) struct InstanceOpts { - pub instance: Option, - pub default_instance: Option, - pub instantiable: Option, + 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, -) -> syn::Result { - + 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_def) => { - let (equal_default_instance, default_instance) = if let Some(default_instance) = default_instance_def { - (quote!{= #default_instance}, Some(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) + (quote!(), None) }; + Ok(InstanceOpts { comma_instance: quote!{, #instance}, equal_default_instance, @@ -952,8 +1180,35 @@ fn get_instance_opts( }) }, (None, None, None) => Ok(Default::default()), - (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))), + (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 01177b9b3ed77ad3e7ba7f06bc56f0122947e15f..1033ebcce2de5b7da84e3d7ebc21bc0220ae4bfe 100644 --- a/srml/support/procedural/tools/src/syn_ext.rs +++ b/srml/support/procedural/tools/src/syn_ext.rs @@ -18,22 +18,17 @@ //! Extension to syn types, mainly for parsing // end::description[] -use syn::parse::{ - Parse, - ParseStream, - Result, -}; -use proc_macro2::TokenStream as T2; +use syn::{visit::{Visit, self}, parse::{Parse, ParseStream, Result}, Ident}; +use proc_macro2::{TokenStream, TokenTree}; use quote::{ToTokens, quote}; use std::iter::once; -use syn::Ident; use srml_support_procedural_tools_derive::{ToTokens, Parse}; /// stop parsing here getting remaining token as content /// Warn duplicate stream (part of) #[derive(Parse, ToTokens, Debug)] pub struct StopParse { - pub inner: T2, + pub inner: TokenStream, } // inner macro really dependant on syn naming convention, do not export @@ -55,8 +50,8 @@ macro_rules! groups_impl { } impl ToTokens for $name

{ - fn to_tokens(&self, tokens: &mut T2) { - let mut inner_stream = T2::new(); + fn to_tokens(&self, tokens: &mut TokenStream) { + let mut inner_stream = TokenStream::new(); self.content.to_tokens(&mut inner_stream); let token_tree: proc_macro2::TokenTree = proc_macro2::Group::new(proc_macro2::Delimiter::$deli, inner_stream).into(); @@ -107,7 +102,7 @@ impl Parse for PunctuatedInner { } impl ToTokens for PunctuatedInner { - fn to_tokens(&self, tokens: &mut T2) { + fn to_tokens(&self, tokens: &mut TokenStream) { self.inner.to_tokens(tokens) } } @@ -127,7 +122,7 @@ impl Parse for Meta { } impl ToTokens for Meta { - fn to_tokens(&self, tokens: &mut T2) { + fn to_tokens(&self, tokens: &mut TokenStream) { match self.inner { syn::Meta::Word(ref ident) => { let ident = ident.clone(); @@ -157,7 +152,7 @@ impl Parse for OuterAttributes { } impl ToTokens for OuterAttributes { - fn to_tokens(&self, tokens: &mut T2) { + fn to_tokens(&self, tokens: &mut TokenStream) { for att in self.inner.iter() { att.to_tokens(tokens); } @@ -182,121 +177,80 @@ impl Parse for Opt

{ } impl ToTokens for Opt

{ - fn to_tokens(&self, tokens: &mut T2) { + fn to_tokens(&self, tokens: &mut TokenStream) { if let Some(ref p) = self.inner { p.to_tokens(tokens); } } } -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 { - path.path.segments.last().and_then(|v| { - if v.value().ident == "Option" { - if let syn::PathArguments::AngleBracketed(ref a) = v.value().arguments { - let args = &a.args; - Some(quote!{ #args }) - } else { - None - } - } else { - None + 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 }) } - }) - } else { - None + } } -} -pub fn is_parametric_type_def(typ: &syn::Type, default: bool) -> bool { - match *typ { - syn::Type::Path(ref path) => { - path.path.segments.iter().any(|v| { - if let syn::PathArguments::AngleBracketed(..) = v.arguments { - true - } else { - false - } - }) - }, - syn::Type::Slice(ref inner) => is_parametric_type_def(&inner.elem, default), - syn::Type::Array(ref inner) => is_parametric_type_def(&inner.elem, default), - syn::Type::Ptr(ref inner) => is_parametric_type_def(&inner.elem, default), - syn::Type::Reference(ref inner) => is_parametric_type_def(&inner.elem, default), - syn::Type::BareFn(ref inner) => inner.variadic.is_some(), - syn::Type::Never(..) => false, - syn::Type::Tuple(ref inner) => - inner.elems.iter().any(|t| is_parametric_type_def(t, default)), - syn::Type::TraitObject(..) => true, - syn::Type::ImplTrait(..) => true, - syn::Type::Paren(ref inner) => is_parametric_type_def(&inner.elem, default), - syn::Type::Group(ref inner) => is_parametric_type_def(&inner.elem, default), - syn::Type::Infer(..) => true, - syn::Type::Macro(..) => default, - syn::Type::Verbatim(..) => default, - } + None } -/// check if type has any type parameter, defaults to true for some cases. -pub fn is_parametric_type(typ: &syn::Type) -> bool { - is_parametric_type_def(typ, true) +/// Auxialary structure to check if a given `Ident` is contained in an ast. +struct ContainsIdent<'a> { + ident: &'a Ident, + result: bool, } -fn has_parametric_type_def_in_path(path: &syn::Path, ident: &Ident, default: bool) -> bool { - path.segments.iter().any(|v| { - if ident == &v.ident { - return true; - } - if let syn::PathArguments::AngleBracketed(ref a) = v.arguments { - for arg in a.args.iter() { - if let syn::GenericArgument::Type(ref typ) = arg { - if has_parametric_type_def(typ, ident, default) { - return true; - } - } - // potentially missing matches here +impl<'ast> ContainsIdent<'ast> { + fn visit_tokenstream(&mut self, stream: TokenStream) { + stream.into_iter().for_each(|tt| + match tt { + TokenTree::Ident(id) => self.visit_ident(&id), + TokenTree::Group(ref group) => self.visit_tokenstream(group.stream()), + _ => {} } - false - } else { - false - } - }) + ) + } + fn visit_ident(&mut self, ident: &Ident) { + if ident == self.ident { + self.result = true; + } + } } -pub fn has_parametric_type_def(typ: &syn::Type, ident: &Ident, default: bool) -> bool { - match *typ { - syn::Type::Path(ref path) => has_parametric_type_def_in_path(&path.path, ident, default), - syn::Type::Slice(ref inner) => has_parametric_type_def(&inner.elem, ident, default), - syn::Type::Array(ref inner) => has_parametric_type_def(&inner.elem, ident, default), - syn::Type::Ptr(ref inner) => has_parametric_type_def(&inner.elem, ident, default), - syn::Type::Reference(ref inner) => has_parametric_type_def(&inner.elem, ident, default), - syn::Type::BareFn(ref inner) => inner.variadic.is_some(), - syn::Type::Never(..) => false, - syn::Type::Tuple(ref inner) => - inner.elems.iter().any(|t| has_parametric_type_def(t, ident, default)), - syn::Type::TraitObject(ref to) => { - to.bounds.iter().any(|bound| { - if let syn::TypeParamBound::Trait(ref t) = bound { - has_parametric_type_def_in_path(&t.path, ident, default) - } else { false } - }) - }, - syn::Type::ImplTrait(ref it) => { - it.bounds.iter().any(|bound| { - if let syn::TypeParamBound::Trait(ref t) = bound { - has_parametric_type_def_in_path(&t.path, ident, default) - } else { false } - }) - }, - syn::Type::Paren(ref inner) => has_parametric_type_def(&inner.elem, ident, default), - syn::Type::Group(ref inner) => has_parametric_type_def(&inner.elem, ident, default), - syn::Type::Infer(..) => default, - syn::Type::Macro(..) => default, - syn::Type::Verbatim(..) => default, + +impl<'ast> Visit<'ast> for ContainsIdent<'ast> { + fn visit_ident(&mut self, input: &'ast Ident) { + self.visit_ident(input); + } + + fn visit_macro(&mut self, input: &'ast syn::Macro) { + self.visit_tokenstream(input.tts.clone()); + visit::visit_macro(self, input); } } -/// check if type has a type parameter, defaults to true for some cases. -pub fn has_parametric_type(typ: &syn::Type, ident: &Ident) -> bool { - has_parametric_type_def(typ, ident, true) +/// Check if a `Type` contains the given `Ident`. +pub fn type_contains_ident(typ: &syn::Type, ident: &Ident) -> bool { + let mut visit = ContainsIdent { + result: false, + ident, + }; + + visit::visit_type(&mut visit, typ); + visit.result } + +/// Check if a `Expr` contains the given `Ident`. +pub fn expr_contains_ident(expr: &syn::Expr, ident: &Ident) -> bool { + let mut visit = ContainsIdent { + result: false, + ident, + }; + + visit::visit_expr(&mut visit, expr); + visit.result +} \ No newline at end of file diff --git a/srml/support/src/dispatch.rs b/srml/support/src/dispatch.rs index 37e40058252dd68c67a82af8946f1841cb8c6a69..7afbd11cd1d0d5851d9cf431b20553439af620ac 100644 --- a/srml/support/src/dispatch.rs +++ b/srml/support/src/dispatch.rs @@ -17,41 +17,36 @@ //! Dispatch system. Contains a macro for defining runtime modules and //! generating values representing lazy module function calls. -pub use crate::rstd::prelude::{Vec, Clone, Eq, PartialEq}; +pub use crate::rstd::{result, prelude::{Vec, Clone, Eq, PartialEq}, marker}; #[cfg(feature = "std")] pub use std::fmt; -pub use crate::rstd::result; pub use crate::codec::{Codec, Decode, Encode, Input, Output, HasCompact, EncodeAsRef}; -pub use srml_metadata::{FunctionMetadata, DecodeDifferent, DecodeDifferentArray, FunctionArgumentMetadata}; -pub use sr_primitives::weights::{TransactionWeight, Weighable, Weight}; +pub use srml_metadata::{ + FunctionMetadata, DecodeDifferent, DecodeDifferentArray, FunctionArgumentMetadata, + ModuleConstantMetadata, DefaultByte, DefaultByteGetter, +}; +pub use sr_primitives::weights::{SimpleDispatchInfo, GetDispatchInfo, DispatchInfo, WeighData, + ClassifyDispatch, + TransactionPriority +}; +pub use sr_primitives::traits::{Dispatchable, DispatchResult}; /// A type that cannot be instantiated. pub enum Never {} /// Result of a module function call; either nothing (functions are only called for "side effects") /// or an error message. -pub type Result = result::Result<(), &'static str>; - -/// A lazy call (module function and argument values) that can be executed via its `dispatch` -/// method. -pub trait Dispatchable { - /// Every function call from your runtime has an origin, which specifies where the extrinsic was - /// generated from. In the case of a signed extrinsic (transaction), the origin contains an - /// identifier for the caller. The origin can be empty in the case of an inherent extrinsic. - type Origin; - type Trait; - fn dispatch(self, origin: Self::Origin) -> Result; -} +pub type Result = DispatchResult; /// Serializable version of Dispatchable. /// This value can be used as a "function" in an extrinsic. -pub trait Callable { +pub trait Callable { type Call: Dispatchable + Codec + Clone + PartialEq + Eq; } // dirty hack to work around serde_derive issue // https://github.com/rust-lang/rust/issues/51331 -pub type CallableCallFor = ::Call; +pub type CallableCallFor = >::Call; #[cfg(feature = "std")] pub trait Parameter: Codec + Clone + Eq + fmt::Debug {} @@ -102,6 +97,8 @@ impl Parameter for T where T: Codec + Clone + Eq {} /// * `origin`: Alias of `T::Origin`, declared by the [`impl_outer_origin!`](./macro.impl_outer_origin.html) macro. /// * `Result`: The expected return type from module functions. /// +/// The first parameter of dispatchable functions must always be `origin`. +/// /// ### Shorthand Example /// /// The macro automatically expands a shorthand function declaration to return the `Result` type. @@ -130,8 +127,7 @@ impl Parameter for T where T: Codec + Clone + Eq {} /// /// ### Privileged Function Example /// -/// If the `origin` param is omitted, the macro adds it as the first parameter and adds `ensure_root(origin)` -/// as the first line of the function. These functions are the same: +/// A privileged function checks that the origin of the call is `ROOT`. /// /// ``` /// # #[macro_use] @@ -140,14 +136,8 @@ impl Parameter for T where T: Codec + Clone + Eq {} /// # use srml_system::{self as system, Trait, ensure_signed, ensure_root}; /// decl_module! { /// pub struct Module for enum Call where origin: T::Origin { -/// -/// fn my_privileged_function() -> Result { -/// // Your implementation -/// Ok(()) -/// } -/// -/// fn my_function(origin) -> Result { -/// ensure_root(origin); +/// fn my_privileged_function(origin) -> Result { +/// ensure_root(origin)?; /// // Your implementation /// Ok(()) /// } @@ -181,6 +171,28 @@ impl Parameter for T where T: Codec + Clone + Eq {} /// # fn main() {} /// ``` /// +/// ## Where clause +/// +/// Besides the default `origin: T::Origin`, you can also pass other bounds to the module declaration. +/// This where bound will be replicated to all types generated by this macro. The chaining of multiple +/// trait bounds with `+` is not supported. If multiple bounds for one type are required, it needs to +/// be split up into multiple bounds. +/// +/// ``` +/// # #[macro_use] +/// # extern crate srml_support; +/// # use srml_support::dispatch::Result; +/// # use srml_system::{self as system, ensure_signed}; +/// pub trait Trait: system::Trait where Self::AccountId: From {} +/// +/// decl_module! { +/// pub struct Module for enum Call where origin: T::Origin, T::AccountId: From { +/// // Your implementation +/// } +/// } +/// # fn main() {} +/// ``` +/// /// ## Reserved Functions /// /// The following are reserved function signatures: @@ -204,15 +216,22 @@ macro_rules! decl_module { // Entry point #1. ( $(#[$attr:meta])* - pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident$(, I: $instantiable:path $(= $module_default_instance:path)?)?> - for enum $call_type:ident where origin: $origin_type:ty { - $($t:tt)* + pub struct $mod_type:ident< + $trait_instance:ident: $trait_name:ident + $( , I: $instantiable:path $( = $module_default_instance:path )? )? + > + for enum $call_type:ident where origin: $origin_type:ty $(, $where_ty:ty: $where_bound:path )* { + $( $t:tt )* } ) => { $crate::decl_module!(@normalize $(#[$attr])* - pub struct $mod_type<$trait_instance: $trait_name$(, I: $instantiable $(= $module_default_instance)?)?> + pub struct $mod_type< + $trait_instance: $trait_name $(, I: $instantiable $(= $module_default_instance)?)? + > for enum $call_type where origin: $origin_type, system = system + { $( $where_ty: $where_bound ),* } + {} {} {} {} @@ -224,15 +243,26 @@ macro_rules! decl_module { // Entry point #2. ( $(#[$attr:meta])* - pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident$(, I: $instantiable:path $(= $module_default_instance:path)?)?> - for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident { + pub struct $mod_type:ident< + $trait_instance:ident: $trait_name:ident + $( , I: $instantiable:path $( = $module_default_instance:path )? )? + > + for enum $call_type:ident where + origin: $origin_type:ty, + system = $system:ident + $(, $where_ty:ty: $where_bound:path )* + { $($t:tt)* } ) => { $crate::decl_module!(@normalize $(#[$attr])* - pub struct $mod_type<$trait_instance: $trait_name$(, I: $instantiable $(= $module_default_instance)?)?> + pub struct $mod_type< + $trait_instance: $trait_name $(, I: $instantiable $( = $module_default_instance )? )? + > for enum $call_type where origin: $origin_type, system = $system + { $( $where_ty: $where_bound ),* } + {} {} {} {} @@ -247,11 +277,13 @@ macro_rules! decl_module { $(#[$attr:meta])* pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident$(, I: $instantiable:path $(= $module_default_instance:path)?)?> for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident + { $( $other_where_bounds:tt )* } {} { $( $on_initialize:tt )* } { $( $on_finalize:tt )* } { $( $offchain:tt )* } - [ $($t:tt)* ] + { $( $constants:tt )* } + [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* $vis:vis fn deposit_event $(<$dpeg:ident $(, $dpeg_instance:ident)?>)* () = default; $($rest:tt)* @@ -260,11 +292,13 @@ macro_rules! decl_module { $(#[$attr])* pub struct $mod_type<$trait_instance: $trait_name$(, I: $instantiable $(= $module_default_instance)?)?> for enum $call_type where origin: $origin_type, system = $system + { $( $other_where_bounds )* } { $vis fn deposit_event $(<$dpeg $(, $dpeg_instance)?>)* () = default; } { $( $on_initialize )* } { $( $on_finalize )* } { $( $offchain )* } - [ $($t)* ] + { $( $constants )* } + [ $( $dispatchables )* ] $($rest)* ); }; @@ -272,11 +306,13 @@ macro_rules! decl_module { $(#[$attr:meta])* pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident$(, I: $instantiable:path $(= $module_default_instance:path)?)?> for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident + { $( $other_where_bounds:tt )* } {} { $( $on_initialize:tt )* } { $( $on_finalize:tt )* } { $( $offchain:tt )* } - [ $($t:tt)* ] + { $( $constants:tt )* } + [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* $vis:vis fn deposit_event $(<$dpeg:ident $(, $dpeg_instance:ident)?>)* ( $($param_name:ident : $param:ty),* @@ -287,11 +323,13 @@ macro_rules! decl_module { $(#[$attr])* pub struct $mod_type<$trait_instance: $trait_name$(, I: $instantiable $(= $module_default_instance)?)?> for enum $call_type where origin: $origin_type, system = $system + { $( $other_where_bounds )* } { $vis fn deposit_event $(<$dpeg $(, $dpeg_instance)?>)* ($( $param_name: $param ),* ) { $( $impl )* } } { $( $on_initialize )* } { $( $on_finalize )* } { $( $offchain )* } - [ $($t)* ] + { $( $constants )* } + [ $( $dispatchables )* ] $($rest)* ); }; @@ -299,11 +337,13 @@ macro_rules! decl_module { $(#[$attr:meta])* pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident$(, I: $instantiable:path $(= $module_default_instance:path)?)?> for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident + { $( $other_where_bounds:tt )* } { $( $deposit_event:tt )* } { $( $on_initialize:tt )* } {} { $( $offchain:tt )* } - [ $($t:tt)* ] + { $( $constants:tt )* } + [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* fn on_finalize($($param_name:ident : $param:ty),* ) { $( $impl:tt )* } $($rest:tt)* @@ -312,11 +352,13 @@ macro_rules! decl_module { $(#[$attr])* pub struct $mod_type<$trait_instance: $trait_name$(, I: $instantiable $(= $module_default_instance)?)?> for enum $call_type where origin: $origin_type, system = $system + { $( $other_where_bounds )* } { $( $deposit_event )* } { $( $on_initialize )* } { fn on_finalize( $( $param_name : $param ),* ) { $( $impl )* } } { $( $offchain )* } - [ $($t)* ] + { $( $constants )* } + [ $( $dispatchables )* ] $($rest)* ); }; @@ -324,11 +366,13 @@ macro_rules! decl_module { $(#[$attr:meta])* pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident$(, I: $instantiable:path $(= $module_default_instance:path)?)?> for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident + { $( $other_where_bounds:tt )* } { $( $deposit_event:tt )* } { $( $on_initialize:tt )* } {} { $( $offchain:tt )* } - [ $($t:tt)* ] + { $( $constants:tt )* } + [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* fn on_finalise($($param_name:ident : $param:ty),* ) { $( $impl:tt )* } $($rest:tt)* @@ -341,11 +385,13 @@ macro_rules! decl_module { $(#[$attr:meta])* pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident$(, I: $instantiable:path $(= $module_default_instance:path)?)?> for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident + { $( $other_where_bounds:tt )* } { $( $deposit_event:tt )* } {} { $( $on_finalize:tt )* } { $( $offchain:tt )* } - [ $($t:tt)* ] + { $( $constants:tt )* } + [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* fn on_initialize($($param_name:ident : $param:ty),* ) { $( $impl:tt )* } $($rest:tt)* @@ -354,11 +400,13 @@ macro_rules! decl_module { $(#[$attr])* pub struct $mod_type<$trait_instance: $trait_name$(, I: $instantiable $(= $module_default_instance)?)?> for enum $call_type where origin: $origin_type, system = $system + { $( $other_where_bounds )* } { $( $deposit_event )* } { fn on_initialize( $( $param_name : $param ),* ) { $( $impl )* } } { $( $on_finalize )* } { $( $offchain )* } - [ $($t)* ] + { $( $constants )* } + [ $( $dispatchables )* ] $($rest)* ); }; @@ -366,11 +414,13 @@ macro_rules! decl_module { $(#[$attr:meta])* pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident$(, I: $instantiable:path $(= $module_default_instance:path)?)?> for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident + { $( $other_where_bounds:tt )* } { $( $deposit_event:tt )* } {} { $( $on_finalize:tt )* } { $( $offchain:tt )* } - [ $($t:tt)* ] + { $( $constants:tt )* } + [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* fn on_initialise($($param_name:ident : $param:ty),* ) { $( $impl:tt )* } $($rest:tt)* @@ -381,42 +431,95 @@ macro_rules! decl_module { }; (@normalize $(#[$attr:meta])* - pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident> + pub struct $mod_type:ident< + $trait_instance:ident: $trait_name:ident + $(, I: $instantiable:path $(= $module_default_instance:path)?)? + > for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident + { $( $other_where_bounds:tt )* } { $( $deposit_event:tt )* } { $( $on_initialize:tt )* } { $( $on_finalize:tt )* } { } - [ $($t:tt)* ] + { $( $constants:tt )* } + [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* fn offchain_worker($($param_name:ident : $param:ty),* ) { $( $impl:tt )* } $($rest:tt)* ) => { $crate::decl_module!(@normalize $(#[$attr])* - pub struct $mod_type<$trait_instance: $trait_name> + pub struct $mod_type< + $trait_instance: $trait_name$(, I: $instantiable $(= $module_default_instance)?)? + > for enum $call_type where origin: $origin_type, system = $system + { $( $other_where_bounds )* } { $( $deposit_event )* } { $( $on_initialize )* } { $( $on_finalize )* } { fn offchain_worker( $( $param_name : $param ),* ) { $( $impl )* } } - [ $($t)* ] + { $( $constants )* } + [ $( $dispatchables )* ] $($rest)* ); }; + + // This puts a constant in the parsed constants list. + (@normalize + $(#[$attr:meta])* + pub struct $mod_type:ident< + $trait_instance:ident: $trait_name:ident + $(, $instance:ident: $instantiable:path $(= $module_default_instance:path)?)? + > + for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident + { $( $other_where_bounds:tt )* } + { $( $deposit_event:tt )* } + { $( $on_initialize:tt )* } + { $( $on_finalize:tt )* } + { $( $offchain:tt )* } + { $( $constants:tt )* } + [ $( $dispatchables:tt )* ] + $( #[doc = $doc_attr:tt] )* + const $name:ident: $ty:ty = $value:expr; + $( $rest:tt )* + ) => { + $crate::decl_module!(@normalize + $(#[$attr])* + pub struct $mod_type< + $trait_instance: $trait_name + $( , $instance: $instantiable $(= $module_default_instance)? )? + > + for enum $call_type where origin: $origin_type, system = $system + { $( $other_where_bounds )* } + { $( $deposit_event )* } + { $( $on_initialize )* } + { $( $on_finalize )* } + { $( $offchain )* } + { + $( $constants )* + $( #[doc = $doc_attr ] )* + $name: $ty = $value; + } + [ $( $dispatchables )* ] + $($rest)* + ); + }; + // This puts the function statement into the [], decreasing `$rest` and moving toward finishing the parse. (@normalize $(#[$attr:meta])* pub struct $mod_type:ident< - $trait_instance:ident: - $trait_name:ident$(, $instance:ident: $instantiable:path $(= $module_default_instance:path)?)? + $trait_instance:ident: $trait_name:ident + $(, $instance:ident: $instantiable:path $(= $module_default_instance:path)?)? > for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident + { $( $other_where_bounds:tt )* } { $( $deposit_event:tt )* } { $( $on_initialize:tt )* } { $( $on_finalize:tt )* } { $( $offchain:tt )* } - [ $($t:tt)* ] + { $( $constants:tt )* } + [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* #[weight = $weight:expr] $fn_vis:vis fn $fn_name:ident( @@ -430,12 +533,14 @@ macro_rules! decl_module { $trait_instance: $trait_name$(, $instance: $instantiable $(= $module_default_instance)?)? > for enum $call_type where origin: $origin_type, system = $system + { $( $other_where_bounds )* } { $( $deposit_event )* } { $( $on_initialize )* } { $( $on_finalize )* } { $( $offchain )* } + { $( $constants )* } [ - $($t)* + $( $dispatchables )* $(#[doc = $doc_attr])* #[weight = $weight] $fn_vis fn $fn_name( @@ -454,11 +559,13 @@ macro_rules! decl_module { $trait_name:ident$(, $instance:ident: $instantiable:path $(= $module_default_instance:path)?)? > for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident + { $( $other_where_bounds:tt )* } { $( $deposit_event:tt )* } { $( $on_initialize:tt )* } { $( $on_finalize:tt )* } { $( $offchain:tt )* } - [ $($t:tt)* ] + { $( $constants:tt )* } + [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* $fn_vis:vis fn $fn_name:ident( $from:ident $(, $(#[$codec_attr:ident])* $param_name:ident : $param:ty)* @@ -471,13 +578,15 @@ macro_rules! decl_module { $trait_instance: $trait_name$(, $instance: $instantiable $(= $module_default_instance)?)? > for enum $call_type where origin: $origin_type, system = $system + { $( $other_where_bounds )* } { $( $deposit_event )* } { $( $on_initialize )* } { $( $on_finalize )* } { $( $offchain )* } - [ $($t)* ] + { $( $constants )* } + [ $( $dispatchables )* ] $(#[doc = $doc_attr])* - #[weight = $crate::dispatch::TransactionWeight::default()] + #[weight = $crate::dispatch::SimpleDispatchInfo::default()] $fn_vis fn $fn_name( $from $(, $(#[$codec_attr])* $param_name : $param )* ) $( -> $result )* { $( $impl )* } @@ -489,11 +598,13 @@ macro_rules! decl_module { $(#[$attr:meta])* pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident$(, I: $instantiable:path $(= $module_default_instance:path)?)?> for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident + { $( $other_where_bounds:tt )* } { $( $deposit_event:tt )* } { $( $on_initialize:tt )* } { $( $on_finalize:tt )* } { $( $offchain:tt )* } - [ $($t:tt)* ] + { $( $constants:tt )* } + [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* $(#[weight = $weight:expr])? $fn_vis:vis fn $fn_name:ident( @@ -503,20 +614,21 @@ macro_rules! decl_module { ) => { compile_error!( "First parameter of dispatch should be marked `origin` only, with no type specified \ - (a bit like `self`). (For root-matching dispatches, ensure the first parameter does \ - not use the `T::Origin` type.)" - ) + (a bit like `self`)." + ); }; // Ignore any ident which is `origin` but has a type, regardless of the type token itself. (@normalize $(#[$attr:meta])* pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident$(, I: $instantiable:path $(= $module_default_instance:path)?)?> for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident + { $( $other_where_bounds:tt )* } { $( $deposit_event:tt )* } { $( $on_initialize:tt )* } { $( $on_finalize:tt )* } { $( $offchain:tt )* } - [ $($t:tt)* ] + { $( $constants:tt )* } + [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* $(#[weight = $weight:expr])? $fn_vis:vis fn $fn_name:ident( @@ -526,20 +638,21 @@ macro_rules! decl_module { ) => { compile_error!( "First parameter of dispatch should be marked `origin` only, with no type specified \ - (a bit like `self`). (For root-matching dispatches, ensure the first parameter does \ - not use the `T::Origin` type.)" - ) + (a bit like `self`)." + ); }; - // Add root if no origin is defined. + // Ignore any function missing `origin` as the first parameter. (@normalize $(#[$attr:meta])* pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path $(= $module_default_instance:path)?)?> for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident + { $( $other_where_bounds:tt )* } { $( $deposit_event:tt )* } { $( $on_initialize:tt )* } { $( $on_finalize:tt )* } { $( $offchain:tt )* } - [ $($t:tt)* ] + { $( $constants:tt )* } + [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* $(#[weight = $weight:expr])? $fn_vis:vis fn $fn_name:ident( @@ -547,23 +660,10 @@ macro_rules! decl_module { ) $( -> $result:ty )* { $( $impl:tt )* } $($rest:tt)* ) => { - $crate::decl_module!(@normalize - $(#[$attr])* - pub struct $mod_type<$trait_instance: $trait_name$(, $instance: $instantiable $(= $module_default_instance)?)?> - for enum $call_type where origin: $origin_type, system = $system - { $( $deposit_event )* } - { $( $on_initialize )* } - { $( $on_finalize )* } - { $( $offchain )* } - [ $($t)* ] - - $(#[doc = $doc_attr])* - $(#[weight = $weight])? - $fn_vis fn $fn_name( - root $( , $(#[$codec_attr])* $param_name : $param )* - ) $( -> $result )* { $( $impl )* } - - $($rest)* + compile_error!( + "Implicit conversion to privileged function has been removed. \ + First parameter of dispatch should be marked `origin`. \ + For root-matching dispatch, also add `ensure_root(origin)?`." ); }; // Last normalize step. Triggers `@imp` expansion which is the real expansion. @@ -571,37 +671,32 @@ macro_rules! decl_module { $(#[$attr:meta])* pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident$(, I: $instantiable:path $(= $module_default_instance:path)?)?> for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident + { $( $other_where_bounds:tt )* } { $( $deposit_event:tt )* } { $( $on_initialize:tt )* } { $( $on_finalize:tt )* } { $( $offchain:tt )* } - [ $($t:tt)* ] + { $( $constants:tt )* } + [ $( $dispatchables:tt )* ] ) => { $crate::decl_module!(@imp $(#[$attr])* pub struct $mod_type<$trait_instance: $trait_name$(, I: $instantiable $(= $module_default_instance)?)?> for enum $call_type where origin: $origin_type, system = $system { - $($t)* + $( $dispatchables )* } + { $( $other_where_bounds )* } { $( $deposit_event )* } { $( $on_initialize )* } { $( $on_finalize )* } { $( $offchain )* } + { $( $constants )* } ); }; // Implementation of Call enum's .dispatch() method. // TODO: this probably should be a different macro? - (@call - root - $mod_type:ident<$trait_instance:ident $(, $instance:ident)?> $fn_name:ident $origin:ident $system:ident [ $( $param_name:ident),* ] - ) => { - { - $system::ensure_root($origin)?; - <$mod_type<$trait_instance $(, $instance)?>>::$fn_name( $( $param_name ),* ) - } - }; (@call $ingore:ident $mod_type:ident<$trait_instance:ident $(, $instance:ident)?> $fn_name:ident $origin:ident $system:ident [ $( $param_name:ident),* ] @@ -613,14 +708,18 @@ macro_rules! decl_module { (@impl_deposit_event $module:ident<$trait_instance:ident: $trait_name:ident$(, I: $instantiable:path)?>; $system:ident; + { $( $other_where_bounds:tt )* } ) => {}; (@impl_deposit_event $module:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?>; $system:ident; + { $( $other_where_bounds:tt )* } $vis:vis fn deposit_event$(<$event_trait_instance:ident $(, $event_instance:ident)?>)?() = default; ) => { - impl<$trait_instance: $trait_name$(, $instance: $instantiable)?> $module<$trait_instance $(, $instance)?> { + impl<$trait_instance: $trait_name$(, $instance: $instantiable)?> $module<$trait_instance $(, $instance)?> + where $( $other_where_bounds )* + { $vis fn deposit_event(event: Event$(<$event_trait_instance $(, $event_instance)?>)?) { <$system::Module<$trait_instance>>::deposit_event( <$trait_instance as $trait_name$(<$instance>)?>::Event::from(event).into() @@ -632,9 +731,12 @@ macro_rules! decl_module { (@impl_deposit_event $module:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?>; $system:ident; + { $( $other_where_bounds:tt )* } $vis:vis fn deposit_event($param:ident : $param_ty:ty) { $( $impl:tt )* } ) => { - impl<$trait_instance: $trait_name$(, $instance: $instantiable)?> $module<$trait_instance $(, $instance)?> { + impl<$trait_instance: $trait_name$(, $instance: $instantiable)?> $module<$trait_instance $(, $instance)?> + where $( $other_where_bounds )* + { $vis fn deposit_event($param: $param_ty) { $( $impl )* } @@ -643,11 +745,12 @@ macro_rules! decl_module { (@impl_on_initialize $module:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?>; + { $( $other_where_bounds:tt )* } fn on_initialize() { $( $impl:tt )* } ) => { impl<$trait_instance: $trait_name$(, $instance: $instantiable)?> $crate::runtime_primitives::traits::OnInitialize<$trait_instance::BlockNumber> - for $module<$trait_instance$(, $instance)?> + for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )* { fn on_initialize(_block_number_not_used: $trait_instance::BlockNumber) { $( $impl )* } } @@ -655,11 +758,12 @@ macro_rules! decl_module { (@impl_on_initialize $module:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?>; + { $( $other_where_bounds:tt )* } fn on_initialize($param:ident : $param_ty:ty) { $( $impl:tt )* } ) => { impl<$trait_instance: $trait_name$(, $instance: $instantiable)?> $crate::runtime_primitives::traits::OnInitialize<$trait_instance::BlockNumber> - for $module<$trait_instance$(, $instance)?> + for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )* { fn on_initialize($param: $param_ty) { $( $impl )* } } @@ -667,20 +771,22 @@ macro_rules! decl_module { (@impl_on_initialize $module:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?>; + { $( $other_where_bounds:tt )* } ) => { impl<$trait_instance: $trait_name$(, $instance: $instantiable)?> $crate::runtime_primitives::traits::OnInitialize<$trait_instance::BlockNumber> - for $module<$trait_instance$(, $instance)?> + for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )* {} }; (@impl_on_finalize $module:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?>; + { $( $other_where_bounds:tt )* } fn on_finalize() { $( $impl:tt )* } ) => { impl<$trait_instance: $trait_name$(, $instance: $instantiable)?> $crate::runtime_primitives::traits::OnFinalize<$trait_instance::BlockNumber> - for $module<$trait_instance$(, $instance)?> + for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )* { fn on_finalize(_block_number_not_used: $trait_instance::BlockNumber) { $( $impl )* } } @@ -688,11 +794,12 @@ macro_rules! decl_module { (@impl_on_finalize $module:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?>; + { $( $other_where_bounds:tt )* } fn on_finalize($param:ident : $param_ty:ty) { $( $impl:tt )* } ) => { impl<$trait_instance: $trait_name$(, $instance: $instantiable)?> $crate::runtime_primitives::traits::OnFinalize<$trait_instance::BlockNumber> - for $module<$trait_instance$(, $instance)?> + for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )* { fn on_finalize($param: $param_ty) { $( $impl )* } } @@ -700,21 +807,23 @@ macro_rules! decl_module { (@impl_on_finalize $module:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?>; + { $( $other_where_bounds:tt )* } ) => { impl<$trait_instance: $trait_name$(, $instance: $instantiable)?> $crate::runtime_primitives::traits::OnFinalize<$trait_instance::BlockNumber> - for $module<$trait_instance$(, $instance)?> + for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )* { } }; (@impl_offchain $module:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?>; + { $( $other_where_bounds:tt )* } fn offchain_worker() { $( $impl:tt )* } ) => { impl<$trait_instance: $trait_name$(, $instance: $instantiable)?> $crate::runtime_primitives::traits::OffchainWorker<$trait_instance::BlockNumber> - for $module<$trait_instance$(, $instance)?> + for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )* { fn generate_extrinsics(_block_number_not_used: $trait_instance::BlockNumber) { $( $impl )* } } @@ -722,11 +831,12 @@ macro_rules! decl_module { (@impl_offchain $module:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?>; + { $( $other_where_bounds:tt )* } fn offchain_worker($param:ident : $param_ty:ty) { $( $impl:tt )* } ) => { impl<$trait_instance: $trait_name$(, $instance: $instantiable)?> $crate::runtime_primitives::traits::OffchainWorker<$trait_instance::BlockNumber> - for $module<$trait_instance$(, $instance)?> + for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )* { fn generate_extrinsics($param: $param_ty) { $( $impl )* } } @@ -734,45 +844,14 @@ macro_rules! decl_module { (@impl_offchain $module:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?>; + { $( $other_where_bounds:tt )* } ) => { impl<$trait_instance: $trait_name$(, $instance: $instantiable)?> $crate::runtime_primitives::traits::OffchainWorker<$trait_instance::BlockNumber> - for $module<$trait_instance$(, $instance)?> + for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )* {} }; - // Expansion for root dispatch functions with no specified result type. - (@impl_function - $module:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?>; - $origin_ty:ty; - root; - $(#[doc = $doc_attr:tt])* - $vis:vis fn $name:ident ( root $(, $param:ident : $param_ty:ty )* ) { $( $impl:tt )* } - ) => { - $(#[doc = $doc_attr])* - #[allow(unreachable_code)] - $vis fn $name($( $param: $param_ty ),* ) -> $crate::dispatch::Result { - { $( $impl )* } - Ok(()) - } - }; - - // Expansion for root dispatch functions with explicit return types. - (@impl_function - $module:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?>; - $origin_ty:ty; - root; - $(#[doc = $doc_attr:tt])* - $vis:vis fn $name:ident ( - root $(, $param:ident : $param_ty:ty )* - ) -> $result:ty { $( $impl:tt )* } - ) => { - $(#[doc = $doc_attr])* - $vis fn $name($( $param: $param_ty ),* ) -> $result { - $( $impl )* - } - }; - // Expansion for _origin_ dispatch functions with no return type. (@impl_function $module:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?>; @@ -784,10 +863,12 @@ macro_rules! decl_module { ) { $( $impl:tt )* } ) => { $(#[doc = $doc_attr])* + #[allow(unreachable_code)] $vis fn $name( $origin: $origin_ty $(, $param: $param_ty )* ) -> $crate::dispatch::Result { { $( $impl )* } + // May be unreachable. Ok(()) } }; @@ -813,6 +894,7 @@ macro_rules! decl_module { $( #[$attr:meta] )* $call_type:ident; <$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path $(= $module_default_instance:path)?)?> + { $( $other_where_bounds:tt )* } { $( $generated_variants:tt )* } { $( $current_params:tt )* } variant $fn_name:ident; @@ -826,6 +908,7 @@ macro_rules! decl_module { $( #[$attr] )* $call_type; <$trait_instance: $trait_name $(, $instance: $instantiable $(= $module_default_instance)? )?> + { $( $other_where_bounds )* } { $( $generated_variants )* } { $( $current_params )* @@ -843,6 +926,7 @@ macro_rules! decl_module { $( #[$attr:meta] )* $call_type:ident; <$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path $(= $module_default_instance:path)?)?> + { $( $other_where_bounds:tt )* } { $( $generated_variants:tt )* } { $( $current_params:tt )* } variant $fn_name:ident; @@ -855,6 +939,7 @@ macro_rules! decl_module { $( #[$attr] )* $call_type; <$trait_instance: $trait_name $(, $instance: $instantiable $(= $module_default_instance)? )?> + { $( $other_where_bounds )* } { $( $generated_variants )* } { $( $current_params )* @@ -870,6 +955,7 @@ macro_rules! decl_module { $( #[$attr:meta] )* $call_type:ident; <$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path $(= $module_default_instance:path)?)?> + { $( $other_where_bounds:tt )* } { $( $generated_variants:tt )* } { $( $current_params:tt )* } variant $fn_name:ident; @@ -884,6 +970,7 @@ macro_rules! decl_module { $( #[$attr] )* $call_type; <$trait_instance: $trait_name $(, $instance: $instantiable $(= $module_default_instance)? )?> + { $( $other_where_bounds )* } { $( $generated_variants )* #[allow(non_camel_case_types)] @@ -904,12 +991,15 @@ macro_rules! decl_module { $( #[$attr:meta] )* $call_type:ident; <$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path $(= $module_default_instance:path)?)?> + { $( $other_where_bounds:tt )* } { $( $generated_variants:tt )* } {} ) => { #[derive($crate::codec::Encode, $crate::codec::Decode)] $( #[$attr] )* - pub enum $call_type<$trait_instance: $trait_name$(, $instance: $instantiable $( = $module_default_instance)?)?> { + pub enum $call_type<$trait_instance: $trait_name$(, $instance: $instantiable $( = $module_default_instance)?)?> + where $( $other_where_bounds )* + { #[doc(hidden)] #[codec(skip)] __PhantomItem($crate::rstd::marker::PhantomData<($trait_instance $(, $instance)?)>, $crate::dispatch::Never), @@ -935,14 +1025,14 @@ macro_rules! decl_module { { $($fn_instance:ident: $fn_instantiable:path)? } )* } + { $( $other_where_bounds:tt )* } { $( $deposit_event:tt )* } { $( $on_initialize:tt )* } { $( $on_finalize:tt )* } { $( $offchain:tt )* } + { $( $constants:tt )* } ) => { - $crate::__check_reserved_fn_name! { - $($fn_name)* - } + $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)] @@ -950,36 +1040,43 @@ macro_rules! decl_module { pub struct $mod_type< $trait_instance: $trait_name $(, $instance: $instantiable $( = $module_default_instance)?)? - >($crate::rstd::marker::PhantomData<($trait_instance $(, $instance)?)>); + >($crate::rstd::marker::PhantomData<($trait_instance $(, $instance)?)>) where + $( $other_where_bounds )*; $crate::decl_module! { @impl_on_initialize $mod_type<$trait_instance: $trait_name $(, $instance: $instantiable)?>; + { $( $other_where_bounds )* } $( $on_initialize )* } $crate::decl_module! { @impl_on_finalize $mod_type<$trait_instance: $trait_name $(, $instance: $instantiable)?>; + { $( $other_where_bounds )* } $( $on_finalize )* } $crate::decl_module! { @impl_offchain $mod_type<$trait_instance: $trait_name $(, $instance: $instantiable)?>; + { $( $other_where_bounds )* } $( $offchain )* } $crate::decl_module! { @impl_deposit_event $mod_type<$trait_instance: $trait_name $(, $instance: $instantiable)?>; $system; + { $( $other_where_bounds )* } $( $deposit_event )* } /// Can also be called using [`Call`]. /// /// [`Call`]: enum.Call.html - impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $mod_type<$trait_instance $(, $instance)?> { + impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $mod_type<$trait_instance $(, $instance)?> + where $( $other_where_bounds )* + { $( $crate::decl_module! { @impl_function @@ -999,6 +1096,7 @@ macro_rules! decl_module { $( #[$attr] )* $call_type; <$trait_instance: $trait_name $(, $instance: $instantiable $(= $module_default_instance)? )?> + { $( $other_where_bounds )* } {} {} $( @@ -1012,21 +1110,45 @@ macro_rules! decl_module { } // Implement weight calculation function for Call - impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $crate::dispatch::Weighable - for $call_type<$trait_instance $(, $instance)?> + impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $crate::dispatch::GetDispatchInfo + for $call_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )* { - fn weight(&self, _len: usize) -> $crate::dispatch::Weight { - match self { - $( $call_type::$fn_name(..) => $crate::dispatch::Weighable::weight(&$weight, _len), )* - $call_type::__PhantomItem(_, _) => { unreachable!("__PhantomItem should never be used.") }, - } + fn get_dispatch_info(&self) -> $crate::dispatch::DispatchInfo { + $( + if let $call_type::$fn_name($( ref $param_name ),*) = self { + let weight = >::weigh_data( + &$weight, + ($( $param_name, )*) + ); + let class = >::classify_dispatch( + &$weight, + ($( $param_name, )*) + ); + return $crate::dispatch::DispatchInfo { weight, class }; + } + if let $call_type::__PhantomItem(_, _) = self { unreachable!("__PhantomItem should never be used.") } + )* + // Defensive only: this function must have already returned at this point. + // all dispatchable function will have a weight which has the `::default` + // implementation of `SimpleDispatchInfo`. Nonetheless, we create one if it does + // not exist. + let weight = >::weigh_data( + &$crate::dispatch::SimpleDispatchInfo::default(), + () + ); + let class = >::classify_dispatch( + &$crate::dispatch::SimpleDispatchInfo::default(), + () + ); + $crate::dispatch::DispatchInfo { weight, class } + } } // manual implementation of clone/eq/partialeq because using derive erroneously requires // clone/eq/partialeq from T. impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $crate::dispatch::Clone - for $call_type<$trait_instance $(, $instance)?> + for $call_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )* { fn clone(&self) -> Self { match *self { @@ -1039,7 +1161,7 @@ macro_rules! decl_module { } } impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $crate::dispatch::PartialEq - for $call_type<$trait_instance $(, $instance)?> + for $call_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )* { fn eq(&self, _other: &Self) -> bool { match *self { @@ -1061,14 +1183,17 @@ macro_rules! decl_module { } } impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $crate::dispatch::Eq - for $call_type<$trait_instance $(, $instance)?> + 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)?> + for $call_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )* { - fn fmt(&self, _f: &mut $crate::dispatch::fmt::Formatter) -> $crate::dispatch::result::Result<(), $crate::dispatch::fmt::Error> { + fn fmt( + &self, + _f: &mut $crate::dispatch::fmt::Formatter, + ) -> $crate::dispatch::result::Result<(), $crate::dispatch::fmt::Error> { match *self { $( $call_type::$fn_name( $( ref $param_name ),* ) => @@ -1083,7 +1208,7 @@ macro_rules! decl_module { } impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $crate::dispatch::Dispatchable - for $call_type<$trait_instance $(, $instance)?> + for $call_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )* { type Trait = $trait_instance; type Origin = $origin_type; @@ -1102,27 +1227,44 @@ macro_rules! decl_module { } } } - impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $crate::dispatch::Callable - for $mod_type<$trait_instance $(, $instance)?> + impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $crate::dispatch::Callable<$trait_instance> + for $mod_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )* { type Call = $call_type<$trait_instance $(, $instance)?>; } - impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $mod_type<$trait_instance $(, $instance)?> { + impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $mod_type<$trait_instance $(, $instance)?> + where $( $other_where_bounds )* + { #[doc(hidden)] - pub fn dispatch>(d: D, origin: D::Origin) -> $crate::dispatch::Result { + pub fn dispatch>( + d: D, + origin: D::Origin, + ) -> $crate::dispatch::Result { d.dispatch(origin) } } $crate::__dispatch_impl_metadata! { - $mod_type<$trait_instance: $trait_name $(, $instance: $instantiable)?> $call_type $origin_type - {$( $(#[doc = $doc_attr])* fn $fn_name($from $(, $(#[$codec_attr])* $param_name : $param )*); )*} + $mod_type<$trait_instance: $trait_name $(, $instance: $instantiable)?> + { $( $other_where_bounds )* } + $call_type $origin_type + { + $( + $(#[doc = $doc_attr])* + fn $fn_name($from $(, $(#[$codec_attr])* $param_name : $param )*); + )* + } + } + $crate::__impl_module_constants_metadata ! { + $mod_type<$trait_instance: $trait_name $(, $instance: $instantiable)?> + { $( $other_where_bounds )* } + $( $constants )* } } } -pub trait IsSubType { - fn is_aux_sub_type(&self) -> Option<&::Call>; +pub trait IsSubType, R> { + fn is_aux_sub_type(&self) -> Option<&CallableCallFor>; } /// Implement a meta-dispatch module to dispatch to other dispatchers. @@ -1141,13 +1283,13 @@ macro_rules! impl_outer_dispatch { #[cfg_attr(feature = "std", derive(Debug))] pub enum $call_type { $( - $camelcase ( $crate::dispatch::CallableCallFor<$camelcase> ) + $camelcase ( $crate::dispatch::CallableCallFor<$camelcase, $runtime> ) ,)* } - impl $crate::dispatch::Weighable for $call_type { - fn weight(&self, len: usize) -> $crate::dispatch::Weight { + impl $crate::dispatch::GetDispatchInfo for $call_type { + fn get_dispatch_info(&self) -> $crate::dispatch::DispatchInfo { match self { - $( $call_type::$camelcase(call) => call.weight(len), )* + $( $call_type::$camelcase(call) => call.get_dispatch_info(), )* } } } @@ -1161,15 +1303,22 @@ macro_rules! impl_outer_dispatch { } } $( - impl $crate::dispatch::IsSubType<$camelcase> for $call_type { - fn is_aux_sub_type(&self) -> Option<&<$camelcase as $crate::dispatch::Callable>::Call> { - if let $call_type::$camelcase ( ref r ) = *self { - Some(r) - } else { - None + impl $crate::dispatch::IsSubType<$camelcase, $runtime> for $call_type { + #[allow(unreachable_patterns)] + fn is_aux_sub_type(&self) -> Option<&$crate::dispatch::CallableCallFor<$camelcase, $runtime>> { + match *self { + $call_type::$camelcase(ref r) => Some(r), + // May be unreachable + _ => None, } } } + + impl From<$crate::dispatch::CallableCallFor<$camelcase, $runtime>> for $call_type { + fn from(call: $crate::dispatch::CallableCallFor<$camelcase, $runtime>) -> Self { + $call_type::$camelcase(call) + } + } )* } } @@ -1180,9 +1329,12 @@ macro_rules! impl_outer_dispatch { macro_rules! __dispatch_impl_metadata { ( $mod_type:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?> + { $( $other_where_bounds:tt )* } $($rest:tt)* ) => { - impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $mod_type<$trait_instance $(, $instance)?> { + impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $mod_type<$trait_instance $(, $instance)?> + where $( $other_where_bounds )* + { #[doc(hidden)] pub fn call_functions() -> &'static [$crate::dispatch::FunctionMetadata] { $crate::__call_to_functions!($($rest)*) @@ -1191,20 +1343,136 @@ macro_rules! __dispatch_impl_metadata { } } +/// Implement metadata for module constants. +#[macro_export] +#[doc(hidden)] +macro_rules! __impl_module_constants_metadata { + // Without instance + ( + $mod_type:ident<$trait_instance:ident: $trait_name:ident> + { $( $other_where_bounds:tt )* } + $( + $( #[doc = $doc_attr:tt] )* + $name:ident: $type:ty = $value:expr; + )* + ) => { + $crate::paste::item! { + $crate::__impl_module_constants_metadata! { + GENERATE_CODE + $mod_type<$trait_instance: $trait_name> + { $( $other_where_bounds )* } + $( + $( #[doc = $doc_attr] )* + [< $name DefaultByteGetter >] + $name<$trait_instance: $trait_name>: $type = $value; + )* + } + } + }; + // With instance + ( + $mod_type:ident<$trait_instance:ident: $trait_name:ident, $instance:ident: $instantiable:path> + { $( $other_where_bounds:tt )* } + $( + $( #[doc = $doc_attr:tt] )* + $name:ident: $type:ty = $value:expr; + )* + ) => { + $crate::paste::item! { + $crate::__impl_module_constants_metadata! { + GENERATE_CODE + $mod_type<$trait_instance: $trait_name, $instance: $instantiable> + { $( $other_where_bounds )* } + $( + $( #[doc = $doc_attr] )* + [< $name DefaultByteGetter >] + $name<$trait_instance: $trait_name, $instance: $instantiable>: $type = $value; + )* + } + } + }; + // Do the code generation + (GENERATE_CODE + $mod_type:ident<$trait_instance:ident: $trait_name:ident $(, $instance:ident: $instantiable:path)?> + { $( $other_where_bounds:tt )* } + $( + $( #[doc = $doc_attr:tt] )* + $default_byte_name:ident + $name:ident< + $const_trait_instance:ident: $const_trait_name:ident $( + , $const_instance:ident: $const_instantiable:path + )* + >: $type:ty = $value:expr; + )* + ) => { + impl<$trait_instance: 'static + $trait_name $(, $instance: $instantiable)?> + $mod_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )* + { + #[doc(hidden)] + pub fn module_constants_metadata() -> &'static [$crate::dispatch::ModuleConstantMetadata] { + // Create the `ByteGetter`s + $( + #[allow(non_upper_case_types)] + #[allow(non_camel_case_types)] + struct $default_byte_name< + $const_trait_instance: $const_trait_name $( + , $const_instance: $const_instantiable + )? + >($crate::dispatch::marker::PhantomData< + ($const_trait_instance $(, $const_instance)?) + >); + impl<$const_trait_instance: 'static + $const_trait_name $( + , $const_instance: $const_instantiable)? + > $crate::dispatch::DefaultByte + for $default_byte_name <$const_trait_instance $(, $const_instance)?> + { + fn default_byte(&self) -> $crate::dispatch::Vec { + let value: $type = $value; + $crate::dispatch::Encode::encode(&value) + } + } + )* + &[ + $( + $crate::dispatch::ModuleConstantMetadata { + name: $crate::dispatch::DecodeDifferent::Encode(stringify!($name)), + ty: $crate::dispatch::DecodeDifferent::Encode(stringify!($type)), + value: $crate::dispatch::DecodeDifferent::Encode( + $crate::dispatch::DefaultByteGetter( + &$default_byte_name::< + $const_trait_instance $(, $const_instance)? + >( + $crate::dispatch::marker::PhantomData + ) + ) + ), + documentation: $crate::dispatch::DecodeDifferent::Encode( + &[ $( $doc_attr ),* ] + ), + } + ),* + ] + } + } + } +} + /// Convert the list of calls into their JSON representation, joined by ",". #[macro_export] #[doc(hidden)] macro_rules! __call_to_functions { ( $call_type:ident $origin_type:ty - {$( + { + $( $(#[doc = $doc_attr:tt])* fn $fn_name:ident($from:ident $( , $(#[$codec_attr:ident])* $param_name:ident : $param:ty )* ); - )*} + )* + } ) => { $crate::__functions_to_metadata!(0; $origin_type;; $( fn $fn_name( $($(#[$codec_attr])* $param_name: $param ),* ); @@ -1286,7 +1554,7 @@ macro_rules! __function_to_metadata { compile_error!(concat!( "Invalid attribute for parameter `", stringify!($param_name), "`, the following attributes are supported: `#[compact]`" - )) + )); } } @@ -1328,37 +1596,43 @@ macro_rules! __check_reserved_fn_name { mod tests { use super::*; use crate::runtime_primitives::traits::{OnInitialize, OnFinalize}; + use sr_primitives::weights::{DispatchInfo, DispatchClass}; - pub trait Trait { + pub trait Trait: system::Trait + Sized where Self::AccountId: From { type Origin; type BlockNumber: Into; + type Call: From>; } pub mod system { use super::Result; + pub trait Trait { + type AccountId; + } + pub fn ensure_root(_: R) -> Result { Ok(()) } } decl_module! { - pub struct Module for enum Call where origin: T::Origin { + pub struct Module for enum Call where origin: T::Origin, T::AccountId: From { /// Hi, this is a comment. fn aux_0(_origin) -> Result { unreachable!() } fn aux_1(_origin, #[compact] _data: u32) -> Result { unreachable!() } fn aux_2(_origin, _data: i32, _data2: String) -> Result { unreachable!() } - #[weight = TransactionWeight::Basic(10, 100)] - fn aux_3() -> Result { unreachable!() } - fn aux_4(_data: i32) -> Result { unreachable!() } + #[weight = SimpleDispatchInfo::FixedNormal(10)] + fn aux_3(_origin) -> Result { unreachable!() } + fn aux_4(_origin, _data: i32) -> Result { unreachable!() } fn aux_5(_origin, _data: i32, #[compact] _data2: u32) -> Result { unreachable!() } fn on_initialize(n: T::BlockNumber) { if n.into() == 42 { panic!("on_initialize") } } fn on_finalize(n: T::BlockNumber) { if n.into() == 42 { panic!("on_finalize") } } fn offchain_worker() {} - #[weight = TransactionWeight::Max] - fn weighted() { unreachable!() } + #[weight = SimpleDispatchInfo::FixedOperational(5)] + fn operational(_origin) { unreachable!() } } } @@ -1424,17 +1698,30 @@ mod tests { documentation: DecodeDifferent::Encode(&[]), }, FunctionMetadata { - name: DecodeDifferent::Encode("weighted"), + name: DecodeDifferent::Encode("operational"), arguments: DecodeDifferent::Encode(&[]), documentation: DecodeDifferent::Encode(&[]), }, ]; - struct TraitImpl {} + pub struct TraitImpl {} impl Trait for TraitImpl { type Origin = u32; type BlockNumber = u32; + type Call = OuterCall; + } + + type Test = Module; + + impl_outer_dispatch! { + pub enum OuterCall for TraitImpl where origin: u32 { + self::Test, + } + } + + impl system::Trait for TraitImpl { + type AccountId = u32; } #[test] @@ -1486,10 +1773,19 @@ mod tests { #[test] fn weight_should_attach_to_call_enum() { // max weight. not dependent on input. - assert_eq!(Call::::weighted().weight(100), 3 * 1024 * 1024); + assert_eq!( + Call::::operational().get_dispatch_info(), + DispatchInfo { weight: 5, class: DispatchClass::Operational }, + ); // default weight. - assert_eq!(Call::::aux_0().weight(5), 5 /*tx-len*/); + assert_eq!( + Call::::aux_0().get_dispatch_info(), + DispatchInfo { weight: 100, class: DispatchClass::Normal }, + ); // custom basic - assert_eq!(Call::::aux_3().weight(5), 10 + 100 * 5 ); + assert_eq!( + Call::::aux_3().get_dispatch_info(), + DispatchInfo { weight: 10, class: DispatchClass::Normal }, + ); } } diff --git a/srml/support/src/double_map.rs b/srml/support/src/double_map.rs index d35570ae4f7b6d34bc681ec96ba50e5384f61f19..aec7f497a63913391256237b7a8df1a360ab3ccb 100644 --- a/srml/support/src/double_map.rs +++ b/srml/support/src/double_map.rs @@ -34,8 +34,8 @@ use sr_std::borrow::Borrow; /// /// Hasher are implemented in derive_key* methods. pub trait StorageDoubleMapWithHasher { - type Key1: Codec; - type Key2: Codec; + type Key1: Encode; + type Key2: Encode; type Value: Codec + Default; const PREFIX: &'static [u8]; diff --git a/srml/support/src/event.rs b/srml/support/src/event.rs index 052e52d4f56463675dd5cc235da255bb7978aefc..857f42f49e8aaa2b9dfc2a2c49f6a1575a9883b2 100644 --- a/srml/support/src/event.rs +++ b/srml/support/src/event.rs @@ -136,6 +136,7 @@ macro_rules! decl_event { } impl Event { #[allow(dead_code)] + #[doc(hidden)] pub fn metadata() -> &'static [ $crate::event::EventMetadata ] { $crate::__events_to_metadata!(; $( $events )* ) } @@ -321,17 +322,15 @@ macro_rules! __events_to_metadata { } /// Constructs an Event type for a runtime. This is usually called automatically by the -/// construct_runtime macro. See also __create_decl_macro. +/// construct_runtime macro. #[macro_export] macro_rules! impl_outer_event { - // Macro transformations (to convert invocations with incomplete parameters to the canonical // form) - ( $(#[$attr:meta])* pub enum $name:ident for $runtime:ident { - $( $rest:tt $( <$t:ident $(, $rest_instance:path)? > )*, )* + $( $rest_event_without_system:tt )* } ) => { $crate::impl_outer_event!( @@ -339,14 +338,14 @@ macro_rules! impl_outer_event { $name; $runtime; system; - Modules { $( $rest $(<$t $(, $rest_instance)? >)*, )* }; + Modules { $( $rest_event_without_system )* }; ; ); }; ( $(#[$attr:meta])* pub enum $name:ident for $runtime:ident where system = $system:ident { - $( $rest:tt $( <$t:ident $(, $rest_instance:path)? > )*, )* + $( $rest_event_with_system:tt )* } ) => { $crate::impl_outer_event!( @@ -354,30 +353,74 @@ macro_rules! impl_outer_event { $name; $runtime; $system; - Modules { $( $rest $(<$t $(, $rest_instance)? >)*, )* }; + Modules { $( $rest_event_with_system )* }; ; ); }; + // Generic + Instance + ( + $(#[$attr:meta])*; + $name:ident; + $runtime:ident; + $system:ident; + Modules { + $module:ident $instance:ident, + $( $rest_event_generic_instance:tt )* + }; + $( $module_name:ident::Event $( <$generic_param:ident> )? $( { $generic_instance:ident } )?, )*; + ) => { + $crate::impl_outer_event!( + $( #[$attr] )*; + $name; + $runtime; + $system; + Modules { $( $rest_event_generic_instance )* }; + $( $module_name::Event $( <$generic_param> )? $( { $generic_instance } )?, )* $module::Event<$runtime>{ $instance },; + ); + }; + // Instance + ( + $(#[$attr:meta])*; + $name:ident; + $runtime:ident; + $system:ident; + Modules { + $module:ident $instance:ident, + $( $rest_event_instance:tt )* + }; + $( $module_name:ident::Event $( <$generic_param:ident> )? $( { $generic_instance:ident } )?, )*; + ) => { + $crate::impl_outer_event!( + $( #[$attr] )*; + $name; + $runtime; + $system; + Modules { $( $rest_event_instance )* }; + $( $module_name::Event $( <$generic_param> )* $( { $generic_instance } )?, )* $module::Event { $instance },; + ); + }; + // Generic ( $(#[$attr:meta])*; $name:ident; $runtime:ident; $system:ident; Modules { - $module:ident, - $( $rest:tt $( <$t:ident $(, $rest_instance:path)? > )*, )* + $module:ident, + $( $rest_event_generic:tt )* }; - $( $module_name:ident::Event $( <$generic_param:ident $(, $generic_instance:path)? > )*, )*; + $( $module_name:ident::Event $( <$generic_param:ident> )? $( { $generic_instance:ident } )?, )*; ) => { $crate::impl_outer_event!( $( #[$attr] )*; $name; $runtime; $system; - Modules { $( $rest $(<$t $(, $rest_instance)? >)*, )* }; - $( $module_name::Event $( <$generic_param $(, $generic_instance)? > )*, )* $module::Event<$runtime $(, $instance)? >,; + Modules { $( $rest_event_generic )* }; + $( $module_name::Event $( <$generic_param> )? $( { $generic_instance } )?, )* $module::Event<$runtime>,; ); }; + // No Generic and no Instance ( $(#[$attr:meta])*; $name:ident; @@ -385,30 +428,30 @@ macro_rules! impl_outer_event { $system:ident; Modules { $module:ident, - $( $rest:tt )* + $( $rest_event_no_generic_no_instance:tt )* }; - $( $module_name:ident::Event $( <$generic_param:ident $(, $generic_instance:path)? > )*, )*; + $( $module_name:ident::Event $( <$generic_param:ident> )? $( { $generic_instance:ident } )?, )*; ) => { $crate::impl_outer_event!( $( #[$attr] )*; $name; $runtime; $system; - Modules { $( $rest )* }; - $( $module_name::Event $( <$generic_param $(, $generic_instance)? > )*, )* $module::Event,; + Modules { $( $rest_event_no_generic_no_instance )* }; + $( $module_name::Event $( <$generic_param> )? $( { $generic_instance } )?, )* $module::Event,; ); }; // The main macro expansion that actually renders the Event enum code. - ( $(#[$attr:meta])*; $name:ident; $runtime:ident; $system:ident; Modules {}; - $( $module_name:ident::Event $( <$generic_param:ident $(, $generic_instance:path)? > )*, )*; + $( $module_name:ident::Event $( <$generic_param:ident> )? $( { $generic_instance:ident } )?, )*; ) => { + $crate::paste::item! { // 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))] @@ -417,7 +460,9 @@ macro_rules! impl_outer_event { pub enum $name { system($system::Event), $( - $module_name( $module_name::Event $( <$generic_param $(, $generic_instance)? > )* ), + [< $module_name $(_ $generic_instance )? >]( + $module_name::Event < $( $generic_param )? $(, $module_name::$generic_instance )? > + ), )* } impl From<$system::Event> for $name { @@ -426,17 +471,22 @@ macro_rules! impl_outer_event { } } $( - impl From<$module_name::Event $( <$generic_param $(, $generic_instance)? > )*> for $name { - fn from(x: $module_name::Event $( <$generic_param $(, $generic_instance)? > )*) -> Self { - $name::$module_name(x) + impl From<$module_name::Event < $( $generic_param, )? $( $module_name::$generic_instance )? >> for $name { + fn from(x: $module_name::Event < $( $generic_param, )? $( $module_name::$generic_instance )? >) -> Self { + $name::[< $module_name $(_ $generic_instance )? >](x) } } )* + } $crate::__impl_outer_event_json_metadata!( $runtime; $name; $system; - $( $module_name::Event $( <$generic_param $(, $generic_instance)? > )*, )*; + $( + $module_name::Event + < $( $generic_param )? $(, $module_name::$generic_instance )? > + $( $generic_instance )?, + )*; ); } } @@ -448,7 +498,7 @@ macro_rules! __impl_outer_event_json_metadata { $runtime:ident; $event_name:ident; $system:ident; - $( $module_name:ident::Event $( <$generic_param:ident $(, $generic_instance:path)? > )*, )*; + $( $module_name:ident::Event < $( $generic_params:path ),* > $( $instance:ident )?, )*; ) => { impl $runtime { #[allow(dead_code)] @@ -461,7 +511,7 @@ macro_rules! __impl_outer_event_json_metadata { , ( stringify!($module_name), $crate::event::FnEncode( - $module_name::Event $( ::<$generic_param $(, $generic_instance)? > )* ::metadata + $module_name::Event ::< $( $generic_params ),* > ::metadata ) ) )* @@ -472,14 +522,17 @@ macro_rules! __impl_outer_event_json_metadata { pub fn __module_events_system() -> &'static [$crate::event::EventMetadata] { system::Event::metadata() } - $( - #[allow(dead_code)] - $crate::paste::item!{ - pub fn [< __module_events_ $module_name >] () -> &'static [$crate::event::EventMetadata] { - $module_name::Event $( ::<$generic_param $(, $generic_instance)? > )* ::metadata() + + $crate::paste::item! { + $( + #[allow(dead_code)] + pub fn [< __module_events_ $module_name $( _ $instance )? >] () -> + &'static [$crate::event::EventMetadata] + { + $module_name::Event ::< $( $generic_params ),* > ::metadata() } - } - )* + )* + } } } } diff --git a/srml/support/src/lib.rs b/srml/support/src/lib.rs index ae825397a6ae4f0ae9f51324e9c50348b45f00de..e1300c5925852e9932e8b26d747fad3a09a4694a 100644 --- a/srml/support/src/lib.rs +++ b/srml/support/src/lib.rs @@ -34,7 +34,9 @@ pub use once_cell; pub use paste; pub use sr_primitives as runtime_primitives; -pub use self::storage::hashed::generator::{HashedStorage, Twox256, Twox128, Blake2_256, Blake2_128, Twox64Concat}; +pub use self::storage::hashed::generator::{ + HashedStorage, Twox256, Twox128, Blake2_256, Blake2_128, Twox64Concat +}; pub use self::storage::unhashed::generator::UnhashedStorage; #[macro_use] @@ -64,6 +66,7 @@ pub use self::hashable::Hashable; pub use self::dispatch::{Parameter, Dispatchable, Callable, IsSubType}; pub use self::double_map::StorageDoubleMapWithHasher; pub use runtime_io::{print, storage_root}; +pub use runtime_primitives::ConsensusEngineId; /// Macro for easily creating a new implementation of the `Get` trait. Use similarly to /// how you would declare a `const`: @@ -82,19 +85,28 @@ pub use runtime_io::{print, storage_root}; /// ``` #[macro_export] macro_rules! parameter_types { - (pub const $name:ident: $type:ty = $value:expr; $( $rest:tt )*) => ( - pub struct $name; - $crate::parameter_types!{IMPL $name , $type , $value} - $crate::parameter_types!{ $( $rest )* } - ); - (const $name:ident: $type:ty = $value:expr; $( $rest:tt )*) => ( - struct $name; + ( + $( #[ $attr:meta ] )* + $vis:vis const $name:ident: $type:ty = $value:expr; + $( $rest:tt )* + ) => ( + $( #[ $attr ] )* + $vis struct $name; $crate::parameter_types!{IMPL $name , $type , $value} $crate::parameter_types!{ $( $rest )* } ); () => (); (IMPL $name:ident , $type:ty , $value:expr) => { - impl $crate::traits::Get<$type> for $name { fn get() -> $type { $value } } + impl $name { + fn get() -> $type { + $value + } + } + impl> $crate::traits::Get for $name { + fn get() -> I { + I::from($value) + } + } } } @@ -238,10 +250,9 @@ mod tests { use super::*; use codec::Codec; use runtime_io::{with_externalities, Blake2Hasher}; - use runtime_primitives::BuildStorage; pub use srml_metadata::{ - DecodeDifferent, StorageMetadata, StorageFunctionMetadata, - StorageFunctionType, StorageFunctionModifier, + DecodeDifferent, StorageEntryMetadata, + StorageEntryType, StorageEntryModifier, DefaultByte, DefaultByteGetter, StorageHasher }; pub use rstd::marker::PhantomData; @@ -257,9 +268,7 @@ mod tests { use super::Trait; decl_module! { - pub struct Module for enum Call where origin: T::Origin { - - } + pub struct Module for enum Call where origin: T::Origin {} } } use self::module::Module; @@ -285,10 +294,10 @@ mod tests { } fn new_test_ext() -> runtime_io::TestExternalities { - GenesisConfig::::default().build_storage().unwrap().0.into() + GenesisConfig::default().build_storage().unwrap().0.into() } - type Map = Data; + type Map = Data; #[test] fn linked_map_basic_insert_remove_should_work() { @@ -371,39 +380,39 @@ mod tests { #[test] fn double_map_basic_insert_remove_remove_prefix_should_work() { with_externalities(&mut new_test_ext(), || { - type DoubleMap = DataDM; + type DoubleMap = DataDM; // initialized during genesis assert_eq!(DoubleMap::get(&15u32, &16u32), 42u64); // get / insert / take let key1 = 17u32; let key2 = 18u32; - assert_eq!(DoubleMap::get(key1, key2), 0u64); - DoubleMap::insert(key1, key2, 4u64); - assert_eq!(DoubleMap::get(key1, key2), 4u64); - assert_eq!(DoubleMap::take(key1, key2), 4u64); - assert_eq!(DoubleMap::get(key1, key2), 0u64); + assert_eq!(DoubleMap::get(&key1, &key2), 0u64); + DoubleMap::insert(&key1, &key2, &4u64); + assert_eq!(DoubleMap::get(&key1, &key2), 4u64); + assert_eq!(DoubleMap::take(&key1, &key2), 4u64); + assert_eq!(DoubleMap::get(&key1, &key2), 0u64); // mutate - DoubleMap::mutate(key1, key2, |val| { + DoubleMap::mutate(&key1, &key2, |val| { *val = 15; }); - assert_eq!(DoubleMap::get(key1, key2), 15u64); + assert_eq!(DoubleMap::get(&key1, &key2), 15u64); // remove - DoubleMap::remove(key1, key2); - assert_eq!(DoubleMap::get(key1, key2), 0u64); + DoubleMap::remove(&key1, &key2); + assert_eq!(DoubleMap::get(&key1, &key2), 0u64); // remove prefix - DoubleMap::insert(key1, key2, 4u64); - DoubleMap::insert(key1, key2+1, 4u64); - DoubleMap::insert(key1+1, key2, 4u64); - DoubleMap::insert(key1+1, key2+1, 4u64); - DoubleMap::remove_prefix(key1); - assert_eq!(DoubleMap::get(key1, key2), 0u64); - assert_eq!(DoubleMap::get(key1, key2+1), 0u64); - assert_eq!(DoubleMap::get(key1+1, key2), 4u64); - assert_eq!(DoubleMap::get(key1+1, key2+1), 4u64); + DoubleMap::insert(&key1, &key2, &4u64); + DoubleMap::insert(&key1, &(key2 + 1), &4u64); + DoubleMap::insert(&(key1 + 1), &key2, &4u64); + DoubleMap::insert(&(key1 + 1), &(key2 + 1), &4u64); + DoubleMap::remove_prefix(&key1); + assert_eq!(DoubleMap::get(&key1, &key2), 0u64); + assert_eq!(DoubleMap::get(&key1, &(key2 + 1)), 0u64); + assert_eq!(DoubleMap::get(&(key1 + 1), &key2), 4u64); + assert_eq!(DoubleMap::get(&(key1 + 1), &(key2 + 1)), 4u64); }); } @@ -416,120 +425,118 @@ mod tests { let key1 = 17u32; let key2 = 18u32; - DoubleMap::insert(key1, key2, vec![1]); - DoubleMap::append(key1, key2, &[2, 3]).unwrap(); - assert_eq!(DoubleMap::get(key1, key2), vec![1, 2, 3]); + DoubleMap::insert(&key1, &key2, &vec![1]); + DoubleMap::append(&key1, &key2, &[2, 3]).unwrap(); + assert_eq!(DoubleMap::get(&key1, &key2), &[1, 2, 3]); }); } - const EXPECTED_METADATA: StorageMetadata = StorageMetadata { - functions: DecodeDifferent::Encode(&[ - StorageFunctionMetadata { - name: DecodeDifferent::Encode("Data"), - modifier: StorageFunctionModifier::Default, - ty: StorageFunctionType::Map{ - hasher: StorageHasher::Twox64Concat, - key: DecodeDifferent::Encode("u32"), value: DecodeDifferent::Encode("u64"), is_linked: true - }, - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructData(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), + const EXPECTED_METADATA: &[StorageEntryMetadata] = &[ + StorageEntryMetadata { + name: DecodeDifferent::Encode("Data"), + modifier: StorageEntryModifier::Default, + ty: StorageEntryType::Map{ + hasher: StorageHasher::Twox64Concat, + key: DecodeDifferent::Encode("u32"), value: DecodeDifferent::Encode("u64"), is_linked: true }, - StorageFunctionMetadata { - name: DecodeDifferent::Encode("GenericData"), - modifier: StorageFunctionModifier::Default, - ty: StorageFunctionType::Map{ - hasher: StorageHasher::Twox128, - key: DecodeDifferent::Encode("T::BlockNumber"), - value: DecodeDifferent::Encode("T::BlockNumber"), - is_linked: true - }, - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructGenericData(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructData(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageEntryMetadata { + name: DecodeDifferent::Encode("GenericData"), + modifier: StorageEntryModifier::Default, + ty: StorageEntryType::Map{ + hasher: StorageHasher::Twox128, + key: DecodeDifferent::Encode("T::BlockNumber"), + value: DecodeDifferent::Encode("T::BlockNumber"), + is_linked: true }, - StorageFunctionMetadata { - name: DecodeDifferent::Encode("GenericData2"), - modifier: StorageFunctionModifier::Optional, - ty: StorageFunctionType::Map{ - hasher: StorageHasher::Blake2_256, - key: DecodeDifferent::Encode("T::BlockNumber"), - value: DecodeDifferent::Encode("T::BlockNumber"), - is_linked: true - }, - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructGenericData2(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructGenericData(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageEntryMetadata { + name: DecodeDifferent::Encode("GenericData2"), + modifier: StorageEntryModifier::Optional, + ty: StorageEntryType::Map{ + hasher: StorageHasher::Blake2_256, + key: DecodeDifferent::Encode("T::BlockNumber"), + value: DecodeDifferent::Encode("T::BlockNumber"), + is_linked: true }, - StorageFunctionMetadata { - name: DecodeDifferent::Encode("DataDM"), - modifier: StorageFunctionModifier::Default, - ty: StorageFunctionType::DoubleMap{ - hasher: StorageHasher::Twox64Concat, - key1: DecodeDifferent::Encode("u32"), - key2: DecodeDifferent::Encode("u32"), - value: DecodeDifferent::Encode("u64"), - key2_hasher: StorageHasher::Blake2_256, - }, - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructDataDM(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructGenericData2(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageEntryMetadata { + name: DecodeDifferent::Encode("DataDM"), + modifier: StorageEntryModifier::Default, + ty: StorageEntryType::DoubleMap{ + hasher: StorageHasher::Twox64Concat, + key1: DecodeDifferent::Encode("u32"), + key2: DecodeDifferent::Encode("u32"), + value: DecodeDifferent::Encode("u64"), + key2_hasher: StorageHasher::Blake2_256, }, - StorageFunctionMetadata { - name: DecodeDifferent::Encode("GenericDataDM"), - modifier: StorageFunctionModifier::Default, - ty: StorageFunctionType::DoubleMap{ - hasher: StorageHasher::Blake2_256, - key1: DecodeDifferent::Encode("T::BlockNumber"), - key2: DecodeDifferent::Encode("T::BlockNumber"), - value: DecodeDifferent::Encode("T::BlockNumber"), - key2_hasher: StorageHasher::Twox128, - }, - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructGenericDataDM(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructDataDM(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageEntryMetadata { + name: DecodeDifferent::Encode("GenericDataDM"), + modifier: StorageEntryModifier::Default, + ty: StorageEntryType::DoubleMap{ + hasher: StorageHasher::Blake2_256, + key1: DecodeDifferent::Encode("T::BlockNumber"), + key2: DecodeDifferent::Encode("T::BlockNumber"), + value: DecodeDifferent::Encode("T::BlockNumber"), + key2_hasher: StorageHasher::Twox128, }, - StorageFunctionMetadata { - name: DecodeDifferent::Encode("GenericData2DM"), - modifier: StorageFunctionModifier::Optional, - ty: StorageFunctionType::DoubleMap{ - hasher: StorageHasher::Blake2_256, - key1: DecodeDifferent::Encode("T::BlockNumber"), - key2: DecodeDifferent::Encode("T::BlockNumber"), - value: DecodeDifferent::Encode("T::BlockNumber"), - key2_hasher: StorageHasher::Twox256, - }, - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructGenericData2DM(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructGenericDataDM(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageEntryMetadata { + name: DecodeDifferent::Encode("GenericData2DM"), + modifier: StorageEntryModifier::Optional, + ty: StorageEntryType::DoubleMap{ + hasher: StorageHasher::Blake2_256, + key1: DecodeDifferent::Encode("T::BlockNumber"), + key2: DecodeDifferent::Encode("T::BlockNumber"), + value: DecodeDifferent::Encode("T::BlockNumber"), + key2_hasher: StorageHasher::Twox256, }, - StorageFunctionMetadata { - name: DecodeDifferent::Encode("AppendableDM"), - modifier: StorageFunctionModifier::Default, - ty: StorageFunctionType::DoubleMap{ - hasher: StorageHasher::Blake2_256, - key1: DecodeDifferent::Encode("u32"), - key2: DecodeDifferent::Encode("T::BlockNumber"), - value: DecodeDifferent::Encode("Vec"), - key2_hasher: StorageHasher::Blake2_256, - }, - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructGenericData2DM(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructGenericData2DM(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageEntryMetadata { + name: DecodeDifferent::Encode("AppendableDM"), + modifier: StorageEntryModifier::Default, + ty: StorageEntryType::DoubleMap{ + hasher: StorageHasher::Blake2_256, + key1: DecodeDifferent::Encode("u32"), + key2: DecodeDifferent::Encode("T::BlockNumber"), + value: DecodeDifferent::Encode("Vec"), + key2_hasher: StorageHasher::Blake2_256, }, - ]) - }; + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructGenericData2DM(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + ]; #[test] fn store_metadata() { - let metadata = Module::::store_metadata(); + let metadata = Module::::store_metadata_functions(); assert_eq!(EXPECTED_METADATA, metadata); } } diff --git a/srml/support/src/metadata.rs b/srml/support/src/metadata.rs index 9a6b671f7792e640067bef8f3706d71b15782e1a..bdb3671e3431546f223ae8116de76ac293808bf3 100644 --- a/srml/support/src/metadata.rs +++ b/srml/support/src/metadata.rs @@ -15,15 +15,11 @@ // along with Substrate. If not, see . pub use srml_metadata::{ - DecodeDifferent, FnEncode, RuntimeMetadata, - ModuleMetadata, RuntimeMetadataV5, - DefaultByteGetter, RuntimeMetadataPrefixed, - StorageMetadata, StorageFunctionMetadata, - StorageFunctionType, StorageFunctionModifier, - DefaultByte, StorageHasher + DecodeDifferent, FnEncode, RuntimeMetadata, ModuleMetadata, RuntimeMetadataV6, + DefaultByteGetter, RuntimeMetadataPrefixed, StorageEntryMetadata, + StorageEntryType, StorageEntryModifier, DefaultByte, StorageHasher }; - /// Implements the metadata support for the given runtime and all its modules. /// /// Example: @@ -40,8 +36,8 @@ macro_rules! impl_runtime_metadata { ) => { impl $runtime { pub fn metadata() -> $crate::metadata::RuntimeMetadataPrefixed { - $crate::metadata::RuntimeMetadata::V5 ( - $crate::metadata::RuntimeMetadataV5 { + $crate::metadata::RuntimeMetadata::V6 ( + $crate::metadata::RuntimeMetadataV6 { modules: $crate::__runtime_modules_to_metadata!($runtime;; $( $rest )*), } ).into() @@ -67,6 +63,11 @@ macro_rules! __runtime_modules_to_metadata { storage: $crate::__runtime_modules_to_metadata_calls_storage!($mod, $module $( <$instance> )?, $runtime, $(with $kw)*), calls: $crate::__runtime_modules_to_metadata_calls_call!($mod, $module $( <$instance> )?, $runtime, $(with $kw)*), event: $crate::__runtime_modules_to_metadata_calls_event!($mod, $module $( <$instance> )?, $runtime, $(with $kw)*), + constants: $crate::metadata::DecodeDifferent::Encode( + $crate::metadata::FnEncode( + $mod::$module::<$runtime $(, $mod::$instance )?>::module_constants_metadata + ) + ) }; $( $rest )* ) @@ -227,23 +228,31 @@ macro_rules! __runtime_modules_to_metadata_calls_storage { mod tests { use super::*; use srml_metadata::{ - EventMetadata, - StorageFunctionModifier, StorageFunctionType, FunctionMetadata, - StorageFunctionMetadata, - ModuleMetadata, RuntimeMetadataPrefixed + EventMetadata, StorageEntryModifier, StorageEntryType, FunctionMetadata, StorageEntryMetadata, + ModuleMetadata, RuntimeMetadataPrefixed, DefaultByte, ModuleConstantMetadata, DefaultByteGetter, }; - use crate::codec::{Encode, Decode}; + use codec::{Encode, Decode}; + use crate::traits::Get; mod system { + use super::*; + pub trait Trait { + const ASSOCIATED_CONST: u64 = 500; type Origin: Into, Self::Origin>> + From>; - type AccountId; - type BlockNumber; + type AccountId: From + Encode; + type BlockNumber: From + Encode; + type SomeValue: Get; } decl_module! { - pub struct Module for enum Call where origin: T::Origin {} + pub struct Module for enum Call where origin: T::Origin { + /// Hi, I am a comment. + const BlockNumber: T::BlockNumber = 100.into(); + const GetType: T::AccountId = T::SomeValue::get().into(); + const ASSOCIATED_CONST: u64 = T::ASSOCIATED_CONST.into(); + } } decl_event!( @@ -359,10 +368,15 @@ mod tests { type BlockNumber = u32; } + crate::parameter_types! { + pub const SystemValue: u32 = 600; + } + impl system::Trait for TestRuntime { type Origin = Origin; type AccountId = u32; type BlockNumber = u32; + type SomeValue = SystemValue; } impl_runtime_metadata!( @@ -372,76 +386,130 @@ mod tests { event_module2::Module with Event Storage Call, ); - const EXPECTED_METADATA: RuntimeMetadata = RuntimeMetadata::V5( - RuntimeMetadataV5 { - modules: DecodeDifferent::Encode(&[ - ModuleMetadata { - name: DecodeDifferent::Encode("system"), - prefix: DecodeDifferent::Encode(FnEncode(||"")), - storage: None, - calls: None, - event: Some(DecodeDifferent::Encode( - FnEncode(||&[ - EventMetadata { - name: DecodeDifferent::Encode("SystemEvent"), - arguments: DecodeDifferent::Encode(&[]), - documentation: DecodeDifferent::Encode(&[]) - } - ]) - )), - }, - ModuleMetadata { - name: DecodeDifferent::Encode("event_module"), - prefix: DecodeDifferent::Encode(FnEncode(||"")), - storage: None, - calls: Some( - DecodeDifferent::Encode(FnEncode(||&[ - FunctionMetadata { - name: DecodeDifferent::Encode("aux_0"), - arguments: DecodeDifferent::Encode(&[]), - documentation: DecodeDifferent::Encode(&[]), - } - ]))), - event: Some(DecodeDifferent::Encode( - FnEncode(||&[ - EventMetadata { - name: DecodeDifferent::Encode("TestEvent"), - arguments: DecodeDifferent::Encode(&["Balance"]), - documentation: DecodeDifferent::Encode(&[" Hi, I am a comment."]) - } - ]) - )), - }, - ModuleMetadata { - name: DecodeDifferent::Encode("event_module2"), - prefix: DecodeDifferent::Encode(FnEncode(||"TestStorage")), - storage: Some(DecodeDifferent::Encode( - FnEncode(||&[ - StorageFunctionMetadata { - name: DecodeDifferent::Encode("StorageMethod"), - modifier: StorageFunctionModifier::Optional, - ty: StorageFunctionType::Plain(DecodeDifferent::Encode("u32")), - default: DecodeDifferent::Encode( - DefaultByteGetter( - &event_module2::__GetByteStructStorageMethod(::std::marker::PhantomData::) - ) - ), - documentation: DecodeDifferent::Encode(&[]), - } - ]) - )), - calls: Some(DecodeDifferent::Encode(FnEncode(||&[ ]))), - event: Some(DecodeDifferent::Encode( - FnEncode(||&[ - EventMetadata { - name: DecodeDifferent::Encode("TestEvent"), - arguments: DecodeDifferent::Encode(&["Balance"]), - documentation: DecodeDifferent::Encode(&[]) - } - ]) - )), - }, - ])} + struct ConstantBlockNumberByteGetter; + impl DefaultByte for ConstantBlockNumberByteGetter { + fn default_byte(&self) -> Vec { + 100u32.encode() + } + } + + struct ConstantGetTypeByteGetter; + impl DefaultByte for ConstantGetTypeByteGetter { + fn default_byte(&self) -> Vec { + SystemValue::get().encode() + } + } + + struct ConstantAssociatedConstByteGetter; + impl DefaultByte for ConstantAssociatedConstByteGetter { + fn default_byte(&self) -> Vec { + ::ASSOCIATED_CONST.encode() + } + } + + const EXPECTED_METADATA: RuntimeMetadata = RuntimeMetadata::V6( + RuntimeMetadataV6 { + modules: DecodeDifferent::Encode(&[ + ModuleMetadata { + name: DecodeDifferent::Encode("system"), + prefix: DecodeDifferent::Encode(FnEncode(|| "")), + storage: None, + calls: None, + event: Some(DecodeDifferent::Encode( + FnEncode(||&[ + EventMetadata { + name: DecodeDifferent::Encode("SystemEvent"), + arguments: DecodeDifferent::Encode(&[]), + documentation: DecodeDifferent::Encode(&[]) + } + ]) + )), + constants: DecodeDifferent::Encode( + FnEncode(|| &[ + ModuleConstantMetadata { + name: DecodeDifferent::Encode("BlockNumber"), + ty: DecodeDifferent::Encode("T::BlockNumber"), + value: DecodeDifferent::Encode( + DefaultByteGetter(&ConstantBlockNumberByteGetter) + ), + documentation: DecodeDifferent::Encode(&[" Hi, I am a comment."]), + }, + ModuleConstantMetadata { + name: DecodeDifferent::Encode("GetType"), + ty: DecodeDifferent::Encode("T::AccountId"), + value: DecodeDifferent::Encode( + DefaultByteGetter(&ConstantGetTypeByteGetter) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + ModuleConstantMetadata { + name: DecodeDifferent::Encode("ASSOCIATED_CONST"), + ty: DecodeDifferent::Encode("u64"), + value: DecodeDifferent::Encode( + DefaultByteGetter(&ConstantAssociatedConstByteGetter) + ), + documentation: DecodeDifferent::Encode(&[]), + } + ]) + ), + }, + ModuleMetadata { + name: DecodeDifferent::Encode("event_module"), + prefix: DecodeDifferent::Encode(FnEncode(|| "")), + storage: None, + calls: Some( + DecodeDifferent::Encode(FnEncode(|| &[ + FunctionMetadata { + name: DecodeDifferent::Encode("aux_0"), + arguments: DecodeDifferent::Encode(&[]), + documentation: DecodeDifferent::Encode(&[]), + } + ]))), + event: Some(DecodeDifferent::Encode( + FnEncode(||&[ + EventMetadata { + name: DecodeDifferent::Encode("TestEvent"), + arguments: DecodeDifferent::Encode(&["Balance"]), + documentation: DecodeDifferent::Encode(&[" Hi, I am a comment."]) + } + ]) + )), + constants: DecodeDifferent::Encode(FnEncode(|| &[])), + }, + ModuleMetadata { + name: DecodeDifferent::Encode("event_module2"), + prefix: DecodeDifferent::Encode(FnEncode(||"TestStorage")), + storage: Some(DecodeDifferent::Encode( + FnEncode(||&[ + StorageEntryMetadata { + name: DecodeDifferent::Encode("StorageMethod"), + modifier: StorageEntryModifier::Optional, + ty: StorageEntryType::Plain(DecodeDifferent::Encode("u32")), + default: DecodeDifferent::Encode( + DefaultByteGetter( + &event_module2::__GetByteStructStorageMethod( + std::marker::PhantomData:: + ) + ) + ), + documentation: DecodeDifferent::Encode(&[]), + } + ]) + )), + calls: Some(DecodeDifferent::Encode(FnEncode(|| &[]))), + event: Some(DecodeDifferent::Encode( + FnEncode(||&[ + EventMetadata { + name: DecodeDifferent::Encode("TestEvent"), + arguments: DecodeDifferent::Encode(&["Balance"]), + documentation: DecodeDifferent::Encode(&[]) + } + ]) + )), + constants: DecodeDifferent::Encode(FnEncode(|| &[])), + }, + ]) + } ); #[test] @@ -450,6 +518,6 @@ mod tests { let metadata_decoded = RuntimeMetadataPrefixed::decode(&mut &metadata_encoded[..]); let expected_metadata: RuntimeMetadataPrefixed = EXPECTED_METADATA.into(); - assert_eq!(expected_metadata, metadata_decoded.unwrap()); + pretty_assertions::assert_eq!(expected_metadata, metadata_decoded.unwrap()); } } diff --git a/srml/support/src/origin.rs b/srml/support/src/origin.rs index 9bc2cab8b9d178630144cfee9d44c3485fef0f1e..f3fec6e3ae6ce16909abd27487262923e8286ee2 100644 --- a/srml/support/src/origin.rs +++ b/srml/support/src/origin.rs @@ -24,24 +24,24 @@ macro_rules! impl_outer_origin { // Macro transformations (to convert invocations with incomplete parameters to the canonical // form) - ( $(#[$attr:meta])* pub enum $name:ident for $runtime:ident { - $( $module:ident $( <$generic:ident $(, $instance:path )? > )? ),* $(,)? + $( $rest_without_system:tt )* } ) => { $crate::impl_outer_origin! { $(#[$attr])* pub enum $name for $runtime where system = system { - $( $module $( <$generic $(, $instance )? > )?, )* + $( $rest_without_system )* } } }; + ( $(#[$attr:meta])* pub enum $name:ident for $runtime:ident where system = $system:ident { - $( $module:ident $( <$generic:ident $(, $instance:path )?> )? ),* $(,)? + $( $rest_with_system:tt )* } ) => { $crate::impl_outer_origin!( @@ -49,20 +49,41 @@ macro_rules! impl_outer_origin { $name; $runtime; $system; - Modules { $( $module $( <$generic $(, $instance )? > )*, )* }; + Modules { $( $rest_with_system )* }; ); }; - // Replace generic param with runtime + // Generic + Instance + ( + $(#[$attr:meta])*; + $name:ident; + $runtime:ident; + $system:ident; + Modules { + $module:ident $instance:ident + $(, $( $rest_module:tt )* )? + }; + $( $parsed:tt )* + ) => { + $crate::impl_outer_origin!( + $( #[$attr] )*; + $name; + $runtime; + $system; + Modules { $( $( $rest_module )* )? }; + $( $parsed )* $module <$runtime> { $instance }, + ); + }; + // Instance ( $(#[$attr:meta])*; $name:ident; $runtime:ident; $system:ident; Modules { - $module:ident $( )?, - $( $rest_module:tt )* + $module:ident $instance:ident + $(, $rest_module:tt )* }; $( $parsed:tt )* ) => { @@ -72,33 +93,80 @@ macro_rules! impl_outer_origin { $runtime; $system; Modules { $( $rest_module )* }; - $( $parsed )* $module $( <$runtime $(, $instance )? > )?, + $( $parsed )* $module { $instance }, ); }; - // The main macro expansion that actually renders the Origin enum code. + // Generic + ( + $(#[$attr:meta])*; + $name:ident; + $runtime:ident; + $system:ident; + Modules { + $module:ident + $(, $( $rest_module:tt )* )? + }; + $( $parsed:tt )* + ) => { + $crate::impl_outer_origin!( + $( #[$attr] )*; + $name; + $runtime; + $system; + Modules { $( $( $rest_module )* )? }; + $( $parsed )* $module <$runtime>, + ); + }; + // No Generic and no Instance + ( + $(#[$attr:meta])*; + $name:ident; + $runtime:ident; + $system:ident; + Modules { + $module:ident + $(, $( $rest_module:tt )* )? + }; + $( $parsed:tt )* + ) => { + $crate::impl_outer_origin!( + $( #[$attr] )*; + $name; + $runtime; + $system; + Modules { $( $( $rest_module )* )? }; + $( $parsed )* $module, + ); + }; + + // The main macro expansion that actually renders the Origin enum code. ( $(#[$attr:meta])*; $name:ident; $runtime:ident; $system:ident; Modules { }; - $( $module:ident $( <$generic_param:ident $(, $generic_instance:path )? > )* ,)* + $( $module:ident $( < $generic:ident > )? $( { $generic_instance:ident } )? ,)* ) => { - // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. - #[derive(Clone, PartialEq, Eq)] - #[cfg_attr(feature = "std", derive(Debug))] - $(#[$attr])* - #[allow(non_camel_case_types)] - pub enum $name { - system($system::Origin<$runtime>), - $( - $module($module::Origin $( <$generic_param $(, $generic_instance )? > )* ), - )* - #[allow(dead_code)] - Void($crate::Void) + $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))] + $(#[$attr])* + #[allow(non_camel_case_types)] + pub enum $name { + system($system::Origin<$runtime>), + $( + [< $module $( _ $generic_instance )? >] + ($module::Origin < $( $generic, )? $( $module::$generic_instance )? > ), + )* + #[allow(dead_code)] + Void($crate::Void) + } } + #[allow(dead_code)] impl $name { pub const NONE: Self = $name::system($system::RawOrigin::None); @@ -127,23 +195,27 @@ macro_rules! impl_outer_origin { } } $( - impl From<$module::Origin $( <$generic_param $(, $generic_instance )? > )*> for $name { - fn from(x: $module::Origin $( <$generic_param $(, $generic_instance )? > )*) -> Self { - $name::$module(x) + $crate::paste::item! { + impl From<$module::Origin < $( $generic )? $(, $module::$generic_instance )? > > for $name { + fn from(x: $module::Origin < $( $generic )? $(, $module::$generic_instance )? >) -> Self { + $name::[< $module $( _ $generic_instance )? >](x) + } } - } - impl Into<$crate::rstd::result::Result< - $module::Origin $( <$generic_param $(, $generic_instance )? > )*, - $name - >> for $name { - fn into(self) -> $crate::rstd::result::Result< - $module::Origin $( <$generic_param $(, $generic_instance )? > )*, - Self - > { - if let $name::$module(l) = self { - Ok(l) - } else { - Err(self) + impl Into< + $crate::rstd::result::Result< + $module::Origin < $( $generic )? $(, $module::$generic_instance )? >, + $name, + >> + for $name { + fn into(self) -> $crate::rstd::result::Result< + $module::Origin < $( $generic )? $(, $module::$generic_instance )? >, + Self, + > { + if let $name::[< $module $( _ $generic_instance )? >](l) = self { + Ok(l) + } else { + Err(self) + } } } } diff --git a/srml/support/src/runtime.rs b/srml/support/src/runtime.rs index 6bccac0d4eff01def9000d1a23c809c84349aa9a..4461e37518698a3c145a726a58ece426c9759a3c 100644 --- a/srml/support/src/runtime.rs +++ b/srml/support/src/runtime.rs @@ -61,9 +61,9 @@ /// - `Module` /// - `Call` /// - `Storage` -/// - `Event` or `Event` (if the event is generic) or `Event` (if also over instance) -/// - `Origin` or `Origin` (if the origin is generic) or `Origin` (if also over instance) -/// - `Config` or `Config` (if the config is generic) or `Config` (if also over instance) +/// - `Event` or `Event` (if the event is generic) +/// - `Origin` or `Origin` (if the origin is generic) +/// - `Config` or `Config` (if the config is generic) /// - `Inherent $( (CALL) )*` - If the module provides/can check inherents. The optional parameter /// is for modules that use a `Call` from a different module as /// inherent. @@ -101,6 +101,7 @@ macro_rules! construct_runtime { $( $rest )* ); }; + // No modules given, expand to the default module set. ( { $( $preset:tt )* }; { $( $expanded:tt )* }; @@ -114,41 +115,17 @@ macro_rules! construct_runtime { $( $rest )* ); }; + // `default` identifier given, expand to default + given extra modules ( { $( $preset:tt )* }; { $( $expanded:tt )* }; $name:ident: $module:ident::{ default $(, - $modules:ident - $( <$modules_generic:ident $(, $modules_instance:ident)?> )* - $( ( $( $modules_args:ident ),* ) )* - )* - }, - $( $rest:tt )* - ) => { - $crate::construct_runtime!( - { $( $preset )* }; - { $( $expanded )* }; - $name: $module::{ - Module, Call, Storage, Event, Config - $(, - $modules $( <$modules_generic $(, $modules_instance)?> )* - $( ( $( $modules_args ),* ) )* - )* - }, - $( $rest )* - ); - }; - ( - { $( $preset:tt )* }; - { $( $expanded:tt )* }; - $name:ident: $module:ident::{ - $( $modules:ident $( <$modules_generic:ident> )* $( ( $( $modules_args:ident ),* ) )* - ),* + )* }, $( $rest:tt )* ) => { @@ -157,22 +134,24 @@ macro_rules! construct_runtime { { $( $expanded )* $name: $module::{ - $( + Module, Call, Storage, Event, Config + $(, $modules $( <$modules_generic> )* $( ( $( $modules_args ),* ) )* - ),* + )* }, }; $( $rest )* ); }; - ( // Instance module: we indicate the generic instance `I` with the full instance path + // Take all modules as given by the user. + ( { $( $preset:tt )* }; { $( $expanded:tt )* }; - $name:ident: $module:ident ::< $module_instance:ident >::{ + $name:ident: $module:ident :: $( < $module_instance:ident >:: )? { $( $modules:ident - $( <$modules_generic:ident $(, $modules_instance:ident )?> )* + $( <$modules_generic:ident> )* $( ( $( $modules_args:ident ),* ) )* ),* }, @@ -182,9 +161,9 @@ macro_rules! construct_runtime { { $( $preset )* }; { $( $expanded )* - $name: $module::<$module_instance>::{ + $name: $module:: $( < $module_instance >:: )? { $( - $modules $( <$modules_generic $(, $modules_instance=$module::$module_instance)?> )* + $modules $( <$modules_generic> )* $( ( $( $modules_args ),* ) )* ),* }, @@ -192,9 +171,7 @@ macro_rules! construct_runtime { $( $rest )* ); }; - // The main macro expansion that actually renders the Runtime code. - ( { $runtime:ident; @@ -207,7 +184,7 @@ macro_rules! construct_runtime { $name:ident: $module:ident :: $( < $module_instance:ident >:: )? { $( $modules:ident - $( <$modules_generic:ident $(, I=$modules_instance:path)?> )* + $( <$modules_generic:ident> )* $( ( $( $modules_args:ident ),* ) )* ),* }, @@ -223,19 +200,20 @@ macro_rules! construct_runtime { impl $crate::runtime_primitives::traits::GetRuntimeBlockType for $runtime { type RuntimeBlock = $block; } - $crate::__decl_instance_import!( - $( $( $module < $module_instance > )? )* - ); $crate::__decl_outer_event!( $runtime; $( - $name: $module:: $( < $module_instance >:: )? { $( $modules $( <$modules_generic $(, $modules_instance)?> )* ),* } + $name: $module:: $( < $module_instance >:: )? { + $( $modules $( <$modules_generic> )* ),* + } ),* ); $crate::__decl_outer_origin!( $runtime; $( - $name: $module:: $( < $module_instance >:: )? { $( $modules $( <$modules_generic $(, $modules_instance)?> )* ),* } + $name: $module:: $( < $module_instance >:: )? { + $( $modules $( <$modules_generic> )* ),* + } ),* ); $crate::__decl_all_modules!( @@ -265,7 +243,7 @@ macro_rules! construct_runtime { {}; $( $name: $module:: $( < $module_instance >:: )? { - $( $modules $( <$modules_generic $(, $modules_instance)?> )* ),* + $( $modules $( <$modules_generic> )* ),* }, )* ); @@ -307,7 +285,7 @@ macro_rules! __create_decl_macro { ( $runtime:ident; $d( $name:ident : $module:ident:: $d( < $module_instance:ident >:: )? { - $d( $modules:ident $d( <$modules_generic:ident $d(, $modules_instance:path)?> ),* ),* + $d( $modules:ident $d( <$modules_generic:ident> ),* ),* }),* ) => { $d crate::$macro_name!(@inner @@ -316,7 +294,7 @@ macro_rules! __create_decl_macro { {}; $d( $name: $module:: $d( < $module_instance >:: )? { - $d( $modules $d( <$modules_generic $d(, $modules_instance)?> )* ),* + $d( $modules $d( <$modules_generic> )* ),* }, )* ); @@ -342,7 +320,7 @@ macro_rules! __create_decl_macro { $d( $system:ident )?; { $d( $parsed:tt )* }; $name:ident : $module:ident:: < $module_instance:ident >:: { - $macro_enum_name <$event_generic:ident, $event_instance:path> $d(, $ignore:ident $d( <$ignor:ident $d(, $ignore_instance:path)?> )* )* + $macro_enum_name <$event_generic:ident> $d(, $ingore:ident $d( <$ignor:ident> )* )* }, $d( $rest:tt )* ) => { @@ -351,33 +329,17 @@ macro_rules! __create_decl_macro { $d( $system )?; { $d( $parsed )* - $module $module_instance <$event_generic, $event_instance>, + $module $module_instance <$event_generic>, }; $d( $rest )* ); }; - (@inner - $runtime:ident; - $d( $system:ident )?; - { $d( $parsed:tt )* }; - $name:ident : $module:ident:: < $module_instance:ident >:: { - $macro_enum_name $d( <$event_generic:ident> )* $d(, $ignore:ident $d( <$ignor:ident $d(, $ignore_instance:path)?> )* )* - }, - $d( $rest:tt )* - ) => { - compile_error!{concat!{ - "Module `", stringify!{$name}, "` must have `", stringify!{$macro_enum_name}, "`", - " but has `", stringify!{$macro_enum_name} $d(, "<", stringify!{$event_generic}, ">")*, "`", - ": Instantiated modules must have ", stringify!{$macro_enum_name}, - " generic over instance to be able to convert to outer ", stringify!{$macro_enum_name} - }} - }; (@inner $runtime:ident; $d( $system:ident )?; { $d( $parsed:tt )* }; $name:ident : $module:ident:: { - $macro_enum_name $d( <$event_generic:ident $d(, $event_instance:path)?> )* $d(, $ignore:ident $d( <$ignor:ident $d(, $ignore_instance:path)?> )* )* + $macro_enum_name $d( <$event_generic:ident> )* $d(, $ignore:ident $d( <$ignor:ident> )* )* }, $d( $rest:tt )* ) => { @@ -386,7 +348,7 @@ macro_rules! __create_decl_macro { $d( $system )?; { $d( $parsed )* - $module $d( <$event_generic $d(, $event_instance)?> )*, + $module $d( <$event_generic> )*, }; $d( $rest )* ); @@ -396,7 +358,7 @@ macro_rules! __create_decl_macro { $d( $system:ident )?; { $d( $parsed:tt )* }; $name:ident : $module:ident:: $d( < $module_instance:ident >:: )? { - $ignore:ident $d( <$ignor:ident $d(, $ignore_instance:path)?> )* $d(, $modules:ident $d( <$modules_generic:ident $d(, $modules_instance:path)?> )* )* + $ingore:ident $d( <$ignor:ident> )* $d(, $modules:ident $d( <$modules_generic:ident> )* )* }, $d( $rest:tt )* ) => { @@ -404,7 +366,7 @@ macro_rules! __create_decl_macro { $runtime; $d( $system )?; { $d( $parsed )* }; - $name: $module:: $d( < $module_instance >:: )? { $d( $modules $d( <$modules_generic $d(, $modules_instance)?> )* ),* }, + $name: $module:: $d( < $module_instance >:: )? { $d( $modules $d( <$modules_generic> )* ),* }, $d( $rest )* ); }; @@ -425,16 +387,13 @@ macro_rules! __create_decl_macro { (@inner $runtime:ident; $system:ident; - { $d( $parsed_modules:ident $d( $instance:ident )? $d( <$parsed_generic:ident $d(, $parsed_instance_full_path:path)?> )* ,)* }; + { $d( $parsed_modules:ident $d( $instance:ident )? $d( <$parsed_generic:ident> )? ,)* }; ) => { - $d crate::paste::item! { - $d crate::$macro_outer_name! { - - pub enum $macro_enum_name for $runtime where system = $system { - $d( - [< $parsed_modules $d(_ $instance )? >] $d( <$parsed_generic $d(, $parsed_instance_full_path)?> )*, - )* - } + $d crate::$macro_outer_name! { + pub enum $macro_enum_name for $runtime where system = $system { + $d( + $parsed_modules $d( $instance )? $d( <$parsed_generic> )?, + )* } } } @@ -668,7 +627,7 @@ macro_rules! __decl_runtime_metadata { } } -/// A private macro that generates GenesisConfig for the runtime. See impl_outer_config macro. +/// A private macro that generates GenesisConfig for the runtime. See `impl_outer_config!` macro. #[macro_export] #[doc(hidden)] macro_rules! __decl_outer_config { @@ -676,7 +635,8 @@ macro_rules! __decl_outer_config { $runtime:ident; { $( $parsed:tt )* }; $name:ident: $module:ident:: $( < $module_instance:ident >:: )? { - Config $(< $config_generic:ident $(, $config_instance:path)?>)? $(, $modules:ident $( <$modules_generic:ident $(, $modules_instance:path)?> )* )* + Config $( <$config_generic:ident> )? + $(, $modules:ident $( <$modules_generic:ident> )* )* }, $( $rest:tt )* ) => { @@ -684,7 +644,7 @@ macro_rules! __decl_outer_config { $runtime; { $( $parsed )* - $module::$name $( $module_instance )? $(<$config_generic $(, $config_instance)?>)?, + $module::$name $( $module_instance )? $( <$config_generic> )?, }; $( $rest )* ); @@ -693,14 +653,15 @@ macro_rules! __decl_outer_config { $runtime:ident; { $( $parsed:tt )* }; $name:ident: $module:ident:: $( < $module_instance:ident >:: )? { - $ignore:ident $( <$ignor:ident $(, $ignore_instance:path)?> )* $(, $modules:ident $( <$modules_generic:ident $(, $modules_instance:path)?> )* )* + $ingore:ident $( <$ignore_gen:ident> )* + $(, $modules:ident $( <$modules_generic:ident> )* )* }, $( $rest:tt )* ) => { $crate::__decl_outer_config!( $runtime; { $( $parsed )* }; - $name: $module:: $( < $module_instance >:: )? { $( $modules $( <$modules_generic $(, $modules_instance)?> )* ),* }, + $name: $module:: $( < $module_instance >:: )? { $( $modules $( <$modules_generic> )* ),* }, $( $rest )* ); }; @@ -718,13 +679,21 @@ macro_rules! __decl_outer_config { }; ( $runtime:ident; - {$( $parsed_modules:ident :: $parsed_name:ident $( $parsed_instance:ident )? $( < $parsed_generic:ident $(, $parsed_instance_full_path:path)? > )* ,)* }; + { + $( + $parsed_modules:ident :: $parsed_name:ident $( $parsed_instance:ident )? + $( + <$parsed_generic:ident> + )* + ,)* + }; ) => { $crate::paste::item! { $crate::runtime_primitives::impl_outer_config!( pub struct GenesisConfig for $runtime { $( - [< $parsed_name Config >] => [< $parsed_modules $( _ $parsed_instance)? >] $( < $parsed_generic $(, $parsed_instance_full_path)? > )*, + [< $parsed_name Config >] => + $parsed_modules $( $parsed_instance )? $( <$parsed_generic> )*, )* } ); diff --git a/srml/support/src/storage/child.rs b/srml/support/src/storage/child.rs new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/srml/support/src/storage/hashed/generator.rs b/srml/support/src/storage/hashed/generator.rs index 5a8b2f9d8f54b0fe4f51edb715aafa8614bf589a..4d367fb2a870a38c8b64f2324bc80ae51516aff9 100644 --- a/srml/support/src/storage/hashed/generator.rs +++ b/srml/support/src/storage/hashed/generator.rs @@ -16,7 +16,7 @@ //! Abstract storage to use on HashedStorage trait -use crate::codec; +use crate::codec::{self, Encode}; use crate::rstd::prelude::{Vec, Box}; #[cfg(feature = "std")] use crate::storage::unhashed::generator::UnhashedStorage; @@ -27,7 +27,7 @@ pub trait StorageHasher: 'static { fn hash(x: &[u8]) -> Self::Output; } -/// Hash storage keys with `concat(twox128(key), key)` +/// Hash storage keys with `concat(twox64(key), key)` pub struct Twox64Concat; impl StorageHasher for Twox64Concat { type Output = Vec; @@ -184,6 +184,13 @@ pub trait StorageValue { storage.put(Self::key(), val) } + /// Store a value under this key into the provided storage instance; this can take any reference + /// type that derefs to `T` (and has `Encode` implemented). + /// Store a value under this key into the provided storage instance. + fn put_ref>(val: &Arg, storage: &mut S) where T: AsRef { + val.using_encoded(|b| storage.put_raw(Self::key(), b)) + } + /// Mutate this value fn mutate R, S: HashedStorage>(f: F, storage: &mut S) -> R; @@ -236,6 +243,17 @@ pub trait StorageMap { storage.put(&Self::key_for(key)[..], val); } + /// Store a value under this key into the provided storage instance; this can take any reference + /// type that derefs to `T` (and has `Encode` implemented). + /// Store a value under this key into the provided storage instance. + fn insert_ref>( + key: &K, + val: &Arg, + storage: &mut S + ) where V: AsRef { + val.using_encoded(|b| storage.put_raw(&Self::key_for(key)[..], b)) + } + /// Remove the value under a key. fn remove>(key: &K, storage: &mut S) { storage.kill(&Self::key_for(key)[..]); diff --git a/srml/support/src/storage/mod.rs b/srml/support/src/storage/mod.rs index a1891dade3b0c5b4010824c0bff8e641e18f9194..446855b55c187dce546f7eb257d3edd0830560d3 100644 --- a/srml/support/src/storage/mod.rs +++ b/srml/support/src/storage/mod.rs @@ -104,7 +104,7 @@ impl UnhashedStorage for RuntimeStorage { } /// Put a value in under a key. - fn put(&mut self, key: &[u8], val: &T) { + fn put(&mut self, key: &[u8], val: &T) { unhashed::put(key, val) } @@ -149,6 +149,10 @@ pub trait StorageValue { /// Store a value under this key into the provided storage instance. fn put>(val: Arg); + /// Store a value under this key into the provided storage instance; this can take any reference + /// type that derefs to `T` (and has `Encode` implemented). + fn put_ref(val: &Arg) where T: AsRef; + /// Mutate the value fn mutate R>(f: F) -> R; @@ -180,6 +184,9 @@ impl StorageValue for U where U: hashed::generator::StorageValue fn put>(val: Arg) { U::put(val.borrow(), &mut RuntimeStorage) } + fn put_ref(val: &Arg) where T: AsRef { + U::put_ref(val, &mut RuntimeStorage) + } fn mutate R>(f: F) -> R { U::mutate(f, &mut RuntimeStorage) } @@ -216,6 +223,10 @@ pub trait StorageMap { /// Store a value to be associated with the given key from the map. fn insert, ValArg: Borrow>(key: KeyArg, val: ValArg); + /// Store a value under this key into the provided storage instance; this can take any reference + /// type that derefs to `T` (and has `Encode` implemented). + fn insert_ref, ValArg: ?Sized + Encode>(key: KeyArg, val: &ValArg) where V: AsRef; + /// Remove the value under a key. fn remove>(key: KeyArg); @@ -249,6 +260,10 @@ impl StorageMap for U where U: hashed::generator::S U::insert(key.borrow(), val.borrow(), &mut RuntimeStorage) } + fn insert_ref, ValArg: ?Sized + Encode>(key: KeyArg, val: &ValArg) where V: AsRef { + U::insert_ref(key.borrow(), val, &mut RuntimeStorage) + } + fn remove>(key: KeyArg) { U::remove(key.borrow(), &mut RuntimeStorage) } @@ -317,60 +332,83 @@ impl EnumerableStorageMap for U /// is a hash of a `Key2`. /// /// /!\ be careful while choosing the Hash, indeed malicious could craft second keys to lower the trie. -pub trait StorageDoubleMap { +pub trait StorageDoubleMap { /// The type that get/take returns. type Query; - /// Get the prefix key in storage. fn prefix() -> &'static [u8]; - /// Get the storage key used to fetch a value corresponding to a specific key. - fn key_for, KArg2: Borrow>(k1: KArg1, k2: KArg2) -> Vec; - - /// Get the storage prefix used to fetch keys corresponding to a specific key1. - fn prefix_for>(k1: KArg1) -> Vec; + fn key_for(k1: &KArg1, k2: &KArg2) -> Vec + where + K1: Borrow, + K2: Borrow, + KArg1: ?Sized + Encode, + KArg2: ?Sized + Encode; - /// true if the value is defined in storage. - fn exists, KArg2: Borrow>(k1: KArg1, k2: KArg2) -> bool; + fn prefix_for(k1: &KArg1) -> Vec where KArg1: ?Sized + Encode, K1: Borrow; - /// Load the value associated with the given key from the map. - fn get, KArg2: Borrow>(k1: KArg1, k2: KArg2) -> Self::Query; + fn exists(k1: &KArg1, k2: &KArg2) -> bool + where + K1: Borrow, + K2: Borrow, + KArg1: ?Sized + Encode, + KArg2: ?Sized + Encode; - /// Take the value under a key. - fn take, KArg2: Borrow>(k1: KArg1, k2: KArg2) -> Self::Query; + fn get(k1: &KArg1, k2: &KArg2) -> Self::Query + where + K1: Borrow, + K2: Borrow, + KArg1: ?Sized + Encode, + KArg2: ?Sized + Encode; - /// Store a value to be associated with the given key from the map. - fn insert, KArg2: Borrow, VArg: Borrow>(k1: KArg1, k2: KArg2, val: VArg); + fn take(k1: &KArg1, k2: &KArg2) -> Self::Query + where + K1: Borrow, + K2: Borrow, + KArg1: ?Sized + Encode, + KArg2: ?Sized + Encode; - /// Remove the value under a key. - fn remove, KArg2: Borrow>(k1: KArg1, k2: KArg2); + fn insert(k1: &KArg1, k2: &KArg2, val: &VArg) + where + K1: Borrow, + K2: Borrow, + V: Borrow, + KArg1: ?Sized + Encode, + KArg2: ?Sized + Encode, + VArg: ?Sized + Encode; + + fn remove(k1: &KArg1, k2: &KArg2) + where + K1: Borrow, + K2: Borrow, + KArg1: ?Sized + Encode, + KArg2: ?Sized + Encode; - /// Removes all entries that shares the `k1` as the first key. - fn remove_prefix>(k1: KArg1); + fn remove_prefix(k1: &KArg1) where KArg1: ?Sized + Encode, K1: Borrow; - /// Mutate the value under a key. - fn mutate(k1: KArg1, k2: KArg2, f: F) -> R + fn mutate(k1: &KArg1, k2: &KArg2, f: F) -> R where - KArg1: Borrow, - KArg2: Borrow, + K1: Borrow, + K2: Borrow, + KArg1: ?Sized + Encode, + KArg2: ?Sized + Encode, F: FnOnce(&mut Self::Query) -> R; - /// Append the given items to the value under the key specified. - /// - /// `V` is required to implement `codec::EncodeAppend`. fn append( - k1: KArg1, - k2: KArg2, + k1: &KArg1, + k2: &KArg2, items: &[I], ) -> Result<(), &'static str> where - KArg1: Borrow, - KArg2: Borrow, + K1: Borrow, + K2: Borrow, + KArg1: ?Sized + Encode, + KArg2: ?Sized + Encode, I: codec::Encode, V: EncodeAppend; } -impl StorageDoubleMap for U +impl StorageDoubleMap for U where U: unhashed::generator::StorageDoubleMap { @@ -380,59 +418,101 @@ where >::prefix() } - fn key_for, KArg2: Borrow>(k1: KArg1, k2: KArg2) -> Vec { - >::key_for(k1.borrow(), k2.borrow()) + fn key_for(k1: &KArg1, k2: &KArg2) -> Vec + where + K1: Borrow, + K2: Borrow, + KArg1: ?Sized + Encode, + KArg2: ?Sized + Encode, + { + >::key_for(k1, k2) } - fn prefix_for>(k1: KArg1) -> Vec { - >::prefix_for(k1.borrow()) + fn prefix_for(k1: &KArg1) -> Vec where KArg1: ?Sized + Encode, K1: Borrow { + >::prefix_for(k1) } - fn exists, KArg2: Borrow>(k1: KArg1, k2: KArg2) -> bool { - U::exists(k1.borrow(), k2.borrow(), &RuntimeStorage) + fn exists(k1: &KArg1, k2: &KArg2) -> bool + where + K1: Borrow, + K2: Borrow, + KArg1: ?Sized + Encode, + KArg2: ?Sized + Encode, + { + U::exists(k1, k2, &RuntimeStorage) } - fn get, KArg2: Borrow>(k1: KArg1, k2: KArg2) -> Self::Query { - U::get(k1.borrow(), k2.borrow(), &RuntimeStorage) + fn get(k1: &KArg1, k2: &KArg2) -> Self::Query + where + K1: Borrow, + K2: Borrow, + KArg1: ?Sized + Encode, + KArg2: ?Sized + Encode, + { + U::get(k1, k2, &RuntimeStorage) } - fn take, KArg2: Borrow>(k1: KArg1, k2: KArg2) -> Self::Query { + fn take(k1: &KArg1, k2: &KArg2) -> Self::Query + where + K1: Borrow, + K2: Borrow, + KArg1: ?Sized + Encode, + KArg2: ?Sized + Encode, + { U::take(k1.borrow(), k2.borrow(), &mut RuntimeStorage) } - fn insert, KArg2: Borrow, VArg: Borrow>(k1: KArg1, k2: KArg2, val: VArg) { - U::insert(k1.borrow(), k2.borrow(), val.borrow(), &mut RuntimeStorage) + fn insert(k1: &KArg1, k2: &KArg2, val: &VArg) + where + K1: Borrow, + K2: Borrow, + V: Borrow, + KArg1: ?Sized + Encode, + KArg2: ?Sized + Encode, + VArg: ?Sized + Encode, + { + U::insert(k1, k2, val, &mut RuntimeStorage) } - fn remove, KArg2: Borrow>(k1: KArg1, k2: KArg2) { - U::remove(k1.borrow(), k2.borrow(), &mut RuntimeStorage) + fn remove(k1: &KArg1, k2: &KArg2) + where + K1: Borrow, + K2: Borrow, + KArg1: ?Sized + Encode, + KArg2: ?Sized + Encode, + { + U::remove(k1, k2, &mut RuntimeStorage) } - fn remove_prefix>(k1: KArg1) { - U::remove_prefix(k1.borrow(), &mut RuntimeStorage) + fn remove_prefix(k1: &KArg1) where KArg1: ?Sized + Encode, K1: Borrow { + U::remove_prefix(k1, &mut RuntimeStorage) } - fn mutate(k1: KArg1, k2: KArg2, f: F) -> R + fn mutate(k1: &KArg1, k2: &KArg2, f: F) -> R where - KArg1: Borrow, - KArg2: Borrow, + K1: Borrow, + K2: Borrow, + KArg1: ?Sized + Encode, + KArg2: ?Sized + Encode, F: FnOnce(&mut Self::Query) -> R { - U::mutate(k1.borrow(), k2.borrow(), f, &mut RuntimeStorage) + U::mutate(k1, k2, f, &mut RuntimeStorage) } fn append( - k1: KArg1, - k2: KArg2, + k1: &KArg1, + k2: &KArg2, items: &[I], ) -> Result<(), &'static str> where - KArg1: Borrow, - KArg2: Borrow, + K1: Borrow, + K2: Borrow, + KArg1: ?Sized + Encode, + KArg2: ?Sized + Encode, I: codec::Encode, V: EncodeAppend, { - U::append(k1.borrow(), k2.borrow(), items, &mut RuntimeStorage) + U::append(k1, k2, items, &mut RuntimeStorage) } } diff --git a/srml/support/src/storage/storage_items.rs b/srml/support/src/storage/storage_items.rs index 9d89b81e0d950b224f45e31e90aa460a35948230..f67dbf0529419c1d8790d1ab7d932b13c1ce6ad3 100644 --- a/srml/support/src/storage/storage_items.rs +++ b/srml/support/src/storage/storage_items.rs @@ -346,7 +346,7 @@ 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; + 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 = Some(4); @@ -390,333 +390,331 @@ mod tests { type BlockNumber = u32; } - const EXPECTED_METADATA: StorageMetadata = StorageMetadata { - functions: DecodeDifferent::Encode(&[ - StorageFunctionMetadata { - name: DecodeDifferent::Encode("U32"), - modifier: StorageFunctionModifier::Optional, - ty: StorageFunctionType::Plain(DecodeDifferent::Encode("u32")), - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructU32(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[ " Hello, this is doc!" ]), + const EXPECTED_METADATA: &[StorageEntryMetadata] = &[ + StorageEntryMetadata { + name: DecodeDifferent::Encode("U32"), + modifier: StorageEntryModifier::Optional, + ty: StorageEntryType::Plain(DecodeDifferent::Encode("u32")), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructU32(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[ " Hello, this is doc!" ]), + }, + StorageEntryMetadata { + name: DecodeDifferent::Encode("PUBU32"), + modifier: StorageEntryModifier::Optional, + ty: StorageEntryType::Plain(DecodeDifferent::Encode("u32")), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructPUBU32(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageEntryMetadata { + name: DecodeDifferent::Encode("U32MYDEF"), + modifier: StorageEntryModifier::Optional, + ty: StorageEntryType::Plain(DecodeDifferent::Encode("u32")), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructU32MYDEF(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageEntryMetadata { + name: DecodeDifferent::Encode("PUBU32MYDEF"), + modifier: StorageEntryModifier::Optional, + ty: StorageEntryType::Plain(DecodeDifferent::Encode("u32")), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructPUBU32MYDEF(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageEntryMetadata { + name: DecodeDifferent::Encode("GETU32"), + modifier: StorageEntryModifier::Default, + ty: StorageEntryType::Plain(DecodeDifferent::Encode("T::Origin")), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructGETU32(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageEntryMetadata { + name: DecodeDifferent::Encode("PUBGETU32"), + modifier: StorageEntryModifier::Default, + ty: StorageEntryType::Plain(DecodeDifferent::Encode("u32")), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructPUBGETU32(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageEntryMetadata { + name: DecodeDifferent::Encode("GETU32WITHCONFIG"), + modifier: StorageEntryModifier::Default, + ty: StorageEntryType::Plain(DecodeDifferent::Encode("u32")), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructGETU32WITHCONFIG(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageEntryMetadata { + name: DecodeDifferent::Encode("PUBGETU32WITHCONFIG"), + modifier: StorageEntryModifier::Default, + ty: StorageEntryType::Plain(DecodeDifferent::Encode("u32")), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructPUBGETU32WITHCONFIG(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageEntryMetadata { + name: DecodeDifferent::Encode("GETU32MYDEF"), + modifier: StorageEntryModifier::Optional, + ty: StorageEntryType::Plain(DecodeDifferent::Encode("u32")), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructGETU32MYDEF(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageEntryMetadata { + name: DecodeDifferent::Encode("PUBGETU32MYDEF"), + modifier: StorageEntryModifier::Default, + ty: StorageEntryType::Plain(DecodeDifferent::Encode("u32")), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructPUBGETU32MYDEF(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageEntryMetadata { + name: DecodeDifferent::Encode("GETU32WITHCONFIGMYDEF"), + modifier: StorageEntryModifier::Default, + ty: StorageEntryType::Plain(DecodeDifferent::Encode("u32")), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructGETU32WITHCONFIGMYDEF(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageEntryMetadata { + name: DecodeDifferent::Encode("PUBGETU32WITHCONFIGMYDEF"), + modifier: StorageEntryModifier::Default, + ty: StorageEntryType::Plain(DecodeDifferent::Encode("u32")), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructPUBGETU32WITHCONFIGMYDEF(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageEntryMetadata { + name: DecodeDifferent::Encode("PUBGETU32WITHCONFIGMYDEFOPT"), + modifier: StorageEntryModifier::Optional, + ty: StorageEntryType::Plain(DecodeDifferent::Encode("u32")), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructPUBGETU32WITHCONFIGMYDEFOPT(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + + StorageEntryMetadata { + name: DecodeDifferent::Encode("MAPU32"), + modifier: StorageEntryModifier::Optional, + ty: StorageEntryType::Map { + hasher: StorageHasher::Blake2_256, + key: DecodeDifferent::Encode("u32"), + value: DecodeDifferent::Encode("String"), + is_linked: false, }, - StorageFunctionMetadata { - name: DecodeDifferent::Encode("PUBU32"), - modifier: StorageFunctionModifier::Optional, - ty: StorageFunctionType::Plain(DecodeDifferent::Encode("u32")), - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructPUBU32(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructMAPU32(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageEntryMetadata { + name: DecodeDifferent::Encode("PUBMAPU32"), + modifier: StorageEntryModifier::Optional, + ty: StorageEntryType::Map { + hasher: StorageHasher::Blake2_256, + key: DecodeDifferent::Encode("u32"), + value: DecodeDifferent::Encode("String"), + is_linked: false, }, - StorageFunctionMetadata { - name: DecodeDifferent::Encode("U32MYDEF"), - modifier: StorageFunctionModifier::Optional, - ty: StorageFunctionType::Plain(DecodeDifferent::Encode("u32")), - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructU32MYDEF(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructPUBMAPU32(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageEntryMetadata { + name: DecodeDifferent::Encode("MAPU32MYDEF"), + modifier: StorageEntryModifier::Optional, + ty: StorageEntryType::Map { + hasher: StorageHasher::Blake2_256, + key: DecodeDifferent::Encode("u32"), + value: DecodeDifferent::Encode("String"), + is_linked: false, }, - StorageFunctionMetadata { - name: DecodeDifferent::Encode("PUBU32MYDEF"), - modifier: StorageFunctionModifier::Optional, - ty: StorageFunctionType::Plain(DecodeDifferent::Encode("u32")), - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructPUBU32MYDEF(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructMAPU32MYDEF(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageEntryMetadata { + name: DecodeDifferent::Encode("PUBMAPU32MYDEF"), + modifier: StorageEntryModifier::Optional, + ty: StorageEntryType::Map { + hasher: StorageHasher::Blake2_256, + key: DecodeDifferent::Encode("u32"), + value: DecodeDifferent::Encode("String"), + is_linked: false, }, - StorageFunctionMetadata { - name: DecodeDifferent::Encode("GETU32"), - modifier: StorageFunctionModifier::Default, - ty: StorageFunctionType::Plain(DecodeDifferent::Encode("T::Origin")), - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructGETU32(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructPUBMAPU32MYDEF(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageEntryMetadata { + name: DecodeDifferent::Encode("GETMAPU32"), + modifier: StorageEntryModifier::Default, + ty: StorageEntryType::Map { + hasher: StorageHasher::Blake2_256, + key: DecodeDifferent::Encode("u32"), + value: DecodeDifferent::Encode("String"), + is_linked: false, }, - StorageFunctionMetadata { - name: DecodeDifferent::Encode("PUBGETU32"), - modifier: StorageFunctionModifier::Default, - ty: StorageFunctionType::Plain(DecodeDifferent::Encode("u32")), - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructPUBGETU32(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructGETMAPU32(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageEntryMetadata { + name: DecodeDifferent::Encode("PUBGETMAPU32"), + modifier: StorageEntryModifier::Default, + ty: StorageEntryType::Map { + hasher: StorageHasher::Blake2_256, + key: DecodeDifferent::Encode("u32"), + value: DecodeDifferent::Encode("String"), + is_linked: false, }, - StorageFunctionMetadata { - name: DecodeDifferent::Encode("GETU32WITHCONFIG"), - modifier: StorageFunctionModifier::Default, - ty: StorageFunctionType::Plain(DecodeDifferent::Encode("u32")), - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructGETU32WITHCONFIG(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructPUBGETMAPU32(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageEntryMetadata { + name: DecodeDifferent::Encode("GETMAPU32MYDEF"), + modifier: StorageEntryModifier::Default, + ty: StorageEntryType::Map { + hasher: StorageHasher::Blake2_256, + key: DecodeDifferent::Encode("u32"), + value: DecodeDifferent::Encode("String"), + is_linked: false, }, - StorageFunctionMetadata { - name: DecodeDifferent::Encode("PUBGETU32WITHCONFIG"), - modifier: StorageFunctionModifier::Default, - ty: StorageFunctionType::Plain(DecodeDifferent::Encode("u32")), - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructPUBGETU32WITHCONFIG(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructGETMAPU32MYDEF(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageEntryMetadata { + name: DecodeDifferent::Encode("PUBGETMAPU32MYDEF"), + modifier: StorageEntryModifier::Default, + ty: StorageEntryType::Map { + hasher: StorageHasher::Blake2_256, + key: DecodeDifferent::Encode("u32"), + value: DecodeDifferent::Encode("String"), + is_linked: false, }, - StorageFunctionMetadata { - name: DecodeDifferent::Encode("GETU32MYDEF"), - modifier: StorageFunctionModifier::Optional, - ty: StorageFunctionType::Plain(DecodeDifferent::Encode("u32")), - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructGETU32MYDEF(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructPUBGETMAPU32MYDEF(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageEntryMetadata { + name: DecodeDifferent::Encode("LINKEDMAPU32"), + modifier: StorageEntryModifier::Optional, + ty: StorageEntryType::Map { + hasher: StorageHasher::Blake2_256, + key: DecodeDifferent::Encode("u32"), + value: DecodeDifferent::Encode("String"), + is_linked: true, }, - StorageFunctionMetadata { - name: DecodeDifferent::Encode("PUBGETU32MYDEF"), - modifier: StorageFunctionModifier::Default, - ty: StorageFunctionType::Plain(DecodeDifferent::Encode("u32")), - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructPUBGETU32MYDEF(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructLINKEDMAPU32(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageEntryMetadata { + name: DecodeDifferent::Encode("PUBLINKEDMAPU32MYDEF"), + modifier: StorageEntryModifier::Optional, + ty: StorageEntryType::Map { + hasher: StorageHasher::Blake2_256, + key: DecodeDifferent::Encode("u32"), + value: DecodeDifferent::Encode("String"), + is_linked: true, }, - StorageFunctionMetadata { - name: DecodeDifferent::Encode("GETU32WITHCONFIGMYDEF"), - modifier: StorageFunctionModifier::Default, - ty: StorageFunctionType::Plain(DecodeDifferent::Encode("u32")), - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructGETU32WITHCONFIGMYDEF(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructPUBLINKEDMAPU32MYDEF(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageEntryMetadata { + name: DecodeDifferent::Encode("GETLINKEDMAPU32"), + modifier: StorageEntryModifier::Default, + ty: StorageEntryType::Map { + hasher: StorageHasher::Blake2_256, + key: DecodeDifferent::Encode("u32"), + value: DecodeDifferent::Encode("String"), + is_linked: true, }, - StorageFunctionMetadata { - name: DecodeDifferent::Encode("PUBGETU32WITHCONFIGMYDEF"), - modifier: StorageFunctionModifier::Default, - ty: StorageFunctionType::Plain(DecodeDifferent::Encode("u32")), - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructPUBGETU32WITHCONFIGMYDEF(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructGETLINKEDMAPU32(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageEntryMetadata { + name: DecodeDifferent::Encode("PUBGETLINKEDMAPU32MYDEF"), + modifier: StorageEntryModifier::Default, + ty: StorageEntryType::Map { + hasher: StorageHasher::Blake2_256, + key: DecodeDifferent::Encode("u32"), + value: DecodeDifferent::Encode("String"), + is_linked: true, }, - StorageFunctionMetadata { - name: DecodeDifferent::Encode("PUBGETU32WITHCONFIGMYDEFOPT"), - modifier: StorageFunctionModifier::Optional, - ty: StorageFunctionType::Plain(DecodeDifferent::Encode("u32")), - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructPUBGETU32WITHCONFIGMYDEFOPT(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), - }, - - StorageFunctionMetadata { - name: DecodeDifferent::Encode("MAPU32"), - modifier: StorageFunctionModifier::Optional, - ty: StorageFunctionType::Map { - hasher: StorageHasher::Blake2_256, - key: DecodeDifferent::Encode("u32"), - value: DecodeDifferent::Encode("String"), - is_linked: false, - }, - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructMAPU32(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), - }, - StorageFunctionMetadata { - name: DecodeDifferent::Encode("PUBMAPU32"), - modifier: StorageFunctionModifier::Optional, - ty: StorageFunctionType::Map { - hasher: StorageHasher::Blake2_256, - key: DecodeDifferent::Encode("u32"), - value: DecodeDifferent::Encode("String"), - is_linked: false, - }, - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructPUBMAPU32(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), - }, - StorageFunctionMetadata { - name: DecodeDifferent::Encode("MAPU32MYDEF"), - modifier: StorageFunctionModifier::Optional, - ty: StorageFunctionType::Map { - hasher: StorageHasher::Blake2_256, - key: DecodeDifferent::Encode("u32"), - value: DecodeDifferent::Encode("String"), - is_linked: false, - }, - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructMAPU32MYDEF(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), - }, - StorageFunctionMetadata { - name: DecodeDifferent::Encode("PUBMAPU32MYDEF"), - modifier: StorageFunctionModifier::Optional, - ty: StorageFunctionType::Map { - hasher: StorageHasher::Blake2_256, - key: DecodeDifferent::Encode("u32"), - value: DecodeDifferent::Encode("String"), - is_linked: false, - }, - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructPUBMAPU32MYDEF(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), - }, - StorageFunctionMetadata { - name: DecodeDifferent::Encode("GETMAPU32"), - modifier: StorageFunctionModifier::Default, - ty: StorageFunctionType::Map { - hasher: StorageHasher::Blake2_256, - key: DecodeDifferent::Encode("u32"), - value: DecodeDifferent::Encode("String"), - is_linked: false, - }, - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructGETMAPU32(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), - }, - StorageFunctionMetadata { - name: DecodeDifferent::Encode("PUBGETMAPU32"), - modifier: StorageFunctionModifier::Default, - ty: StorageFunctionType::Map { - hasher: StorageHasher::Blake2_256, - key: DecodeDifferent::Encode("u32"), - value: DecodeDifferent::Encode("String"), - is_linked: false, - }, - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructPUBGETMAPU32(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), - }, - StorageFunctionMetadata { - name: DecodeDifferent::Encode("GETMAPU32MYDEF"), - modifier: StorageFunctionModifier::Default, - ty: StorageFunctionType::Map { - hasher: StorageHasher::Blake2_256, - key: DecodeDifferent::Encode("u32"), - value: DecodeDifferent::Encode("String"), - is_linked: false, - }, - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructGETMAPU32MYDEF(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), - }, - StorageFunctionMetadata { - name: DecodeDifferent::Encode("PUBGETMAPU32MYDEF"), - modifier: StorageFunctionModifier::Default, - ty: StorageFunctionType::Map { - hasher: StorageHasher::Blake2_256, - key: DecodeDifferent::Encode("u32"), - value: DecodeDifferent::Encode("String"), - is_linked: false, - }, - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructPUBGETMAPU32MYDEF(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), - }, - StorageFunctionMetadata { - name: DecodeDifferent::Encode("LINKEDMAPU32"), - modifier: StorageFunctionModifier::Optional, - ty: StorageFunctionType::Map { - hasher: StorageHasher::Blake2_256, - key: DecodeDifferent::Encode("u32"), - value: DecodeDifferent::Encode("String"), - is_linked: true, - }, - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructLINKEDMAPU32(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), - }, - StorageFunctionMetadata { - name: DecodeDifferent::Encode("PUBLINKEDMAPU32MYDEF"), - modifier: StorageFunctionModifier::Optional, - ty: StorageFunctionType::Map { - hasher: StorageHasher::Blake2_256, - key: DecodeDifferent::Encode("u32"), - value: DecodeDifferent::Encode("String"), - is_linked: true, - }, - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructPUBLINKEDMAPU32MYDEF(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), - }, - StorageFunctionMetadata { - name: DecodeDifferent::Encode("GETLINKEDMAPU32"), - modifier: StorageFunctionModifier::Default, - ty: StorageFunctionType::Map { - hasher: StorageHasher::Blake2_256, - key: DecodeDifferent::Encode("u32"), - value: DecodeDifferent::Encode("String"), - is_linked: true, - }, - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructGETLINKEDMAPU32(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), - }, - StorageFunctionMetadata { - name: DecodeDifferent::Encode("PUBGETLINKEDMAPU32MYDEF"), - modifier: StorageFunctionModifier::Default, - ty: StorageFunctionType::Map { - hasher: StorageHasher::Blake2_256, - key: DecodeDifferent::Encode("u32"), - value: DecodeDifferent::Encode("String"), - is_linked: true, - }, - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructPUBGETLINKEDMAPU32MYDEF(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), - }, - StorageFunctionMetadata { - name: DecodeDifferent::Encode("COMPLEXTYPE1"), - modifier: StorageFunctionModifier::Default, - ty: StorageFunctionType::Plain(DecodeDifferent::Encode("::std::vec::Vec<::Origin>")), - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructCOMPLEXTYPE1(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), - }, - StorageFunctionMetadata { - name: DecodeDifferent::Encode("COMPLEXTYPE2"), - modifier: StorageFunctionModifier::Default, - ty: StorageFunctionType::Plain(DecodeDifferent::Encode("(Vec)>>, u32)")), - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructCOMPLEXTYPE2(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), - }, - StorageFunctionMetadata { - name: DecodeDifferent::Encode("COMPLEXTYPE3"), - modifier: StorageFunctionModifier::Default, - ty: StorageFunctionType::Plain(DecodeDifferent::Encode("([u32; 25])")), - default: DecodeDifferent::Encode( - DefaultByteGetter(&__GetByteStructCOMPLEXTYPE3(PhantomData::)) - ), - documentation: DecodeDifferent::Encode(&[]), - }, - ]) - }; + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructPUBGETLINKEDMAPU32MYDEF(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageEntryMetadata { + name: DecodeDifferent::Encode("COMPLEXTYPE1"), + modifier: StorageEntryModifier::Default, + ty: StorageEntryType::Plain(DecodeDifferent::Encode("::std::vec::Vec<::Origin>")), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructCOMPLEXTYPE1(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageEntryMetadata { + name: DecodeDifferent::Encode("COMPLEXTYPE2"), + modifier: StorageEntryModifier::Default, + ty: StorageEntryType::Plain(DecodeDifferent::Encode("(Vec)>>, u32)")), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructCOMPLEXTYPE2(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageEntryMetadata { + name: DecodeDifferent::Encode("COMPLEXTYPE3"), + modifier: StorageEntryModifier::Default, + ty: StorageEntryType::Plain(DecodeDifferent::Encode("([u32; 25])")), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructCOMPLEXTYPE3(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + ]; #[test] fn store_metadata() { - let metadata = Module::::store_metadata(); + let metadata = Module::::store_metadata_functions(); assert_eq!(EXPECTED_METADATA, metadata); } #[test] fn check_genesis_config() { - let config = GenesisConfig::::default(); + let config = GenesisConfig::default(); assert_eq!(config.u32_getter_with_config, 0u32); assert_eq!(config.pub_u32_getter_with_config, 0u32); @@ -820,13 +818,13 @@ mod test_map_vec_append { use runtime_io::{with_externalities, TestExternalities}; with_externalities(&mut TestExternalities::default(), || { - let _ = >::append(1, &[1, 2, 3]); - let _ = >::append(1, &[4, 5]); - assert_eq!(>::get(1), vec![1, 2, 3, 4, 5]); + let _ = MapVec::append(1, &[1, 2, 3]); + let _ = MapVec::append(1, &[4, 5]); + assert_eq!(MapVec::get(1), vec![1, 2, 3, 4, 5]); - let _ = >::append(&[1, 2, 3]); - let _ = >::append(&[4, 5]); - assert_eq!(>::get(), vec![1, 2, 3, 4, 5]); + let _ = JustVec::append(&[1, 2, 3]); + let _ = JustVec::append(&[4, 5]); + assert_eq!(JustVec::get(), vec![1, 2, 3, 4, 5]); }); } } diff --git a/srml/support/src/storage/unhashed/generator.rs b/srml/support/src/storage/unhashed/generator.rs index 3c56ae0ac5fd4cb2119ece2d289031ff0e00d3fe..26cb4b73e5def6531bbe710869699e283fe648e4 100644 --- a/srml/support/src/storage/unhashed/generator.rs +++ b/srml/support/src/storage/unhashed/generator.rs @@ -14,8 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use crate::codec; -use crate::rstd::vec::Vec; +use crate::codec::{self, Encode, EncodeAppend}; +use crate::rstd::{borrow::Borrow, vec::Vec}; /// Abstraction around storage with unhashed access. pub trait UnhashedStorage { @@ -38,7 +38,7 @@ pub trait UnhashedStorage { } /// Put a value in under a key. - fn put(&mut self, key: &[u8], val: &T); + fn put(&mut self, key: &[u8], val: &T); /// Remove the bytes of a key from storage. fn kill(&mut self, key: &[u8]); @@ -82,7 +82,7 @@ impl UnhashedStorage for sr_primitives::StorageOverlay { .map(|x| codec::Decode::decode(&mut x.as_slice()).expect("Unable to decode expected type.")) } - fn put(&mut self, key: &[u8], val: &T) { + fn put(&mut self, key: &[u8], val: &T) { self.insert(key.to_vec(), codec::Encode::encode(val)); } @@ -117,7 +117,7 @@ impl UnhashedStorage for sr_primitives::StorageOverlay { /// is a hash of a `Key2`. /// /// /!\ be careful while choosing the Hash, indeed malicious could craft second keys to lower the trie. -pub trait StorageDoubleMap { +pub trait StorageDoubleMap { /// The type that get/take returns. type Query; @@ -125,50 +125,110 @@ pub trait StorageDoubleMap fn prefix() -> &'static [u8]; /// Get the storage key used to fetch a value corresponding to a specific key. - fn key_for(k1: &K1, k2: &K2) -> Vec; + fn key_for( + k1: &KArg1, + k2: &KArg2, + ) -> Vec where + K1: Borrow, + K2: Borrow, + KArg1: ?Sized + Encode, + KArg2: ?Sized + Encode; /// Get the storage prefix used to fetch keys corresponding to a specific key1. - fn prefix_for(k1: &K1) -> Vec; + fn prefix_for(k1: &KArg1) -> Vec where KArg1: ?Sized + Encode, K1: Borrow; /// true if the value is defined in storage. - fn exists(k1: &K1, k2: &K2, storage: &S) -> bool { + fn exists( + k1: &KArg1, + k2: &KArg2, + storage: &S, + ) -> bool where K1: Borrow, K2: Borrow, KArg1: ?Sized + Encode, KArg2: ?Sized + Encode { storage.exists(&Self::key_for(k1, k2)) } /// Load the value associated with the given key from the map. - fn get(k1: &K1, k2: &K2, storage: &S) -> Self::Query; + fn get( + k1: &KArg1, + k2: &KArg2, + storage: &S, + ) -> Self::Query where + K1: Borrow, + K2: Borrow, + KArg1: ?Sized + Encode, + KArg2: ?Sized + Encode; /// Take the value under a key. - fn take(k1: &K1, k2: &K2, storage: &mut S) -> Self::Query; + fn take( + k1: &KArg1, + k2: &KArg2, + storage: &mut S, + ) -> Self::Query where + K1: Borrow, + K2: Borrow, + KArg1: ?Sized + Encode, + KArg2: ?Sized + Encode; /// Store a value to be associated with the given key from the map. - fn insert(k1: &K1, k2: &K2, val: &V, storage: &mut S) { + fn insert( + k1: &KArg1, + k2: &KArg2, + val: &VArg, + storage: &mut S, + ) where + K1: Borrow, + K2: Borrow, + V: Borrow, + KArg1: ?Sized + Encode, + KArg2: ?Sized + Encode, + VArg: ?Sized + Encode, + { storage.put(&Self::key_for(k1, k2), val); } /// Remove the value under a key. - fn remove(k1: &K1, k2: &K2, storage: &mut S) { + fn remove( + k1: &KArg1, + k2: &KArg2, + storage: &mut S, + ) where K1: Borrow, K2: Borrow, KArg1: ?Sized + Encode, KArg2: ?Sized + Encode { storage.kill(&Self::key_for(k1, k2)); } /// Removes all entries that shares the `k1` as the first key. - fn remove_prefix(k1: &K1, storage: &mut S) { + fn remove_prefix( + k1: &KArg1, + storage: &mut S, + ) where KArg1: ?Sized + Encode, K1: Borrow { storage.kill_prefix(&Self::prefix_for(k1)); } /// Mutate the value under a key. - fn mutate R, S: UnhashedStorage>(k1: &K1, k2: &K2, f: F, storage: &mut S) -> R; + fn mutate( + k1: &KArg1, + k2: &KArg2, + f: F, + storage: &mut S, + ) -> R where + K1: Borrow, + K2: Borrow, + KArg1: ?Sized + Encode, + KArg2: ?Sized + Encode, + F: FnOnce(&mut Self::Query) -> R; /// Append the given items to the value under the key specified. - fn append( - k1: &K1, - k2: &K2, + fn append( + k1: &KArg1, + k2: &KArg2, items: &[I], storage: &mut S, ) -> Result<(), &'static str> where + K1: Borrow, + K2: Borrow, + KArg1: ?Sized + Encode, + KArg2: ?Sized + Encode, I: codec::Encode, - V: codec::EncodeAppend, + V: EncodeAppend, { let key = Self::key_for(k1, k2); let new_val = ::append( diff --git a/srml/support/src/storage/unhashed/mod.rs b/srml/support/src/storage/unhashed/mod.rs index 40e18d0cd212a3bd198745cab7af999175f92b9a..77e6c2b37b4197e552a68e701c3afec55168716f 100644 --- a/srml/support/src/storage/unhashed/mod.rs +++ b/srml/support/src/storage/unhashed/mod.rs @@ -51,7 +51,7 @@ pub fn get_or_else T>(key: &[u8], default_valu } /// Put `value` in storage under `key`. -pub fn put(key: &[u8], value: &T) { +pub fn put(key: &[u8], value: &T) { value.using_encoded(|slice| runtime_io::set_storage(key, slice)); } diff --git a/srml/support/src/traits.rs b/srml/support/src/traits.rs index 3b3c63e223ce14831368094bf3b882a929c89f74..0b35eca659ba059c943d5170c13c6d2034be73ec 100644 --- a/srml/support/src/traits.rs +++ b/srml/support/src/traits.rs @@ -18,11 +18,11 @@ //! //! NOTE: If you're looking for `parameter_types`, it has moved in to the top-level module. -use crate::rstd::result; +use crate::rstd::{result, marker::PhantomData, ops::Div}; use crate::codec::{Codec, Encode, Decode}; -use crate::runtime_primitives::traits::{ - MaybeSerializeDebug, SimpleArithmetic -}; +use substrate_primitives::u32_trait::Value as U32; +use crate::runtime_primitives::traits::{MaybeSerializeDebug, SimpleArithmetic, Saturating}; +use crate::runtime_primitives::ConsensusEngineId; use super::for_each_tuple; @@ -89,17 +89,49 @@ pub enum UpdateBalanceOutcome { AccountKilled, } -/// Simple trait designed for hooking into a transaction payment. -/// -/// It operates over a single generic `AccountId` type. -pub trait MakePayment { - /// Make transaction payment from `who` for an extrinsic of encoded length - /// `encoded_len` bytes. Return `Ok` iff the payment was successful. - fn make_payment(who: &AccountId, encoded_len: usize) -> Result<(), &'static str>; +/// A trait for finding the author of a block header based on the `PreRuntime` digests contained +/// within it. +pub trait FindAuthor { + /// Find the author of a block based on the pre-runtime digests. + fn find_author<'a, I>(digests: I) -> Option + where I: 'a + IntoIterator; +} + +impl FindAuthor for () { + fn find_author<'a, I>(_: I) -> Option + where I: 'a + IntoIterator + { + None + } } -impl MakePayment for () { - fn make_payment(_: &T, _: usize) -> Result<(), &'static str> { Ok(()) } +/// A trait for verifying the seal of a header and returning the author. +pub trait VerifySeal { + /// Verify a header and return the author, if any. + fn verify_seal(header: &Header) -> Result, &'static str>; +} + +/// Something which can compute and check proofs of +/// a historical key owner and return full identification data of that +/// key owner. +pub trait KeyOwnerProofSystem { + /// The proof of membership itself. + type Proof: Codec; + /// The full identification of a key owner. + type FullIdentification: Codec; + + /// Prove membership of a key owner in the current block-state. + /// + /// This should typically only be called off-chain, since it may be + /// computationally heavy. + /// + /// Returns `Some` iff the key owner referred to by the given `key` is a + /// member of the current set. + fn prove(key: Key) -> Option; + + /// Check a proof of membership on-chain. Return `Some` iff the proof is + /// valid and recent enough to check. + fn check_proof(key: Key, proof: Self::Proof) -> Option; } /// Handler for when some currency "account" decreased in balance for @@ -255,6 +287,34 @@ impl< } } +/// Split an unbalanced amount two ways between a common divisor. +pub struct SplitTwoWays< + Balance, + Imbalance, + Part1, + Target1, + Part2, + Target2, +>(PhantomData<(Balance, Imbalance, Part1, Target1, Part2, Target2)>); + +impl< + Balance: From + Saturating + Div, + I: Imbalance, + Part1: U32, + Target1: OnUnbalanced, + Part2: U32, + Target2: OnUnbalanced, +> OnUnbalanced for SplitTwoWays +{ + fn on_unbalanced(amount: I) { + let total: u32 = Part1::VALUE + Part2::VALUE; + let amount1 = amount.peek().saturating_mul(Part1::VALUE.into()) / total.into(); + let (imb1, imb2) = amount.split(amount1); + Target1::on_unbalanced(imb1); + Target2::on_unbalanced(imb2); + } +} + /// Abstraction over a fungible assets system. pub trait Currency { /// The balance of an account. @@ -284,6 +344,21 @@ pub trait Currency { /// `ExistentialDeposit`. fn minimum_balance() -> Self::Balance; + /// Reduce the total issuance by `amount` and return the according imbalance. The imbalance will + /// typically be used to reduce an account by the same amount with e.g. `settle`. + /// + /// This is infallible, but doesn't guarantee that the entire `amount` is burnt, for example + /// in the case of underflow. + fn burn(amount: Self::Balance) -> Self::PositiveImbalance; + + /// Increase the total issuance by `amount` and return the according imbalance. The imbalance + /// will typically be used to increase an account by the same amount with e.g. + /// `resolve_into_existing` or `resolve_creating`. + /// + /// This is infallible, but doesn't guarantee that the entire `amount` is issued, for example + /// in the case of overflow. + fn issue(amount: Self::Balance) -> Self::NegativeImbalance; + /// The 'free' balance of a given account. /// /// This is the only balance that matters in terms of most operations on tokens. It alone @@ -340,17 +415,18 @@ pub trait Currency { value: Self::Balance ) -> result::Result; - /// Removes some free balance from `who` account for `reason` if possible. If `liveness` is `KeepAlive`, - /// then no less than `ExistentialDeposit` must be left remaining. - /// - /// This checks any locks, vesting, and liquidity requirements. If the removal is not possible, then it - /// returns `Err`. - fn withdraw( + /// Similar to deposit_creating, only accepts a `NegativeImbalance` and returns nothing on + /// success. + fn resolve_into_existing( who: &AccountId, - value: Self::Balance, - reason: WithdrawReason, - liveness: ExistenceRequirement, - ) -> result::Result; + value: Self::NegativeImbalance, + ) -> result::Result<(), Self::NegativeImbalance> { + let v = value.peek(); + match Self::deposit_into_existing(who, v) { + Ok(opposite) => Ok(drop(value.offset(opposite))), + _ => Err(value), + } + } /// Adds up to `value` to the free balance of `who`. If `who` doesn't exist, it is created. /// @@ -360,6 +436,45 @@ pub trait Currency { value: Self::Balance, ) -> Self::PositiveImbalance; + /// Similar to deposit_creating, only accepts a `NegativeImbalance` and returns nothing on + /// success. + fn resolve_creating( + who: &AccountId, + value: Self::NegativeImbalance, + ) { + let v = value.peek(); + drop(value.offset(Self::deposit_creating(who, v))); + } + + /// Removes some free balance from `who` account for `reason` if possible. If `liveness` is + /// `KeepAlive`, then no less than `ExistentialDeposit` must be left remaining. + /// + /// This checks any locks, vesting, and liquidity requirements. If the removal is not possible, + /// then it returns `Err`. + /// + /// If the operation is successful, this will return `Ok` with a `NegativeImbalance` whose value + /// is `value`. + fn withdraw( + who: &AccountId, + value: Self::Balance, + reason: WithdrawReason, + liveness: ExistenceRequirement, + ) -> result::Result; + + /// Similar to withdraw, only accepts a `PositiveImbalance` and returns nothing on success. + fn settle( + who: &AccountId, + value: Self::PositiveImbalance, + reason: WithdrawReason, + liveness: ExistenceRequirement, + ) -> result::Result<(), Self::PositiveImbalance> { + let v = value.peek(); + match Self::withdraw(who, v, reason, liveness) { + Ok(opposite) => Ok(drop(value.offset(opposite))), + _ => Err(value), + } + } + /// Ensure an account's free balance equals some value; this will create the account /// if needed. /// @@ -500,3 +615,22 @@ bitmask! { } } +impl WithdrawReasons { + /// Choose all variants except for `one`. + pub fn except(one: WithdrawReason) -> WithdrawReasons { + let mut mask = Self::all(); + mask.toggle(one); + mask + } +} + +/// Trait for type that can handle incremental changes to a set of account IDs. +pub trait ChangeMembers { + /// A number of members `_incoming` just joined the set and replaced some `_outgoing` ones. The + /// new set is thus given by `_new`. + fn change_members(_incoming: &[AccountId], _outgoing: &[AccountId], _new: &[AccountId]); +} + +impl ChangeMembers for () { + fn change_members(_incoming: &[T], _outgoing: &[T], _new_set: &[T]) {} +} diff --git a/srml/support/test/Cargo.toml b/srml/support/test/Cargo.toml index 44a3b8d8841a20cdffbf6e7f62eef052574f2a47..fa4529d71e623bac39a548a9b8a1a9a890ea9b20 100644 --- a/srml/support/test/Cargo.toml +++ b/srml/support/test/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" [dependencies] serde = { version = "1.0", default-features = false, features = ["derive"] } -parity-codec = { version = "3.3", default-features = false, features = ["derive"] } +parity-codec = { version = "4.1.1", default-features = false, features = ["derive"] } runtime_io = { package = "sr-io", path = "../../../core/sr-io", default-features = false } srml-support = { version = "2", path = "../", default-features = false } inherents = { package = "substrate-inherents", path = "../../../core/inherents", default-features = false } diff --git a/srml/support/test/tests/final_keys.rs b/srml/support/test/tests/final_keys.rs index 549506df496c9505719ad0e07ff5a0c7fe84c480..54c08d5e5feac0e7c4b76ee7a80b880f7852200a 100644 --- a/srml/support/test/tests/final_keys.rs +++ b/srml/support/test/tests/final_keys.rs @@ -17,7 +17,6 @@ use runtime_io::{with_externalities, Blake2Hasher}; use srml_support::{StorageValue, StorageMap, StorageDoubleMap}; use srml_support::storage::unhashed; -use srml_support::runtime_primitives::BuildStorage; use parity_codec::{Encode, Decode}; pub trait Trait { @@ -30,7 +29,7 @@ srml_support::decl_module! { } srml_support::decl_storage!{ - trait Store for Module as Module { + trait Store for Module as FinalKeys { pub Value config(value): u32; pub Map: map u32 => u32; @@ -60,38 +59,38 @@ fn new_test_ext() -> runtime_io::TestExternalities { #[test] fn final_keys() { with_externalities(&mut new_test_ext(), || { - >::put(1); - assert_eq!(unhashed::get::(&runtime_io::twox_128(b"Module Value")), Some(1u32)); + Value::put(1); + assert_eq!(unhashed::get::(&runtime_io::twox_128(b"FinalKeys Value")), Some(1u32)); - >::insert(1, 2); - let mut k = b"Module Map".to_vec(); + Map::insert(1, 2); + let mut k = b"FinalKeys Map".to_vec(); k.extend(1u32.encode()); assert_eq!(unhashed::get::(&runtime_io::blake2_256(&k)), Some(2u32)); - >::insert(1, 2); - let mut k = b"Module Map2".to_vec(); + Map2::insert(1, 2); + let mut k = b"FinalKeys Map2".to_vec(); k.extend(1u32.encode()); assert_eq!(unhashed::get::(&runtime_io::twox_128(&k)), Some(2u32)); - >::insert(1, 2); - let mut k = b"Module LinkedMap".to_vec(); + LinkedMap::insert(1, 2); + let mut k = b"FinalKeys LinkedMap".to_vec(); k.extend(1u32.encode()); assert_eq!(unhashed::get::(&runtime_io::blake2_256(&k)), Some(2u32)); - >::insert(1, 2); - let mut k = b"Module LinkedMap2".to_vec(); + LinkedMap2::insert(1, 2); + let mut k = b"FinalKeys LinkedMap2".to_vec(); k.extend(1u32.encode()); assert_eq!(unhashed::get::(&runtime_io::twox_128(&k)), Some(2u32)); - >::insert(1, 2, 3); - let mut k = b"Module DoubleMap".to_vec(); + DoubleMap::insert(&1, &2, &3); + let mut k = b"FinalKeys DoubleMap".to_vec(); k.extend(1u32.encode()); let mut k = runtime_io::blake2_256(&k).to_vec(); k.extend(&runtime_io::blake2_256(&2u32.encode())); assert_eq!(unhashed::get::(&k), Some(3u32)); - >::insert(1, 2, 3); - let mut k = b"Module DoubleMap2".to_vec(); + DoubleMap2::insert(&1, &2, &3); + let mut k = b"FinalKeys DoubleMap2".to_vec(); k.extend(1u32.encode()); let mut k = runtime_io::twox_128(&k).to_vec(); k.extend(&runtime_io::blake2_128(&2u32.encode())); diff --git a/srml/support/test/tests/genesisconfig.rs b/srml/support/test/tests/genesisconfig.rs new file mode 100644 index 0000000000000000000000000000000000000000..717c7105b587bf36b39c541af93699402a1335e4 --- /dev/null +++ b/srml/support/test/tests/genesisconfig.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 . + +pub trait Trait { + type BlockNumber: parity_codec::Codec + Default; + type Origin; +} + +srml_support::decl_module! { + pub struct Module for enum Call where origin: T::Origin {} +} + +srml_support::decl_storage! { + trait Store for Module as Example { + pub AppendableDM config(t): double_map u32, blake2_256(T::BlockNumber) => Vec; + } +} + +struct Test; + +impl Trait for Test { + type BlockNumber = u32; + type Origin = (); +} + +#[test] +fn init_genesis_config() { + GenesisConfig:: { + t: Default::default(), + }; +} \ No newline at end of file diff --git a/srml/support/test/tests/instance.rs b/srml/support/test/tests/instance.rs index f7b4a4bd3a2517506ca905dd7f723eef0f1a4822..4702105bba72da0681a5ea83b0a00b127ced754d 100644 --- a/srml/support/test/tests/instance.rs +++ b/srml/support/test/tests/instance.rs @@ -13,84 +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, Blake2Hasher}; -use srml_support::rstd::prelude::*; -use srml_support::rstd as rstd; -use srml_support::runtime_primitives::{generic, BuildStorage}; -use srml_support::runtime_primitives::traits::{BlakeTwo256, Block as _, Verify}; -use srml_support::Parameter; +use srml_support::{ + Parameter, traits::Get, parameter_types, + runtime_primitives::{generic, BuildStorage, traits::{BlakeTwo256, Block as _, Verify}}, +}; use inherents::{ ProvideInherent, InherentData, InherentIdentifier, RuntimeString, MakeFatalError }; use srml_support::{StorageValue, StorageMap, StorageDoubleMap}; use primitives::{H256, sr25519}; -pub trait Currency { -} - -// Mock -mod system { - use super::*; - - pub trait Trait: 'static + Eq + Clone { - type Origin: Into, Self::Origin>> - + From>; - type BlockNumber; - type Hash; - type AccountId; - type Event: From; - } +mod system; - pub type DigestItemOf = generic::DigestItem<::Hash>; - - srml_support::decl_module! { - pub struct Module for enum Call where origin: T::Origin { - pub fn deposit_event(_event: T::Event) { - } - } - } - impl Module { - pub fn deposit_log(_item: DigestItemOf) { - unimplemented!(); - } - } - - srml_support::decl_event!( - pub enum Event { - ExtrinsicSuccess, - ExtrinsicFailed, - } - ); - - /// Origin for the system module. - #[derive(PartialEq, Eq, Clone)] - #[cfg_attr(feature = "std", derive(Debug))] - pub enum RawOrigin { - Root, - Signed(AccountId), - None, - } - - impl From> for RawOrigin { - fn from(s: Option) -> RawOrigin { - match s { - Some(who) => RawOrigin::Signed(who), - None => RawOrigin::None, - } - } - } - - pub type Origin = RawOrigin<::AccountId>; - - pub fn ensure_root(o: OuterOrigin) -> Result<(), &'static str> - where OuterOrigin: Into, OuterOrigin>> - { - o.into().map(|_| ()).map_err(|_| "bad origin: expected to be a root origin") - } -} +pub trait Currency {} // Test for: // * No default instance @@ -99,31 +37,48 @@ mod system { mod module1 { use super::*; - pub trait Trait: system::Trait { + pub trait Trait: system::Trait where ::BlockNumber: From { type Event: From> + Into<::Event>; type Origin: From>; + type SomeParameter: Get; + type GenericType: Default + Clone + parity_codec::Codec; } srml_support::decl_module! { - pub struct Module, I: InstantiableThing> for enum Call where origin: ::Origin { + pub struct Module, I: InstantiableThing> for enum Call where + origin: ::Origin, + T::BlockNumber: From + { + fn offchain_worker() {} + fn deposit_event() = default; - fn one() { + fn one(origin) { + system::ensure_root(origin)?; Self::deposit_event(RawEvent::AnotherVariant(3)); } } } srml_support::decl_storage! { - trait Store for Module, I: InstantiableThing> as Module1 { - pub Value config(value): u64; + trait Store for Module, I: InstantiableThing> as Module1 where + T::BlockNumber: From + std::fmt::Display + { + pub Value config(value): T::GenericType; pub Map: map u32 => u64; pub LinkedMap: linked_map u32 => u64; } + + add_extra_genesis { + config(test) : T::BlockNumber; + build(|_, _, config: &Self| { + println!("{}", config.test); + }); + } } srml_support::decl_event! { - pub enum Event where Phantom = rstd::marker::PhantomData { + pub enum Event where Phantom = std::marker::PhantomData { _Phantom(Phantom), AnotherVariant(u32), } @@ -131,14 +86,16 @@ mod module1 { #[derive(PartialEq, Eq, Clone)] #[cfg_attr(feature = "std", derive(Debug))] - pub enum Origin, I> { + pub enum Origin, I> where T::BlockNumber: From { Members(u32), - _Phantom(rstd::marker::PhantomData<(T, I)>), + _Phantom(std::marker::PhantomData<(T, I)>), } pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"12345678"; - impl, I: InstantiableThing> ProvideInherent for Module { + impl, I: InstantiableThing> ProvideInherent for Module where + T::BlockNumber: From + { type Call = Call; type Error = MakeFatalError; const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER; @@ -147,7 +104,7 @@ mod module1 { unimplemented!(); } - fn check_inherent(_call: &Self::Call, _data: &InherentData) -> rstd::result::Result<(), Self::Error> { + fn check_inherent(_: &Self::Call, _: &InherentData) -> std::result::Result<(), Self::Error> { unimplemented!(); } } @@ -168,7 +125,9 @@ mod module2 { impl, I: Instance> Currency for Module {} srml_support::decl_module! { - pub struct Module, I: Instance=DefaultInstance> for enum Call where origin: ::Origin { + pub struct Module, I: Instance=DefaultInstance> for enum Call where + origin: ::Origin + { fn deposit_event() = default; } } @@ -180,7 +139,6 @@ mod module2 { pub LinkedMap config(linked_map): linked_map u64 => u64; pub DoubleMap config(double_map): double_map u64, blake2_256(u64) => u64; } - extra_genesis_skip_phantom_data_field; } srml_support::decl_event! { @@ -193,7 +151,7 @@ mod module2 { #[cfg_attr(feature = "std", derive(Debug))] pub enum Origin, I=DefaultInstance> { Members(u32), - _Phantom(rstd::marker::PhantomData<(T, I)>), + _Phantom(std::marker::PhantomData<(T, I)>), } pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"12345678"; @@ -207,7 +165,7 @@ mod module2 { unimplemented!(); } - fn check_inherent(_call: &Self::Call, _data: &InherentData) -> rstd::result::Result<(), Self::Error> { + fn check_inherent(_call: &Self::Call, _data: &InherentData) -> std::result::Result<(), Self::Error> { unimplemented!(); } } @@ -224,18 +182,25 @@ mod module3 { } srml_support::decl_module! { - pub struct Module for enum Call where origin: ::Origin { - } + pub struct Module for enum Call where origin: ::Origin {} } } +parameter_types! { + pub const SomeValue: u32 = 100; +} + impl module1::Trait for Runtime { type Event = Event; type Origin = Origin; + type SomeParameter = SomeValue; + type GenericType = u32; } impl module1::Trait for Runtime { type Event = Event; type Origin = Origin; + type SomeParameter = SomeValue; + type GenericType = u32; } impl module2::Trait for Runtime { type Amount = u16; @@ -282,29 +247,39 @@ srml_support::construct_runtime!( UncheckedExtrinsic = UncheckedExtrinsic { System: system::{Module, Call, Event}, - Module1_1: module1::::{Module, Call, Storage, Event, Config, Origin, Inherent}, - Module1_2: module1::::{Module, Call, Storage, Event, Config, Origin, Inherent}, + Module1_1: module1::::{ + Module, Call, Storage, Event, Config, Origin, Inherent + }, + Module1_2: module1::::{ + Module, Call, Storage, Event, Config, Origin, Inherent + }, Module2: module2::{Module, Call, Storage, Event, Config, Origin, Inherent}, - Module2_1: module2::::{Module, Call, Storage, Event, Config, Origin, Inherent}, - Module2_2: module2::::{Module, Call, Storage, Event, Config, Origin, Inherent}, - Module2_3: module2::::{Module, Call, Storage, Event, Config, Origin, Inherent}, + Module2_1: module2::::{ + Module, Call, Storage, Event, Config, Origin, Inherent + }, + Module2_2: module2::::{ + Module, Call, Storage, Event, Config, Origin, Inherent + }, + Module2_3: module2::::{ + Module, Call, Storage, Event, Config, Origin, Inherent + }, Module3: module3::{Module, Call}, } ); pub type Header = generic::Header; pub type Block = generic::Block; -pub type UncheckedExtrinsic = generic::UncheckedMortalCompactExtrinsic; +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; fn new_test_ext() -> runtime_io::TestExternalities { GenesisConfig{ module1_Instance1: Some(module1::GenesisConfig { value: 3, - .. Default::default() + test: 2, }), module1_Instance2: Some(module1::GenesisConfig { value: 4, - _genesis_phantom_data: Default::default(), + test: 5, }), module2: Some(module2::GenesisConfig { value: 4, @@ -326,48 +301,48 @@ fn new_test_ext() -> runtime_io::TestExternalities { #[test] fn storage_instance_independance() { with_externalities(&mut new_test_ext(), || { - let mut map = rstd::collections::btree_map::BTreeMap::new(); + let mut map = std::collections::btree_map::BTreeMap::new(); for key in [ module2::Value::::key().to_vec(), module2::Value::::key().to_vec(), module2::Value::::key().to_vec(), module2::Value::::key().to_vec(), - module2::Map::::prefix().to_vec(), - module2::Map::::prefix().to_vec(), - module2::Map::::prefix().to_vec(), - module2::Map::::prefix().to_vec(), - module2::LinkedMap::::prefix().to_vec(), - module2::LinkedMap::::prefix().to_vec(), - module2::LinkedMap::::prefix().to_vec(), - module2::LinkedMap::::prefix().to_vec(), - module2::DoubleMap::::prefix().to_vec(), - module2::DoubleMap::::prefix().to_vec(), - module2::DoubleMap::::prefix().to_vec(), - module2::DoubleMap::::prefix().to_vec(), - module2::Map::::key_for(0), - module2::Map::::key_for(0).to_vec(), - module2::Map::::key_for(0).to_vec(), - module2::Map::::key_for(0).to_vec(), - module2::LinkedMap::::key_for(0), - module2::LinkedMap::::key_for(0).to_vec(), - module2::LinkedMap::::key_for(0).to_vec(), - module2::LinkedMap::::key_for(0).to_vec(), - module2::Map::::key_for(1), - module2::Map::::key_for(1).to_vec(), - module2::Map::::key_for(1).to_vec(), - module2::Map::::key_for(1).to_vec(), - module2::LinkedMap::::key_for(1), - module2::LinkedMap::::key_for(1).to_vec(), - module2::LinkedMap::::key_for(1).to_vec(), - module2::LinkedMap::::key_for(1).to_vec(), - module2::DoubleMap::::prefix_for(1), - module2::DoubleMap::::prefix_for(1).to_vec(), - module2::DoubleMap::::prefix_for(1).to_vec(), - module2::DoubleMap::::prefix_for(1).to_vec(), - module2::DoubleMap::::key_for(1, 1), - module2::DoubleMap::::key_for(1, 1).to_vec(), - module2::DoubleMap::::key_for(1, 1).to_vec(), - module2::DoubleMap::::key_for(1, 1).to_vec(), + module2::Map::::prefix().to_vec(), + module2::Map::::prefix().to_vec(), + module2::Map::::prefix().to_vec(), + module2::Map::::prefix().to_vec(), + module2::LinkedMap::::prefix().to_vec(), + module2::LinkedMap::::prefix().to_vec(), + module2::LinkedMap::::prefix().to_vec(), + module2::LinkedMap::::prefix().to_vec(), + module2::DoubleMap::::prefix().to_vec(), + module2::DoubleMap::::prefix().to_vec(), + module2::DoubleMap::::prefix().to_vec(), + module2::DoubleMap::::prefix().to_vec(), + module2::Map::::key_for(0), + module2::Map::::key_for(0).to_vec(), + module2::Map::::key_for(0).to_vec(), + module2::Map::::key_for(0).to_vec(), + module2::LinkedMap::::key_for(0), + module2::LinkedMap::::key_for(0).to_vec(), + module2::LinkedMap::::key_for(0).to_vec(), + module2::LinkedMap::::key_for(0).to_vec(), + module2::Map::::key_for(1), + module2::Map::::key_for(1).to_vec(), + module2::Map::::key_for(1).to_vec(), + module2::Map::::key_for(1).to_vec(), + module2::LinkedMap::::key_for(1), + module2::LinkedMap::::key_for(1).to_vec(), + module2::LinkedMap::::key_for(1).to_vec(), + module2::LinkedMap::::key_for(1).to_vec(), + module2::DoubleMap::::prefix_for(&1), + module2::DoubleMap::::prefix_for(&1).to_vec(), + module2::DoubleMap::::prefix_for(&1).to_vec(), + module2::DoubleMap::::prefix_for(&1).to_vec(), + module2::DoubleMap::::key_for(&1, &1), + module2::DoubleMap::::key_for(&1, &1).to_vec(), + module2::DoubleMap::::key_for(&1, &1).to_vec(), + module2::DoubleMap::::key_for(&1, &1).to_vec(), ].iter() { assert!(map.insert(key, ()).is_none()) } @@ -378,9 +353,9 @@ fn storage_instance_independance() { fn storage_with_instance_basic_operation() { with_externalities(&mut new_test_ext(), || { type Value = module2::Value; - type Map = module2::Map; - type LinkedMap = module2::LinkedMap; - type DoubleMap = module2::DoubleMap; + type Map = module2::Map; + type LinkedMap = module2::LinkedMap; + type DoubleMap = module2::DoubleMap; assert_eq!(Value::exists(), true); assert_eq!(Value::get(), 4); @@ -421,15 +396,15 @@ fn storage_with_instance_basic_operation() { let key1 = 1; let key2 = 1; - assert_eq!(DoubleMap::exists(0, 0), true); - assert_eq!(DoubleMap::exists(key1, key2), false); - DoubleMap::insert(key1, key2, 1); - assert_eq!(DoubleMap::get(key1, key2), 1); - assert_eq!(DoubleMap::take(key1, key2), 1); - assert_eq!(DoubleMap::get(key1, key2), 0); - DoubleMap::mutate(key1, key2, |a| *a=2); - assert_eq!(DoubleMap::get(key1, key2), 2); - DoubleMap::remove(key1, key2); - assert_eq!(DoubleMap::get(key1, key2), 0); + assert_eq!(DoubleMap::exists(&0, &0), true); + assert_eq!(DoubleMap::exists(&key1, &key2), false); + DoubleMap::insert(&key1, &key2, &1); + assert_eq!(DoubleMap::get(&key1, &key2), 1); + assert_eq!(DoubleMap::take(&key1, &key2), 1); + assert_eq!(DoubleMap::get(&key1, &key2), 0); + DoubleMap::mutate(&key1, &key2, |a| *a=2); + assert_eq!(DoubleMap::get(&key1, &key2), 2); + DoubleMap::remove(&key1, &key2); + assert_eq!(DoubleMap::get(&key1, &key2), 0); }); } diff --git a/srml/support/test/tests/issue2219.rs b/srml/support/test/tests/issue2219.rs new file mode 100644 index 0000000000000000000000000000000000000000..54ad62cc937ab28616387dd1c5e455b5638b5a1f --- /dev/null +++ b/srml/support/test/tests/issue2219.rs @@ -0,0 +1,186 @@ +// 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 srml_support::runtime_primitives::generic; +use srml_support::runtime_primitives::traits::{BlakeTwo256, Block as _, Verify}; +use srml_support::codec::{Encode, Decode}; +use primitives::{H256, sr25519}; +use serde::{Serialize, Deserialize}; + +mod system; + +mod module { + use super::*; + + pub type Request = ( + ::AccountId, + Role, + ::BlockNumber, + ); + pub type Requests = Vec>; + + #[derive(Encode, Decode, Copy, Clone, Eq, PartialEq, Debug)] + pub enum Role { + Storage, + } + + #[derive(Encode, Decode, Copy, Clone, Eq, PartialEq, Debug)] + pub struct RoleParameters { + // minimum actors to maintain - if role is unstaking + // and remaining actors would be less that this value - prevent or punish for unstaking + pub min_actors: u32, + + // the maximum number of spots available to fill for a role + pub max_actors: u32, + + // payouts are made at this block interval + pub reward_period: T::BlockNumber, + + // minimum amount of time before being able to unstake + pub bonding_period: T::BlockNumber, + + // how long tokens remain locked for after unstaking + pub unbonding_period: T::BlockNumber, + + // minimum period required to be in service. unbonding before this time is highly penalized + pub min_service_period: T::BlockNumber, + + // "startup" time allowed for roles that need to sync their infrastructure + // with other providers before they are considered in service and punishable for + // not delivering required level of service. + pub startup_grace_period: T::BlockNumber, + } + + impl Default for RoleParameters { + fn default() -> Self { + Self { + max_actors: 10, + reward_period: T::BlockNumber::default(), + unbonding_period: T::BlockNumber::default(), + + // not currently used + min_actors: 5, + bonding_period: T::BlockNumber::default(), + min_service_period: T::BlockNumber::default(), + startup_grace_period: T::BlockNumber::default(), + } + } + } + + pub trait Trait: system::Trait {} + + srml_support::decl_module! { + pub struct Module for enum Call where origin: T::Origin {} + } + + #[derive(Encode, Decode, Copy, Clone, Serialize, Deserialize)] + pub struct Data { + pub data: T::BlockNumber, + } + + impl Default for Data { + fn default() -> Self { + Self { + data: T::BlockNumber::default(), + } + } + } + + srml_support::decl_storage! { + trait Store for Module as Actors { + /// requirements to enter and maintain status in roles + pub Parameters get(parameters) build(|config: &GenesisConfig| { + if config.enable_storage_role { + let storage_params: RoleParameters = Default::default(); + vec![(Role::Storage, storage_params)] + } else { + vec![] + } + }): map Role => Option>; + + /// the roles members can enter into + pub AvailableRoles get(available_roles) build(|config: &GenesisConfig| { + if config.enable_storage_role { + vec![(Role::Storage)] + } else { + vec![] + } + }): Vec; + + /// Actors list + pub ActorAccountIds get(actor_account_ids) : Vec; + + /// actor accounts associated with a role + pub AccountIdsByRole get(account_ids_by_role) : map Role => Vec; + + /// tokens locked until given block number + pub Bondage get(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. + /// The member must then send a stake() transaction to approve the request and enter the desired role. + /// 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; + + /// Entry request expires after this number of blocks + pub RequestLifeTime get(request_life_time) config(request_life_time) : u64 = 0; + } + add_extra_genesis { + config(enable_storage_role): bool; + } + } +} + +pub type Signature = sr25519::Signature; +pub type AccountId = ::Signer; +pub type BlockNumber = u64; +pub type Index = u64; +pub type Header = generic::Header; +pub type Block = generic::Block; +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; + +impl system::Trait for Runtime { + type Hash = H256; + type Origin = Origin; + type BlockNumber = BlockNumber; + type AccountId = AccountId; + type Event = Event; +} + +impl module::Trait for Runtime {} + +srml_support::construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + System: system::{Module, Call, Event}, + Module: module::{Module, Call, Storage, Config}, + } +); + +#[test] +fn create_genesis_config() { + GenesisConfig { + module: Some(module::GenesisConfig { + request_life_time: 0, + enable_storage_role: true, + }) + }; +} diff --git a/srml/support/test/tests/reserved_keyword/on_initialize.rs b/srml/support/test/tests/reserved_keyword/on_initialize.rs index c63153241ce8f3ce052d9e355e36aa3d5f860818..f9c2f5f7f0e5add4bdf3358e3330fec4123321eb 100644 --- a/srml/support/test/tests/reserved_keyword/on_initialize.rs +++ b/srml/support/test/tests/reserved_keyword/on_initialize.rs @@ -19,7 +19,7 @@ macro_rules! reserved { srml_support::decl_module! { pub struct Module for enum Call where origin: T::Origin { - fn $reserved() -> Result { unreachable!() } + fn $reserved(_origin) -> Result { unreachable!() } } } } diff --git a/srml/support/test/tests/system.rs b/srml/support/test/tests/system.rs new file mode 100644 index 0000000000000000000000000000000000000000..6483161211afcc3ab232c8d1fcfa2e71dce6e215 --- /dev/null +++ b/srml/support/test/tests/system.rs @@ -0,0 +1,52 @@ +use srml_support::codec::{Encode, Decode}; + +pub trait Trait: 'static + Eq + Clone { + type Origin: Into, Self::Origin>> + + From>; + + type BlockNumber: Decode + Encode + Clone + Default; + type Hash; + type AccountId: Encode + Decode; + type Event: From; +} + +srml_support::decl_module! { + pub struct Module for enum Call where origin: T::Origin { + pub fn deposit_event(_event: T::Event) { + } + } +} + +srml_support::decl_event!( + pub enum Event { + ExtrinsicSuccess, + ExtrinsicFailed, + } +); + +/// Origin for the system module. +#[derive(PartialEq, Eq, Clone)] +#[cfg_attr(feature = "std", derive(Debug))] +pub enum RawOrigin { + Root, + Signed(AccountId), + None, +} + +impl From> for RawOrigin { + fn from(s: Option) -> RawOrigin { + match s { + Some(who) => RawOrigin::Signed(who), + None => RawOrigin::None, + } + } +} + +pub type Origin = RawOrigin<::AccountId>; + +#[allow(dead_code)] +pub fn ensure_root(o: OuterOrigin) -> Result<(), &'static str> + where OuterOrigin: Into, OuterOrigin>> +{ + o.into().map(|_| ()).map_err(|_| "bad origin: expected to be a root origin") +} \ No newline at end of file diff --git a/srml/system/Cargo.toml b/srml/system/Cargo.toml index 5295d0708c429423ae926d370d210613a182c630..d6039499e15fbd470a2c14e310c84502f3d8fd34 100644 --- a/srml/system/Cargo.toml +++ b/srml/system/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" [dependencies] serde = { version = "1.0", optional = true, features = ["derive"] } safe-mix = { version = "1.0", default-features = false} -parity-codec = { version = "3.5", default-features = false, features = ["derive"] } +parity-codec = { version = "4.1.1", default-features = false, features = ["derive"] } substrate-primitives = { path = "../../core/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 } diff --git a/srml/system/benches/bench.rs b/srml/system/benches/bench.rs index ee4ebf711ab31c872efc022359008e0ca87a55f7..5ff63baa1cdb41b81a6fc54b101e18b1f650a40b 100644 --- a/srml/system/benches/bench.rs +++ b/srml/system/benches/bench.rs @@ -20,7 +20,7 @@ use srml_support::{decl_module, decl_event, impl_outer_origin, impl_outer_event} use runtime_io::{with_externalities, Blake2Hasher}; use substrate_primitives::H256; use primitives::{ - BuildStorage, traits::{BlakeTwo256, IdentityLookup}, + traits::{BlakeTwo256, IdentityLookup}, testing::Header, }; @@ -54,6 +54,11 @@ impl_outer_event! { } } +srml_support::parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 4 * 1024 * 1024; + pub const MaximumBlockLength: u32 = 4 * 1024 * 1024; +} #[derive(Clone, Eq, PartialEq)] pub struct Runtime; impl system::Trait for Runtime { @@ -65,7 +70,11 @@ 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; + type MaximumBlockLength = MaximumBlockLength; } impl module::Trait for Runtime { @@ -73,7 +82,7 @@ impl module::Trait for Runtime { } fn new_test_ext() -> runtime_io::TestExternalities { - system::GenesisConfig::::default().build_storage().unwrap().0.into() + system::GenesisConfig::default().build_storage::().unwrap().0.into() } fn deposit_events(n: usize) { diff --git a/srml/system/src/lib.rs b/srml/system/src/lib.rs index 4abf68dcbeecf58ca42b1b614d42953dbbe2b283..b880c742a2766718448672025d9ee8e5913c14d8 100644 --- a/srml/system/src/lib.rs +++ b/srml/system/src/lib.rs @@ -76,20 +76,22 @@ use serde::Serialize; use rstd::prelude::*; #[cfg(any(feature = "std", test))] use rstd::map; -use primitives::{generic, traits::{self, CheckEqual, SimpleArithmetic, +use rstd::marker::PhantomData; +use primitives::generic::{self, Era}; +use primitives::weights::{Weight, DispatchInfo, DispatchClass, WeightMultiplier}; +use primitives::transaction_validity::{ValidTransaction, TransactionPriority, TransactionLongevity}; +use primitives::traits::{self, CheckEqual, SimpleArithmetic, Zero, SignedExtension, Convert, SimpleBitOps, Hash, Member, MaybeDisplay, EnsureOrigin, CurrentHeight, BlockNumberToHash, - MaybeSerializeDebugButNotDeserialize, MaybeSerializeDebug, StaticLookup, One, Bounded, Lookup, -}}; -#[cfg(any(feature = "std", test))] -use primitives::traits::Zero; + MaybeSerializeDebugButNotDeserialize, MaybeSerializeDebug, StaticLookup, One, Bounded, + Lookup, DispatchError, SaturatedConversion, +}; use substrate_primitives::{storage::well_known_keys, ChangesTrieConfiguration}; use srml_support::{ - storage, decl_module, decl_event, decl_storage, StorageDoubleMap, StorageValue, - StorageMap, Parameter, for_each_tuple, traits::Contains + storage, decl_module, decl_event, decl_storage, StorageDoubleMap, StorageValue, StorageMap, + Parameter, for_each_tuple, traits::{Contains, Get} }; use safe_mix::TripletMix; use parity_codec::{Encode, Decode}; -use crate::{self as system}; #[cfg(any(feature = "std", test))] use runtime_io::{twox_128, TestExternalities, Blake2Hasher}; @@ -173,6 +175,14 @@ pub trait Trait: 'static + Eq + Clone { /// (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, @@ -181,6 +191,15 @@ pub trait Trait: 'static + Eq + Clone { /// The aggregated event type of the runtime. type Event: Parameter + Member + From; + + /// Maximum number of block number to block hash mappings to keep (oldest pruned first). + type BlockHashCount: Get; + + /// The maximum weight of a block. + type MaximumBlockWeight: Get; + + /// The maximum length of a block (in bytes). + type MaximumBlockLength: Get; } pub type DigestOf = generic::Digest<::Hash>; @@ -202,17 +221,20 @@ decl_module! { } /// Set the number of pages in the WebAssembly environment's heap. - fn set_heap_pages(pages: u64) { + fn set_heap_pages(origin, pages: u64) { + ensure_root(origin)?; storage::unhashed::put_raw(well_known_keys::HEAP_PAGES, &pages.encode()); } /// Set the new code. - pub fn set_code(new: Vec) { + pub fn set_code(origin, new: Vec) { + ensure_root(origin)?; storage::unhashed::put_raw(well_known_keys::CODE, &new); } /// Set the new changes trie configuration. - pub fn set_changes_trie_config(changes_trie_config: Option) { + pub fn set_changes_trie_config(origin, changes_trie_config: Option) { + ensure_root(origin)?; match changes_trie_config.clone() { Some(changes_trie_config) => storage::unhashed::put_raw( well_known_keys::CHANGES_TRIE_CONFIG, @@ -228,14 +250,16 @@ decl_module! { } /// Set some items of storage. - fn set_storage(items: Vec) { + fn set_storage(origin, items: Vec) { + ensure_root(origin)?; for i in &items { storage::unhashed::put_raw(&i.0, &i.1); } } /// Kill some items from storage. - fn kill_storage(keys: Vec) { + fn kill_storage(origin, keys: Vec) { + ensure_root(origin)?; for key in &keys { storage::unhashed::kill(&key); } @@ -323,7 +347,12 @@ decl_storage! { /// Total extrinsics count for the current block. ExtrinsicCount: Option; /// Total weight for all extrinsics put together, for the current block. - AllExtrinsicsWeight: Option; + 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; /// Extrinsics data for the current block (maps an extrinsic's index to its data). @@ -370,7 +399,7 @@ decl_storage! { #[serde(with = "substrate_primitives::bytes")] config(code): Vec; - build(|storage: &mut primitives::StorageOverlay, _: &mut primitives::ChildrenStorageOverlay, config: &GenesisConfig| { + build(|storage: &mut primitives::StorageOverlay, _: &mut primitives::ChildrenStorageOverlay, config: &GenesisConfig| { use parity_codec::Encode; storage.insert(well_known_keys::CODE.to_vec(), config.code.clone()); @@ -498,14 +527,14 @@ impl Module { // Index of the to be added event. let event_idx = { - let old_event_count = >::get(); + let old_event_count = EventCount::get(); let new_event_count = match old_event_count.checked_add(1) { // We've reached the maximum number of events at this block, just // don't do anything and leave the event_count unaltered. None => return, Some(nc) => nc, }; - >::put(new_event_count); + EventCount::put(new_event_count); old_event_count }; @@ -537,12 +566,27 @@ impl Module { /// Gets extrinsics count. pub fn extrinsic_count() -> u32 { - >::get().unwrap_or_default() + ExtrinsicCount::get().unwrap_or_default() } /// Gets a total weight of all executed extrinsics. - pub fn all_extrinsics_weight() -> u32 { - >::get().unwrap_or_default() + pub fn all_extrinsics_weight() -> Weight { + AllExtrinsicsWeight::get().unwrap_or_default() + } + + pub fn all_extrinsics_len() -> u32 { + 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. @@ -566,19 +610,33 @@ impl Module { *index = (*index + 1) % 81; }); >::kill(); - >::kill(); + EventCount::kill(); >::remove_prefix(&()); } /// Remove temporary "environment" entries in storage. pub fn finalize() -> T::Header { - >::kill(); - >::kill(); + ExtrinsicCount::kill(); + Self::update_weight_multiplier(); + AllExtrinsicsWeight::kill(); + AllExtrinsicsLen::kill(); let number = >::take(); let parent_hash = >::take(); let mut digest = >::take(); let extrinsics_root = >::take(); + + // move block hash pruning window by one block + let block_hash_count = ::get(); + if number > block_hash_count { + let to_remove = number - block_hash_count - One::one(); + + // keep genesis hash + if to_remove != Zero::zero() { + >::remove(to_remove); + } + } + let storage_root = T::Hashing::storage_root(); let storage_changes_root = T::Hashing::storage_changes_root(parent_hash); @@ -712,38 +770,237 @@ impl Module { /// NOTE: This function is called only when the block is being constructed locally. /// `execute_block` doesn't note any extrinsics. pub fn note_extrinsic(encoded_xt: Vec) { - >::insert(Self::extrinsic_index().unwrap_or_default(), encoded_xt); + ExtrinsicData::insert(Self::extrinsic_index().unwrap_or_default(), encoded_xt); } /// To be called immediately after an extrinsic has been applied. - pub fn note_applied_extrinsic(r: &Result<(), &'static str>, encoded_len: u32) { + pub fn note_applied_extrinsic(r: &Result<(), &'static str>, _encoded_len: u32) { Self::deposit_event(match r { Ok(_) => Event::ExtrinsicSuccess, Err(_) => Event::ExtrinsicFailed, }.into()); let next_extrinsic_index = Self::extrinsic_index().unwrap_or_default() + 1u32; - let total_length = encoded_len.saturating_add(Self::all_extrinsics_weight()); storage::unhashed::put(well_known_keys::EXTRINSIC_INDEX, &next_extrinsic_index); - >::put(&total_length); } /// To be called immediately after `note_applied_extrinsic` of the last extrinsic of the block /// has been called. pub fn note_finished_extrinsics() { let extrinsic_index: u32 = storage::unhashed::take(well_known_keys::EXTRINSIC_INDEX).unwrap_or_default(); - >::put(extrinsic_index); + ExtrinsicCount::put(extrinsic_index); } /// Remove all extrinsic data and save the extrinsics trie root. pub fn derive_extrinsics() { - let extrinsics = (0..>::get().unwrap_or_default()).map(>::take).collect(); + let extrinsics = (0..ExtrinsicCount::get().unwrap_or_default()).map(ExtrinsicData::take).collect(); let xts_root = extrinsics_data_root::(extrinsics); >::put(xts_root); } } +/// resource limit check. +#[derive(Encode, Decode, Clone, Eq, PartialEq)] +pub struct CheckWeight(PhantomData); + +impl CheckWeight { + + /// Get the quota divisor of each dispatch class type. This indicates that all operational + /// dispatches can use the full capacity of any resource, while user-triggered ones can consume + /// a quarter. + fn get_dispatch_limit_divisor(class: DispatchClass) -> Weight { + match class { + DispatchClass::Operational => 1, + DispatchClass::Normal => 4, + } + } + /// Checks if the current extrinsic can fit into the block with respect to block weight limits. + /// + /// Upon successes, it returns the new block weight as a `Result`. + fn check_weight(info: DispatchInfo) -> Result { + let current_weight = Module::::all_extrinsics_weight(); + let maximum_weight = T::MaximumBlockWeight::get(); + let limit = maximum_weight / Self::get_dispatch_limit_divisor(info.class); + let added_weight = info.weight.min(limit); + let next_weight = current_weight.saturating_add(added_weight); + if next_weight > limit { + return Err(DispatchError::BadState) + } + Ok(next_weight) + } + + /// Checks if the current extrinsic can fit into the block with respect to block length limits. + /// + /// Upon successes, it returns the new block length as a `Result`. + fn check_block_length(info: DispatchInfo, len: usize) -> Result { + let current_len = Module::::all_extrinsics_len(); + let maximum_len = T::MaximumBlockLength::get(); + let limit = maximum_len / Self::get_dispatch_limit_divisor(info.class); + let added_len = len as u32; + let next_len = current_len.saturating_add(added_len); + if next_len > limit { + return Err(DispatchError::BadState) + } + Ok(next_len) + } + + /// get the priority of an extrinsic denoted by `info`. + fn get_priority(info: DispatchInfo) -> TransactionPriority { + match info.class { + DispatchClass::Normal => info.weight.into(), + DispatchClass::Operational => Bounded::max_value() + } + } + + /// Utility constructor for tests and client code. + #[cfg(feature = "std")] + pub fn from() -> Self { + Self(PhantomData) + } +} + +impl SignedExtension for CheckWeight { + type AccountId = T::AccountId; + type AdditionalSigned = (); + + fn additional_signed(&self) -> rstd::result::Result<(), &'static str> { Ok(()) } + + fn pre_dispatch( + self, + _who: &Self::AccountId, + info: DispatchInfo, + len: usize, + ) -> Result<(), DispatchError> { + let next_len = Self::check_block_length(info, len)?; + AllExtrinsicsLen::put(next_len); + let next_weight = Self::check_weight(info)?; + AllExtrinsicsWeight::put(next_weight); + Ok(()) + } + + fn validate( + &self, + _who: &Self::AccountId, + info: DispatchInfo, + len: usize, + ) -> Result { + // There is no point in writing to storage here since changes are discarded. This basically + // discards any transaction which is bigger than the length or weight limit alone, which is + // a guarantee that it will fail in the pre-dispatch phase. + let _ = Self::check_block_length(info, len)?; + let _ = Self::check_weight(info)?; + Ok(ValidTransaction { priority: Self::get_priority(info), ..Default::default() }) + } +} + +#[cfg(feature = "std")] +impl rstd::fmt::Debug for CheckWeight { + fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { + write!(f, "CheckWeight") + } +} + +/// Nonce check and increment to give replay protection for transactions. +#[derive(Encode, Decode, Clone, Eq, PartialEq)] +pub struct CheckNonce(#[codec(compact)] T::Index); + +#[cfg(feature = "std")] +impl CheckNonce { + /// utility constructor. Used only in client/factory code. + pub fn from(nonce: T::Index) -> Self { + Self(nonce) + } +} + +#[cfg(feature = "std")] +impl rstd::fmt::Debug for CheckNonce { + fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { + self.0.fmt(f) + } +} + +impl SignedExtension for CheckNonce { + type AccountId = T::AccountId; + type AdditionalSigned = (); + + fn additional_signed(&self) -> rstd::result::Result<(), &'static str> { Ok(()) } + + fn pre_dispatch( + self, + who: &Self::AccountId, + _info: DispatchInfo, + _len: usize, + ) -> Result<(), DispatchError> { + let expected = >::get(who); + if self.0 != expected { + return Err( + if self.0 < expected { DispatchError::Stale } else { DispatchError::Future } + ) + } + >::insert(who, expected + T::Index::one()); + Ok(()) + } + + fn validate( + &self, + who: &Self::AccountId, + info: DispatchInfo, + _len: usize, + ) -> Result { + // check index + let expected = >::get(who); + if self.0 < expected { + return Err(DispatchError::Stale) + } + + let provides = vec![Encode::encode(&(who, self.0))]; + let requires = if expected < self.0 { + vec![Encode::encode(&(who, self.0 - One::one()))] + } else { + vec![] + }; + + Ok(ValidTransaction { + priority: info.weight as TransactionPriority, + requires, + provides, + longevity: TransactionLongevity::max_value(), + propagate: true, + }) + } +} + +/// Nonce check and increment to give replay protection for transactions. +#[derive(Encode, Decode, Clone, Eq, PartialEq)] +pub struct CheckEra((Era, rstd::marker::PhantomData)); + +#[cfg(feature = "std")] +impl CheckEra { + /// utility constructor. Used only in client/factory code. + pub fn from(era: Era) -> Self { + Self((era, rstd::marker::PhantomData)) + } +} + +#[cfg(feature = "std")] +impl rstd::fmt::Debug for CheckEra { + fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { + self.0.fmt(f) + } +} + +impl SignedExtension for CheckEra { + type AccountId = T::AccountId; + type AdditionalSigned = T::Hash; + fn additional_signed(&self) -> Result { + let current_u64 = >::block_number().saturated_into::(); + let n = (self.0).0.birth(current_u64).saturated_into::(); + if !>::exists(n) { Err("transaction birth block ancient")? } + Ok(>::block_hash(n)) + } +} + pub struct ChainContext(::rstd::marker::PhantomData); impl Default for ChainContext { fn default() -> Self { @@ -779,10 +1036,8 @@ mod tests { use super::*; use runtime_io::with_externalities; use substrate_primitives::H256; - use primitives::BuildStorage; - use primitives::traits::{BlakeTwo256, IdentityLookup}; - use primitives::testing::Header; - use srml_support::impl_outer_origin; + use primitives::{traits::{BlakeTwo256, IdentityLookup}, testing::Header}; + use srml_support::{impl_outer_origin, parameter_types}; impl_outer_origin!{ pub enum Origin for Test where system = super {} @@ -790,6 +1045,13 @@ mod tests { #[derive(Clone, Eq, PartialEq)] pub struct Test; + + parameter_types! { + pub const BlockHashCount: u64 = 10; + pub const MaximumBlockWeight: Weight = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + } + impl Trait for Test { type Origin = Origin; type Index = u64; @@ -799,7 +1061,11 @@ mod tests { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; + type WeightMultiplierUpdate = (); type Event = u16; + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; } impl From for u16 { @@ -814,7 +1080,7 @@ mod tests { type System = Module; fn new_test_ext() -> runtime_io::TestExternalities { - GenesisConfig::::default().build_storage().unwrap().0.into() + GenesisConfig::default().build_storage::().unwrap().0.into() } #[test] @@ -917,4 +1183,180 @@ mod tests { ); }); } + + #[test] + fn prunes_block_hash_mappings() { + with_externalities(&mut new_test_ext(), || { + // simulate import of 15 blocks + for n in 1..=15 { + System::initialize( + &n, + &[n as u8 - 1; 32].into(), + &[0u8; 32].into(), + &Default::default(), + ); + + System::finalize(); + } + + // first 5 block hashes are pruned + for n in 0..5 { + assert_eq!( + System::block_hash(n), + H256::zero(), + ); + } + + // the remaining 10 are kept + for n in 5..15 { + assert_eq!( + System::block_hash(n), + [n as u8; 32].into(), + ); + } + }) + } + + #[test] + fn signed_ext_check_nonce_works() { + with_externalities(&mut new_test_ext(), || { + >::insert(1, 1); + let info = DispatchInfo::default(); + let len = 0_usize; + // stale + assert!(CheckNonce::(0).validate(&1, info, len).is_err()); + assert!(CheckNonce::(0).pre_dispatch(&1, info, len).is_err()); + // correct + assert!(CheckNonce::(1).validate(&1, info, len).is_ok()); + assert!(CheckNonce::(1).pre_dispatch(&1, info, len).is_ok()); + // future + assert!(CheckNonce::(5).validate(&1, info, len).is_ok()); + assert!(CheckNonce::(5).pre_dispatch(&1, info, len).is_err()); + }) + } + + #[test] + fn signed_ext_check_weight_works_user_tx() { + with_externalities(&mut new_test_ext(), || { + let small = DispatchInfo { weight: 100, ..Default::default() }; + let medium = DispatchInfo { + weight: >::get() / 4 - 1, + ..Default::default() + }; + let big = DispatchInfo { + weight: >::get() / 4 + 1, + ..Default::default() + }; + let len = 0_usize; + + let reset_check_weight = |i, f, s| { + AllExtrinsicsWeight::put(s); + let r = CheckWeight::(PhantomData).pre_dispatch(&1, i, len); + if f { assert!(r.is_err()) } else { assert!(r.is_ok()) } + }; + + reset_check_weight(small, false, 0); + reset_check_weight(medium, false, 0); + reset_check_weight(big, true, 1); + }) + } + + #[test] + fn signed_ext_check_weight_fee_works() { + with_externalities(&mut new_test_ext(), || { + let free = DispatchInfo { weight: 0, ..Default::default() }; + let len = 0_usize; + + assert_eq!(System::all_extrinsics_weight(), 0); + let r = CheckWeight::(PhantomData).pre_dispatch(&1, free, len); + assert!(r.is_ok()); + assert_eq!(System::all_extrinsics_weight(), 0); + }) + } + + #[test] + fn signed_ext_check_weight_max_works() { + with_externalities(&mut new_test_ext(), || { + let max = DispatchInfo { weight: Weight::max_value(), ..Default::default() }; + let len = 0_usize; + + assert_eq!(System::all_extrinsics_weight(), 0); + let r = CheckWeight::(PhantomData).pre_dispatch(&1, max, len); + assert!(r.is_ok()); + assert_eq!(System::all_extrinsics_weight(), >::get() / 4); + }) + } + + #[test] + fn signed_ext_check_weight_works_operational_tx() { + with_externalities(&mut new_test_ext(), || { + let normal = DispatchInfo { weight: 100, ..Default::default() }; + let op = DispatchInfo { weight: 100, class: DispatchClass::Operational }; + let len = 0_usize; + + // given almost full block + AllExtrinsicsWeight::put(>::get() / 4); + // will not fit. + assert!(CheckWeight::(PhantomData).pre_dispatch(&1, normal, len).is_err()); + // will fit. + assert!(CheckWeight::(PhantomData).pre_dispatch(&1, op, len).is_ok()); + + // likewise for length limit. + let len = 100_usize; + AllExtrinsicsLen::put(>::get() / 4); + assert!(CheckWeight::(PhantomData).pre_dispatch(&1, normal, len).is_err()); + assert!(CheckWeight::(PhantomData).pre_dispatch(&1, op, len).is_ok()); + }) + } + + #[test] + fn signed_ext_check_weight_priority_works() { + with_externalities(&mut new_test_ext(), || { + let normal = DispatchInfo { weight: 100, class: DispatchClass::Normal }; + let op = DispatchInfo { weight: 100, class: DispatchClass::Operational }; + let len = 0_usize; + + assert_eq!( + CheckWeight::(PhantomData).validate(&1, normal, len).unwrap().priority, + 100, + ); + assert_eq!( + CheckWeight::(PhantomData).validate(&1, op, len).unwrap().priority, + Bounded::max_value(), + ); + }) + } + + #[test] + fn signed_ext_check_weight_block_size_works() { + with_externalities(&mut new_test_ext(), || { + let tx = DispatchInfo::default(); + + let reset_check_weight = |s, f| { + AllExtrinsicsLen::put(0); + let r = CheckWeight::(PhantomData).pre_dispatch(&1, tx, s); + if f { assert!(r.is_err()) } else { assert!(r.is_ok()) } + }; + + reset_check_weight(128, false); + reset_check_weight(512, false); + reset_check_weight(513, true); + }) + } + + #[test] + fn signed_ext_check_era_should_work() { + with_externalities(&mut new_test_ext(), || { + // future + assert_eq!( + CheckEra::::from(Era::mortal(4, 2)).additional_signed().err().unwrap(), + "transaction birth block ancient" + ); + + // correct + System::set_block_number(13); + >::insert(12, H256::repeat_byte(1)); + assert!(CheckEra::::from(Era::mortal(4, 12)).additional_signed().is_ok()); + }) + } } diff --git a/srml/timestamp/Cargo.toml b/srml/timestamp/Cargo.toml index 607a6d60d2860d70927394427bad523965dd144f..bb82d1a42b8ebac9af43d71c9ceb4e669afcd96e 100644 --- a/srml/timestamp/Cargo.toml +++ b/srml/timestamp/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" [dependencies] serde = { version = "1.0", optional = true } -parity-codec = { version = "3.3", default-features = false, features = ["derive"] } +parity-codec = { version = "4.1.1", default-features = false, features = ["derive"] } rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false } runtime_primitives = { package = "sr-primitives", path = "../../core/sr-primitives", default-features = false } inherents = { package = "substrate-inherents", path = "../../core/inherents", default-features = false } diff --git a/srml/timestamp/src/lib.rs b/srml/timestamp/src/lib.rs index e9c0d85a20a0522205aa381916acfe899ea59080..43aa29e04fd24d49613bace0c14df5180239fc29 100644 --- a/srml/timestamp/src/lib.rs +++ b/srml/timestamp/src/lib.rs @@ -44,7 +44,10 @@ //! //! * `get` - Gets the current time for the current block. If this function is called prior to //! setting the timestamp, it will return the timestamp of the previous block. -//! * `minimum_period` - Gets the minimum (and advised) period between blocks for the chain. +//! +//! ### Trait Getters +//! +//! * `MinimumPeriod` - Gets the minimum (and advised) period between blocks for the chain. //! //! ## Usage //! @@ -93,8 +96,7 @@ use parity_codec::Encode; use parity_codec::Decode; #[cfg(feature = "std")] use inherents::ProvideInherentData; -use srml_support::{StorageValue, Parameter, decl_storage, decl_module}; -use srml_support::for_each_tuple; +use srml_support::{StorageValue, Parameter, decl_storage, decl_module, for_each_tuple, traits::Get}; use runtime_primitives::traits::{SimpleArithmetic, Zero, SaturatedConversion}; use system::ensure_none; use inherents::{RuntimeString, InherentIdentifier, ProvideInherent, IsFatalError, InherentData}; @@ -208,23 +210,36 @@ pub trait Trait: system::Trait { /// Something which can be notified when the timestamp is set. Set this to `()` if not needed. type OnTimestampSet: OnTimestampSet; + + /// The minimum period between blocks. Beware that this is different to the *expected* period + /// that the block production apparatus provides. Your chosen consensus system will generally + /// work with this to determine a sensible block time. e.g. For Aura, it will be double this + /// period on default settings. + type MinimumPeriod: Get; } decl_module! { pub struct Module for enum Call where origin: T::Origin { + /// The minimum period between blocks. Beware that this is different to the *expected* period + /// that the block production apparatus provides. Your chosen consensus system will generally + /// work with this to determine a sensible block time. e.g. For Aura, it will be double this + /// period on default settings. + const MinimumPeriod: T::Moment = T::MinimumPeriod::get(); + /// Set the current time. /// - /// This call should be invoked exactly once per block. It will panic at the finalization phase, - /// if this call hasn't been invoked by that time. + /// This call should be invoked exactly once per block. It will panic at the finalization + /// phase, if this call hasn't been invoked by that time. /// - /// The timestamp should be greater than the previous one by the amount specified by `minimum_period`. + /// The timestamp should be greater than the previous one by the amount specified by + /// `MinimumPeriod`. /// /// The dispatch origin for this call must be `Inherent`. fn set(origin, #[compact] now: T::Moment) { ensure_none(origin)?; assert!(!::DidUpdate::exists(), "Timestamp must be updated only once in the block"); assert!( - Self::now().is_zero() || now >= Self::now() + >::get(), + Self::now().is_zero() || now >= Self::now() + T::MinimumPeriod::get(), "Timestamp must increment by at least between sequential blocks" ); ::Now::put(now.clone()); @@ -233,16 +248,6 @@ decl_module! { >::on_timestamp_set(now); } - // Manage upgrade. Remove after all networks upgraded. - // TODO: #2133 - fn on_initialize() { - if let Some(period) = >::take() { - if !>::exists() { - >::put(period) - } - } - } - fn on_finalize() { assert!(::DidUpdate::take(), "Timestamp must be updated once in the block"); } @@ -254,16 +259,6 @@ decl_storage! { /// Current time for the current block. pub Now get(now) build(|_| 0.into()): T::Moment; - /// Old storage item provided for compatibility. Remove after all networks upgraded. - // TODO: #2133 - pub BlockPeriod: Option; - - /// The minimum period between blocks. Beware that this is different to the *expected* period - /// that the block production apparatus provides. Your chosen consensus system will generally - /// work with this to determine a sensible block time. e.g. For Aura, it will be double this - /// period on default settings. - pub MinimumPeriod get(minimum_period) config(): T::Moment = 3.into(); - /// Did the timestamp get updated in this block? DidUpdate: bool; } @@ -301,7 +296,7 @@ impl ProvideInherent for Module { .expect("Gets and decodes timestamp inherent data") .saturated_into(); - let next_time = cmp::max(data, Self::now() + >::get()); + let next_time = cmp::max(data, Self::now() + T::MinimumPeriod::get()); Some(Call::set(next_time.into())) } @@ -315,7 +310,7 @@ impl ProvideInherent for Module { let data = extract_inherent_data(data).map_err(|e| InherentError::Other(e))?; - let minimum = (Self::now() + >::get()).saturated_into::(); + let minimum = (Self::now() + T::MinimumPeriod::get()).saturated_into::(); if t > data + MAX_TIMESTAMP_DRIFT { Err(InherentError::Other("Timestamp too far in future to accept".into())) } else if t < minimum { @@ -330,12 +325,10 @@ impl ProvideInherent for Module { mod tests { use super::*; - use srml_support::{impl_outer_origin, assert_ok}; + use srml_support::{impl_outer_origin, assert_ok, parameter_types}; use runtime_io::{with_externalities, TestExternalities}; use substrate_primitives::H256; - use runtime_primitives::BuildStorage; - use runtime_primitives::traits::{BlakeTwo256, IdentityLookup}; - use runtime_primitives::testing::Header; + use runtime_primitives::{traits::{BlakeTwo256, IdentityLookup}, testing::Header}; impl_outer_origin! { pub enum Origin for Test {} @@ -343,6 +336,11 @@ mod tests { #[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; + } impl system::Trait for Test { type Origin = Origin; type Index = u64; @@ -352,22 +350,26 @@ mod tests { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; + type WeightMultiplierUpdate = (); type Event = (); + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; + } + parameter_types! { + pub const MinimumPeriod: u64 = 5; } impl Trait for Test { type Moment = u64; type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; } type Timestamp = Module; #[test] fn timestamp_works() { - let mut t = system::GenesisConfig::::default().build_storage().unwrap().0; - t.extend(GenesisConfig:: { - minimum_period: 5, - }.build_storage().unwrap().0); - - with_externalities(&mut TestExternalities::new(t), || { + let t = system::GenesisConfig::default().build_storage::().unwrap(); + with_externalities(&mut TestExternalities::new_with_children(t), || { Timestamp::set_timestamp(42); assert_ok!(Timestamp::dispatch(Call::set(69), Origin::NONE)); assert_eq!(Timestamp::now(), 69); @@ -377,12 +379,8 @@ mod tests { #[test] #[should_panic(expected = "Timestamp must be updated only once in the block")] fn double_timestamp_should_fail() { - let mut t = system::GenesisConfig::::default().build_storage().unwrap().0; - t.extend(GenesisConfig:: { - minimum_period: 5, - }.build_storage().unwrap().0); - - with_externalities(&mut TestExternalities::new(t), || { + let t = system::GenesisConfig::default().build_storage::().unwrap(); + with_externalities(&mut TestExternalities::new_with_children(t), || { Timestamp::set_timestamp(42); assert_ok!(Timestamp::dispatch(Call::set(69), Origin::NONE)); let _ = Timestamp::dispatch(Call::set(70), Origin::NONE); @@ -392,12 +390,8 @@ mod tests { #[test] #[should_panic(expected = "Timestamp must increment by at least between sequential blocks")] fn block_period_minimum_enforced() { - let mut t = system::GenesisConfig::::default().build_storage().unwrap().0; - t.extend(GenesisConfig:: { - minimum_period: 5, - }.build_storage().unwrap().0); - - with_externalities(&mut TestExternalities::new(t), || { + let t = system::GenesisConfig::default().build_storage::().unwrap(); + with_externalities(&mut TestExternalities::new_with_children(t), || { Timestamp::set_timestamp(42); let _ = Timestamp::dispatch(Call::set(46), Origin::NONE); }); diff --git a/srml/treasury/Cargo.toml b/srml/treasury/Cargo.toml index 68b0451caf857e04105936d34264582d2bdcf226..64190f9c4fb8123560db3b0f4c138b91fab37a17 100644 --- a/srml/treasury/Cargo.toml +++ b/srml/treasury/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" [dependencies] serde = { version = "1.0", optional = true, features = ["derive"] } -parity-codec = { version = "3.3", default-features = false, features = ["derive"] } +parity-codec = { version = "4.1.1", default-features = false, features = ["derive"] } rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false } runtime_primitives = { package = "sr-primitives", path = "../../core/sr-primitives", default-features = false } srml-support = { path = "../support", default-features = false } diff --git a/srml/treasury/src/lib.rs b/srml/treasury/src/lib.rs index 95fa90f88ff5f543daf03d8145b98a19f315249d..c8cf84355aca01e638c4ceada44bbbf0aa15b286 100644 --- a/srml/treasury/src/lib.rs +++ b/srml/treasury/src/lib.rs @@ -70,10 +70,14 @@ #[cfg(feature = "std")] use serde::{Serialize, Deserialize}; use rstd::prelude::*; -use srml_support::{StorageValue, StorageMap, decl_module, decl_storage, decl_event, ensure}; -use srml_support::traits::{Currency, ReservableCurrency, OnDilution, OnUnbalanced, Imbalance}; -use runtime_primitives::{Permill, - traits::{Zero, EnsureOrigin, StaticLookup, Saturating, CheckedSub, CheckedMul} +use srml_support::{StorageValue, StorageMap, decl_module, decl_storage, decl_event, ensure, print}; +use srml_support::traits::{ + Currency, ExistenceRequirement, Get, Imbalance, OnDilution, OnUnbalanced, + ReservableCurrency, WithdrawReason +}; +use runtime_primitives::{Permill, ModuleId}; +use runtime_primitives::traits::{ + Zero, EnsureOrigin, StaticLookup, CheckedSub, CheckedMul, AccountIdConversion }; use parity_codec::{Encode, Decode}; use system::ensure_signed; @@ -82,6 +86,13 @@ type BalanceOf = <::Currency as Currency<::Ac type PositiveImbalanceOf = <::Currency as Currency<::AccountId>>::PositiveImbalance; type NegativeImbalanceOf = <::Currency as Currency<::AccountId>>::NegativeImbalance; +const MODULE_ID: ModuleId = ModuleId(*b"py/trsry"); + +pub const DEFAULT_PROPOSAL_BOND: u32 = 0; +pub const DEFAULT_PROPOSAL_BOND_MINIMUM: u32 = 0; +pub const DEFAULT_SPEND_PERIOD: u32 = 0; +pub const DEFAULT_BURN: u32 = 0; + pub trait Trait: system::Trait { /// The staking balance. type Currency: Currency + ReservableCurrency; @@ -100,12 +111,38 @@ pub trait Trait: system::Trait { /// Handler for the unbalanced decrease when slashing for a rejected proposal. type ProposalRejection: OnUnbalanced>; + + /// Fraction of a proposal's value that should be bonded in order to place the proposal. + /// An accepted proposal gets these back. A rejected proposal does not. + type ProposalBond: Get; + + /// Minimum amount of funds that should be placed in a deposit for making a proposal. + type ProposalBondMinimum: Get>; + + /// Period between successive spends. + type SpendPeriod: Get; + + /// Percentage of spare funds (if any) that are burnt per spend period. + type Burn: Get; } type ProposalIndex = u32; decl_module! { pub struct Module for enum Call where origin: T::Origin { + /// Fraction of a proposal's value that should be bonded in order to place the proposal. + /// An accepted proposal gets these back. A rejected proposal does not. + const ProposalBond: Permill = T::ProposalBond::get(); + + /// Minimum amount of funds that should be placed in a deposit for making a proposal. + const ProposalBondMinimum: BalanceOf = T::ProposalBondMinimum::get(); + + /// Period between successive spends. + const SpendPeriod: T::BlockNumber = T::SpendPeriod::get(); + + /// Percentage of spare funds (if any) that are burnt per spend period. + const Burn: Permill = T::Burn::get(); + fn deposit_event() = default; /// Put forward a suggestion for spending. A deposit proportional to the value /// is reserved and slashed if the proposal is rejected. It is returned once the @@ -129,31 +166,12 @@ decl_module! { .map_err(|_| "Proposer's balance too low")?; let c = Self::proposal_count(); - >::put(c + 1); + ProposalCount::put(c + 1); >::insert(c, Proposal { proposer, value, beneficiary, bond }); Self::deposit_event(RawEvent::Proposed(c)); } - /// Set the balance of funds available to spend. - fn set_pot(#[compact] new_pot: BalanceOf) { - // Put the new value into storage. - >::put(new_pot); - } - - /// (Re-)configure this module. - fn configure( - #[compact] proposal_bond: Permill, - #[compact] proposal_bond_minimum: BalanceOf, - #[compact] spend_period: T::BlockNumber, - #[compact] burn: Permill - ) { - >::put(proposal_bond); - >::put(proposal_bond_minimum); - >::put(spend_period); - >::put(burn); - } - /// Reject a proposed spend. The original deposit will be slashed. /// /// # @@ -183,12 +201,12 @@ decl_module! { ensure!(>::exists(proposal_id), "No proposal at that index"); - >::mutate(|v| v.push(proposal_id)); + Approvals::mutate(|v| v.push(proposal_id)); } fn on_finalize(n: T::BlockNumber) { // Check to see if we should spend some funds! - if (n % Self::spend_period()).is_zero() { + if (n % T::SpendPeriod::get()).is_zero() { Self::spend_funds(); } } @@ -207,26 +225,6 @@ pub struct Proposal { decl_storage! { trait Store for Module as Treasury { - // Config... - - /// Fraction of a proposal's value that should be bonded in order to place the proposal. - /// An accepted proposal gets these back. A rejected proposal does not. - ProposalBond get(proposal_bond) config(): Permill; - - /// Minimum amount of funds that should be placed in a deposit for making a proposal. - ProposalBondMinimum get(proposal_bond_minimum) config(): BalanceOf; - - /// Period between successive spends. - SpendPeriod get(spend_period) config(): T::BlockNumber = runtime_primitives::traits::One::one(); - - /// Percentage of spare funds (if any) that are burnt per spend period. - Burn get(burn) config(): Permill; - - // State... - - /// Total funds available to this module for spending. - Pot get(pot): BalanceOf; - /// Number of proposals that have been made. ProposalCount get(proposal_count): ProposalIndex; @@ -260,9 +258,17 @@ decl_event!( impl Module { // Add public immutables and private mutables. + /// The account ID of the treasury pot. + /// + /// This actually does computation. If you need to keep using it, then make sure you cache the + /// value and only call this once. + pub fn account_id() -> T::AccountId { + MODULE_ID.into_account() + } + /// The needed bond for a proposal whose spend is `value`. fn calculate_bond(value: BalanceOf) -> BalanceOf { - Self::proposal_bond_minimum().max(Self::proposal_bond() * value) + T::ProposalBondMinimum::get().max(T::ProposalBond::get() * value) } // Spend some money! @@ -272,7 +278,7 @@ impl Module { let mut missed_any = false; let mut imbalance = >::zero(); - >::mutate(|v| { + Approvals::mutate(|v| { v.retain(|&index| { // Should always be true, but shouldn't panic if false or we're screwed. if let Some(p) = Self::proposals(index) { @@ -298,18 +304,36 @@ impl Module { }); }); - T::MintedForSpending::on_unbalanced(imbalance); - if !missed_any { // burn some proportion of the remaining budget if we run a surplus. - let burn = (Self::burn() * budget_remaining).min(budget_remaining); + let burn = (T::Burn::get() * budget_remaining).min(budget_remaining); budget_remaining -= burn; + imbalance.subsume(T::Currency::burn(burn)); Self::deposit_event(RawEvent::Burnt(burn)) } + if let Err(problem) = T::Currency::settle( + &Self::account_id(), + imbalance, + WithdrawReason::Transfer, + ExistenceRequirement::KeepAlive + ) { + print("Inconsistent state - couldn't settle imbalance for funds spent by treasury"); + // Nothing else to do here. + drop(problem); + } + Self::deposit_event(RawEvent::Rollover(budget_remaining)); + } + + fn pot() -> BalanceOf { + T::Currency::free_balance(&Self::account_id()) + } +} - >::put(budget_remaining); +impl OnUnbalanced> for Module { + fn on_unbalanced(amount: NegativeImbalanceOf) { + T::Currency::resolve_creating(&Self::account_id(), amount); } } @@ -322,7 +346,7 @@ impl OnDilution> for Module { if let Some(funding) = total_issuance.checked_sub(&portion) { let funding = funding / portion; if let Some(funding) = funding.checked_mul(&minted) { - >::mutate(|x| *x = x.saturating_add(funding)); + Self::on_unbalanced(T::Currency::issue(funding)); } } } @@ -334,11 +358,9 @@ mod tests { use super::*; use runtime_io::with_externalities; - use srml_support::{impl_outer_origin, assert_ok, assert_noop}; + use srml_support::{assert_noop, assert_ok, impl_outer_origin, parameter_types}; use substrate_primitives::{H256, Blake2Hasher}; - use runtime_primitives::BuildStorage; - use runtime_primitives::traits::{BlakeTwo256, OnFinalize, IdentityLookup}; - use runtime_primitives::testing::Header; + use runtime_primitives::{traits::{BlakeTwo256, OnFinalize, IdentityLookup}, testing::Header}; impl_outer_origin! { pub enum Origin for Test {} @@ -346,6 +368,11 @@ mod tests { #[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; + } impl system::Trait for Test { type Origin = Origin; type Index = u64; @@ -355,7 +382,18 @@ mod tests { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; + type WeightMultiplierUpdate = (); type Event = (); + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; + } + 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; @@ -365,6 +403,17 @@ mod tests { type TransactionPayment = (); type TransferPayment = (); type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type TransferFee = TransferFee; + type CreationFee = CreationFee; + type TransactionBaseFee = TransactionBaseFee; + type TransactionByteFee = TransactionByteFee; + } + parameter_types! { + pub const ProposalBond: Permill = Permill::from_percent(5); + pub const ProposalBondMinimum: u64 = 1; + pub const SpendPeriod: u64 = 2; + pub const Burn: Permill = Permill::from_percent(50); } impl Trait for Test { type Currency = balances::Module; @@ -373,37 +422,26 @@ mod tests { type Event = (); type MintedForSpending = (); type ProposalRejection = (); + type ProposalBond = ProposalBond; + type ProposalBondMinimum = ProposalBondMinimum; + type SpendPeriod = SpendPeriod; + type Burn = Burn; } type Balances = balances::Module; type Treasury = Module; fn new_test_ext() -> runtime_io::TestExternalities { - let mut t = system::GenesisConfig::::default().build_storage().unwrap().0; + let mut t = system::GenesisConfig::default().build_storage::().unwrap().0; t.extend(balances::GenesisConfig::{ balances: vec![(0, 100), (1, 99), (2, 1)], - transaction_base_fee: 0, - transaction_byte_fee: 0, - transfer_fee: 0, - creation_fee: 0, - existential_deposit: 0, vesting: vec![], }.build_storage().unwrap().0); - t.extend(GenesisConfig::{ - proposal_bond: Permill::from_percent(5), - proposal_bond_minimum: 1, - spend_period: 2, - burn: Permill::from_percent(50), - }.build_storage().unwrap().0); t.into() } #[test] fn genesis_config_works() { with_externalities(&mut new_test_ext(), || { - assert_eq!(Treasury::proposal_bond(), Permill::from_percent(5)); - assert_eq!(Treasury::proposal_bond_minimum(), 1); - assert_eq!(Treasury::spend_period(), 2); - assert_eq!(Treasury::burn(), Permill::from_percent(50)); assert_eq!(Treasury::pot(), 0); assert_eq!(Treasury::proposal_count(), 0); }); @@ -521,6 +559,7 @@ mod tests { fn accepted_spend_proposal_enacted_on_spend_period() { with_externalities(&mut new_test_ext(), || { Treasury::on_dilution(100, 100); + assert_eq!(Treasury::pot(), 100); assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3)); assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); @@ -538,25 +577,27 @@ mod tests { fn on_dilution_quantization_effects() { with_externalities(&mut new_test_ext(), || { // minted = 1% of total issuance for all cases - let _ = Treasury::set_pot(0); assert_eq!(Balances::total_issuance(), 200); Treasury::on_dilution(2, 66); // portion = 33% of total issuance assert_eq!(Treasury::pot(), 4); // should increase by 4 (200 - 66) / 66 * 2 + Balances::make_free_balance_be(&Treasury::account_id(), 0); Treasury::on_dilution(2, 67); // portion = 33+eps% of total issuance - assert_eq!(Treasury::pot(), 6); // should increase by 2 (200 - 67) / 67 * 2 + assert_eq!(Treasury::pot(), 2); // should increase by 2 (200 - 67) / 67 * 2 + Balances::make_free_balance_be(&Treasury::account_id(), 0); Treasury::on_dilution(2, 100); // portion = 50% of total issuance - assert_eq!(Treasury::pot(), 8); // should increase by 2 (200 - 100) / 100 * 2 + assert_eq!(Treasury::pot(), 2); // should increase by 2 (200 - 100) / 100 * 2 + Balances::make_free_balance_be(&Treasury::account_id(), 0); // If any more than 50% of the network is staked (i.e. (2 * portion) > total_issuance) // then the pot will not increase. Treasury::on_dilution(2, 101); // portion = 50+eps% of total issuance - assert_eq!(Treasury::pot(), 8); // should increase by 0 (200 - 101) / 101 * 2 + assert_eq!(Treasury::pot(), 0); // should increase by 0 (200 - 101) / 101 * 2 Treasury::on_dilution(2, 134); // portion = 67% of total issuance - assert_eq!(Treasury::pot(), 8); // should increase by 0 (200 - 134) / 134 * 2 + assert_eq!(Treasury::pot(), 0); // should increase by 0 (200 - 134) / 134 * 2 }); } @@ -574,7 +615,7 @@ mod tests { Treasury::on_dilution(100, 100); >::on_finalize(4); assert_eq!(Balances::free_balance(&3), 150); - assert_eq!(Treasury::pot(), 25); + assert_eq!(Treasury::pot(), 75); }); } } diff --git a/subkey/Cargo.toml b/subkey/Cargo.toml index 5f043e9486c5bc5d6231fca57808eb5dbfebb4e1..6ecede88edaec4d81479513033f99b55799fdddf 100644 --- a/subkey/Cargo.toml +++ b/subkey/Cargo.toml @@ -13,11 +13,13 @@ rand = "0.6" clap = { version = "~2.32", features = ["yaml"] } tiny-bip39 = "0.6.0" rustc-hex = "2.0" -substrate-bip39 = { git = "https://github.com/paritytech/substrate-bip39" } -schnorrkel = "0.1" +substrate-bip39 = "0.2.2" +schnorrkel = "0.1.1" hex = "0.3" hex-literal = "0.2" -parity-codec = "3.2" +parity-codec = "4.1.1" +system = { package = "srml-system", path = "../srml/system" } +balances = { package = "srml-balances", path = "../srml/balances" } [features] bench = [] diff --git a/subkey/src/cli.yml b/subkey/src/cli.yml index 89190df3624f5e3310ad125eacf98fce2ce622db..b4f99f8743fa545898a93e06e9693c737b66bc16 100644 --- a/subkey/src/cli.yml +++ b/subkey/src/cli.yml @@ -18,6 +18,12 @@ args: takes_value: true required: false help: The password for the key + - network: + short: n + long: network + takes_value: true + required: false + help: Specify a network. One of substrate (default), polkadot and kusama. subcommands: - generate: about: Generate a random account diff --git a/subkey/src/main.rs b/subkey/src/main.rs index b38bffd772bbf375491f9060a1784d52b84af59e..f9432b42c12b38cc7885751191d11c1a32e3709a 100644 --- a/subkey/src/main.rs +++ b/subkey/src/main.rs @@ -18,21 +18,24 @@ #[cfg(feature = "bench")] extern crate test; -use std::{str::FromStr, io::{stdin, Read}}; +use std::{str::FromStr, io::{stdin, Read}, convert::TryInto}; use hex_literal::hex; use clap::load_yaml; use bip39::{Mnemonic, Language, MnemonicType}; -use substrate_primitives::{ed25519, sr25519, hexdisplay::HexDisplay, Pair, crypto::Ss58Codec, blake2_256}; +use substrate_primitives::{ + ed25519, sr25519, hexdisplay::HexDisplay, Pair, Public, blake2_256, + crypto::{Ss58Codec, set_default_ss58_version, Ss58AddressFormat} +}; use parity_codec::{Encode, Decode, Compact}; use sr_primitives::generic::Era; use node_primitives::{Balance, Index, Hash}; -use node_runtime::{Call, UncheckedExtrinsic, BalancesCall}; +use node_runtime::{Call, UncheckedExtrinsic, /*CheckNonce, TakeFees, */BalancesCall}; mod vanity; trait Crypto { type Pair: Pair; - type Public: Ss58Codec + AsRef<[u8]>; + type Public: Public + Ss58Codec + AsRef<[u8]> + std::hash::Hash; fn pair_from_suri(suri: &str, password: Option<&str>) -> Self::Pair { Self::Pair::from_string(suri, password).expect("Invalid phrase") } @@ -52,11 +55,12 @@ trait Crypto { HexDisplay::from(&Self::public_from_pair(&pair)), Self::ss58_from_pair(&pair) ); - } else if let Ok(public) = ::Public::from_string(uri) { - println!("Public Key URI `{}` is account:\n Public key (hex): 0x{}\n Address (SS58): {}", + } else if let Ok((public, v)) = ::Public::from_string_with_version(uri) { + println!("Public Key URI `{}` is account:\n Network ID/version: {}\n Public key (hex): 0x{}\n Address (SS58): {}", uri, + String::from(Ss58AddressFormat::from(v)), HexDisplay::from(&public.as_ref()), - public.to_ss58check() + public.to_ss58check_with_version(v) ); } else { println!("Invalid phrase/URI given"); @@ -86,7 +90,21 @@ fn execute(matches: clap::ArgMatches) where <::Pair as Pair>::Signature: AsRef<[u8]> + AsMut<[u8]> + Default, <::Pair as Pair>::Public: Sized + AsRef<[u8]> + Ss58Codec + AsRef<<::Pair as Pair>::Public>, { + // let extra = |i: Index, f: Balance| { + // ( + // system::CheckEra::::from(Era::Immortal), + // system::CheckNonce::::from(i), + // system::CheckWeight::::from(), + // balances::TakeFees::::from(f), + // ) + // }; let password = matches.value_of("password"); + let maybe_network = matches.value_of("network"); + if let Some(network) = maybe_network { + let v = network.try_into() + .expect("Invalid network name: must be polkadot/substrate/kusama"); + set_default_ss58_version(v); + } match matches.subcommand() { ("generate", Some(matches)) => { // create a new randomly generated mnemonic phrase @@ -120,7 +138,7 @@ fn execute(matches: clap::ArgMatches) where let sig = pair.sign(&message); println!("{}", hex::encode(&sig)); } - ("transfer", Some(matches)) => { + /*("transfer", Some(matches)) => { let signer = matches.value_of("from") .expect("parameter is required; thus it can't be None; qed"); let signer = Sr25519::pair_from_suri(signer, password); @@ -147,13 +165,12 @@ fn execute(matches: clap::ArgMatches) where "elm" => hex!["10c08714a10c7da78f40a60f6f732cf0dba97acfb5e2035445b032386157d5c3"].into(), "alex" => hex!["dcd1346701ca8396496e52aa2785b1748deb6db09551b72159dcb3e08991025b"].into(), h => hex::decode(h).ok().and_then(|x| Decode::decode(&mut &x[..])) - .expect("Invalid genesis hash or unrecognised chain identifier"), + .expect("Invalid genesis hash or unrecognised chain identifier"), }; println!("Using a genesis hash of {}", HexDisplay::from(&genesis_hash.as_ref())); - let era = Era::immortal(); - let raw_payload = (Compact(index), function, era, genesis_hash); + let raw_payload = (function, extra(index, 0), genesis_hash); let signature = raw_payload.using_encoded(|payload| if payload.len() > 256 { signer.sign(&blake2_256(payload)[..]) } else { @@ -161,11 +178,10 @@ fn execute(matches: clap::ArgMatches) where signer.sign(payload) }); let extrinsic = UncheckedExtrinsic::new_signed( - index, - raw_payload.1, + raw_payload.0, signer.public().into(), signature.into(), - era, + extra(index, 0), ); println!("0x{}", hex::encode(&extrinsic.encode())); } @@ -192,7 +208,7 @@ fn execute(matches: clap::ArgMatches) where let era = Era::immortal(); - let raw_payload = (Compact(index), function, era, prior_block_hash); + let raw_payload = (function, era, prior_block_hash, extra(index, 0)); let signature = raw_payload.using_encoded(|payload| if payload.len() > 256 { signer.sign(&blake2_256(payload)[..]) @@ -202,15 +218,14 @@ fn execute(matches: clap::ArgMatches) where ); let extrinsic = UncheckedExtrinsic::new_signed( - index, - raw_payload.1, + raw_payload.0, signer.public().into(), signature.into(), - era, + extra(index, 0), ); println!("0x{}", hex::encode(&extrinsic.encode())); - } + }*/ ("verify", Some(matches)) => { let sig_data = matches.value_of("sig") .expect("signature parameter is required; thus it can't be None; qed"); diff --git a/test-utils/chain-spec-builder/Cargo.lock b/test-utils/chain-spec-builder/Cargo.lock index 89c08fd77e7af9297381730b0c1141121f36c500..afa92a46f8f4dd70021d0b2e7a53fb81dff6a3fa 100644 --- a/test-utils/chain-spec-builder/Cargo.lock +++ b/test-utils/chain-spec-builder/Cargo.lock @@ -463,7 +463,7 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1370,7 +1370,7 @@ dependencies = [ "parity-multiaddr 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-multihash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "stdweb 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1402,7 +1402,7 @@ dependencies = [ "rw-stream-sink 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "secp256k1 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-timer 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1447,7 +1447,7 @@ dependencies = [ "libp2p-core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "protobuf 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", + "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)", @@ -1466,7 +1466,7 @@ dependencies = [ "parity-multiaddr 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "protobuf 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", + "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)", "tokio-timer 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1493,7 +1493,7 @@ dependencies = [ "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "protobuf 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", + "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)", "tokio-timer 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1514,7 +1514,7 @@ dependencies = [ "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "parity-multiaddr 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (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)", "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-timer 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1860,7 +1860,7 @@ dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", + "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)", @@ -2238,7 +2238,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2250,7 +2250,7 @@ dependencies = [ "libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2262,7 +2262,7 @@ dependencies = [ "libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2941,7 +2941,7 @@ dependencies = [ "rand_core 0.4.0 (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)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "static_slice 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "subtle 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3763,7 +3763,7 @@ dependencies = [ "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", "slog 2.4.1 (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.9 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "substrate-peerset 1.0.0", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-timer 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4457,7 +4457,7 @@ name = "unicode-normalization" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -5091,7 +5091,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum slog-json 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ddc0d2aff1f8f325ef660d9a0eb6e6dcd20b30b3f581a5897f58bf42d061c37a" "checksum slog-scope 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "60c04b4726fa04595ccf2c2dad7bcd15474242c4c5e109a8a376e8a2c9b1539a" "checksum slog_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9eff3b513cf2e0d1a60e1aba152dc72bedc5b05585722bb3cebd7bcb1e31b98f" -"checksum smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c4488ae950c49d403731982257768f48fada354a5203fe81f9bb6f43ca9002be" +"checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" "checksum snow 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5a64f02fd208ef15bd2d1a65861df4707e416151e1272d02c8faafad1c138100" "checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3" "checksum spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44363f6f51401c34e7be73db0db371c04705d35efbe9f7d6082e03a921a32c55" diff --git a/test-utils/transaction-factory/Cargo.toml b/test-utils/transaction-factory/Cargo.toml index 66faaf6b345820bff589b319255de8a177e89813..2868b4f5372eedf4dae28ad887a20ebb3b2844ef 100644 --- a/test-utils/transaction-factory/Cargo.toml +++ b/test-utils/transaction-factory/Cargo.toml @@ -9,7 +9,7 @@ cli = { package = "substrate-cli", path = "../../core/cli" } client = { package = "substrate-client", path = "../../core/client" } consensus_common = { package = "substrate-consensus-common", path = "../../core/consensus/common" } log = "0.4" -parity-codec = { version = "3.3", default-features = false, features = ["derive"] } +parity-codec = { version = "4.1.1", default-features = false, features = ["derive"] } primitives = { package = "substrate-primitives", path = "../../core/primitives", default-features = false } sr_primitives = { package = "sr-primitives", path = "../../core/sr-primitives", default-features = false } substrate-service = { path = "../../core/service" } diff --git a/test-utils/transaction-factory/src/lib.rs b/test-utils/transaction-factory/src/lib.rs index 8f292f3a02a2e48b6106e2ae8da8ea55d12ec68d..61dbfab65844d8571a252d7f4310af450e47591a 100644 --- a/test-utils/transaction-factory/src/lib.rs +++ b/test-utils/transaction-factory/src/lib.rs @@ -30,7 +30,7 @@ use log::info; use client::block_builder::api::BlockBuilder; use client::runtime_api::ConstructRuntimeApi; use consensus_common::{ - BlockOrigin, ImportBlock, InherentData, ForkChoiceStrategy, + BlockOrigin, BlockImportParams, InherentData, ForkChoiceStrategy, SelectChain }; use consensus_common::block_import::BlockImport; @@ -166,7 +166,7 @@ fn import_block( block: ::Block ) -> () where F: ServiceFactory { - let import = ImportBlock { + let import = BlockImportParams { origin: BlockOrigin::File, header: block.header().clone(), post_digests: Vec::new(), @@ -176,5 +176,5 @@ fn import_block( auxiliary: Vec::new(), fork_choice: ForkChoiceStrategy::LongestChain, }; - client.import_block(import, HashMap::new()).expect("Failed to import block"); + (&**client).import_block(import, HashMap::new()).expect("Failed to import block"); }