From 69c22eeabacf171abb1d6ff816658dea3c758919 Mon Sep 17 00:00:00 2001 From: Gav Date: Tue, 11 Sep 2018 20:30:17 +0200 Subject: [PATCH 1/2] Big refactor. --- .gitignore | 2 +- .gitlab-ci.yml | 2 - Cargo.lock | 391 ++-- Cargo.toml | 110 +- README.adoc | 1 - ci/script.sh | 1 - demo/Cargo.toml | 18 - demo/api/Cargo.toml | 13 - demo/api/src/lib.rs | 155 -- demo/build.rs | 24 - demo/cli/Cargo.toml | 12 - demo/cli/src/cli.yml | 12 - demo/cli/src/error.rs | 29 - demo/cli/src/lib.rs | 122 -- demo/consensus/Cargo.toml | 27 - demo/consensus/README.adoc | 5 - demo/consensus/src/error.rs | 51 - demo/consensus/src/evaluation.rs | 96 - demo/consensus/src/lib.rs | 446 ----- demo/consensus/src/offline_tracker.rs | 137 -- demo/consensus/src/service.rs | 172 -- demo/executor/Cargo.toml | 29 - demo/executor/src/lib.rs | 525 ------ demo/network/Cargo.toml | 17 - demo/network/src/consensus.rs | 297 --- demo/network/src/lib.rs | 117 -- demo/primitives/Cargo.toml | 29 - demo/primitives/src/lib.rs | 94 - demo/runtime/Cargo.toml | 61 - demo/runtime/src/checked_block.rs | 94 - demo/runtime/src/lib.rs | 386 ---- demo/runtime/wasm/Cargo.lock | 1103 ----------- demo/runtime/wasm/Cargo.toml | 63 - demo/runtime/wasm/build.sh | 8 - demo/runtime/wasm/src | 1 - .../release/demo_runtime.compact.wasm | Bin 696725 -> 0 bytes .../release/demo_runtime.wasm | Bin 696823 -> 0 bytes demo/service/Cargo.toml | 27 - demo/service/src/chain_spec.rs | 210 --- demo/service/src/lib.rs | 214 --- demo/src/main.rs | 69 - demo/transaction-pool/Cargo.toml | 19 - demo/transaction-pool/src/error.rs | 73 - demo/transaction-pool/src/lib.rs | 237 --- doc/packages/substrate.adoc | 60 +- scripts/build-demos.sh | 28 - scripts/common.sh | 6 +- subkey/Cargo.toml | 3 +- subkey/src/main.rs | 4 +- substrate/bft/Cargo.toml | 22 - substrate/bft/README.adoc | 13 - substrate/bft/src/error.rs | 94 - substrate/bft/src/lib.rs | 1126 ----------- substrate/cli/Cargo.toml | 35 - substrate/cli/README.adoc | 13 - substrate/cli/build.rs | 59 - substrate/cli/doc/shell-completion.adoc | 41 - substrate/cli/src/cli.yml | 229 --- substrate/cli/src/error.rs | 37 - substrate/cli/src/informant.rs | 124 -- substrate/cli/src/lib.rs | 571 ------ substrate/cli/src/panic_hook.rs | 69 - substrate/client/Cargo.toml | 32 - substrate/client/README.adoc | 13 - substrate/client/db/Cargo.toml | 24 - substrate/client/db/src/cache.rs | 432 ----- substrate/client/db/src/lib.rs | 715 ------- substrate/client/db/src/light.rs | 392 ---- substrate/client/db/src/utils.rs | 174 -- substrate/client/src/backend.rs | 107 -- substrate/client/src/block_builder.rs | 140 -- substrate/client/src/blockchain.rs | 92 - substrate/client/src/call_executor.rs | 199 -- substrate/client/src/cht.rs | 274 --- substrate/client/src/client.rs | 810 -------- substrate/client/src/error.rs | 154 -- substrate/client/src/genesis.rs | 3 +- substrate/client/src/in_mem.rs | 439 ----- substrate/client/src/lib.rs | 70 - substrate/client/src/light/backend.rs | 229 --- substrate/client/src/light/blockchain.rs | 142 -- substrate/client/src/light/call_executor.rs | 191 -- substrate/client/src/light/fetcher.rs | 308 --- substrate/client/src/light/mod.rs | 77 - substrate/client/src/notifications.rs | 289 --- substrate/codec/Cargo.toml | 12 - substrate/codec/README.adoc | 13 - substrate/codec/derive/Cargo.toml | 20 - substrate/codec/derive/src/decode.rs | 111 -- substrate/codec/derive/src/encode.rs | 153 -- substrate/codec/derive/src/lib.rs | 126 -- substrate/codec/derive/tests/mod.rs | 151 -- substrate/codec/src/codec.rs | 540 ------ substrate/codec/src/joiner.rs | 33 - substrate/codec/src/keyedvec.rs | 36 - substrate/codec/src/lib.rs | 45 - substrate/ed25519/Cargo.toml | 12 - substrate/ed25519/README.adoc | 13 - substrate/ed25519/src/lib.rs | 354 ---- substrate/environmental/Cargo.toml | 8 - substrate/environmental/README.adoc | 13 - substrate/environmental/src/lib.rs | 383 ---- substrate/environmental/with_std.rs | 32 - substrate/environmental/without_std.rs | 70 - substrate/executor/Cargo.toml | 33 - substrate/executor/README.adoc | 13 - substrate/executor/src/error.rs | 81 - substrate/executor/src/lib.rs | 94 - substrate/executor/src/native_executor.rs | 224 --- substrate/executor/src/sandbox.rs | 676 ------- substrate/executor/src/wasm_executor.rs | 720 ------- substrate/executor/src/wasm_utils.rs | 199 -- substrate/executor/wasm/Cargo.lock | 216 --- substrate/executor/wasm/Cargo.toml | 19 - substrate/executor/wasm/build.sh | 8 - substrate/executor/wasm/src/lib.rs | 122 -- .../release/runtime_test.compact.wasm | Bin 48108 -> 0 bytes .../release/runtime_test.wasm | Bin 48318 -> 0 bytes substrate/extrinsic-pool/Cargo.toml | 19 - substrate/extrinsic-pool/README.adoc | 13 - substrate/extrinsic-pool/src/error.rs | 33 - substrate/extrinsic-pool/src/lib.rs | 49 - substrate/extrinsic-pool/src/listener.rs | 95 - substrate/extrinsic-pool/src/pool.rs | 606 ------ substrate/extrinsic-pool/src/rotator.rs | 209 --- substrate/extrinsic-pool/src/watcher.rs | 103 - substrate/keyring/Cargo.toml | 9 - substrate/keyring/README.adoc | 13 - substrate/keyring/src/lib.rs | 176 -- substrate/keystore/Cargo.toml | 19 - substrate/keystore/README.adoc | 13 - substrate/keystore/src/lib.rs | 295 --- substrate/misbehavior-check/Cargo.toml | 19 - substrate/misbehavior-check/README.adoc | 13 - substrate/misbehavior-check/src/lib.rs | 206 -- substrate/network-libp2p/Cargo.toml | 34 - substrate/network-libp2p/README.adoc | 13 - .../network-libp2p/src/connection_filter.rs | 31 - substrate/network-libp2p/src/custom_proto.rs | 290 --- substrate/network-libp2p/src/error.rs | 221 --- substrate/network-libp2p/src/lib.rs | 75 - substrate/network-libp2p/src/network_state.rs | 953 ---------- substrate/network-libp2p/src/service.rs | 1437 -------------- substrate/network-libp2p/src/timeouts.rs | 115 -- substrate/network-libp2p/src/topology.rs | 655 ------- substrate/network-libp2p/src/traits.rs | 299 --- substrate/network-libp2p/src/transport.rs | 50 - substrate/network-libp2p/tests/tests.rs | 130 -- substrate/network/Cargo.toml | 30 - substrate/network/README.adoc | 13 - substrate/network/src/blocks.rs | 282 --- substrate/network/src/chain.rs | 105 -- substrate/network/src/config.rs | 32 - substrate/network/src/consensus_gossip.rs | 382 ---- substrate/network/src/error.rs | 35 - substrate/network/src/import_queue.rs | 593 ------ substrate/network/src/io.rs | 72 - substrate/network/src/lib.rs | 70 - substrate/network/src/message.rs | 376 ---- substrate/network/src/on_demand.rs | 736 -------- substrate/network/src/protocol.rs | 679 ------- substrate/network/src/service.rs | 340 ---- substrate/network/src/specialization.rs | 48 - substrate/network/src/sync.rs | 428 ----- substrate/network/src/test/mod.rs | 330 ---- substrate/network/src/test/sync.rs | 121 -- substrate/primitives/Cargo.toml | 51 - substrate/primitives/README.adoc | 13 - substrate/primitives/src/authority_id.rs | 106 -- substrate/primitives/src/bytes.rs | 158 -- substrate/primitives/src/hash.rs | 166 -- substrate/primitives/src/hasher.rs | 53 - substrate/primitives/src/hashing.rs | 106 -- substrate/primitives/src/hexdisplay.rs | 94 - substrate/primitives/src/lib.rs | 134 -- substrate/primitives/src/rlp_codec.rs | 123 -- substrate/primitives/src/sandbox.rs | 221 --- substrate/primitives/src/storage.rs | 44 - substrate/primitives/src/tests.rs | 17 - substrate/primitives/src/u32_trait.rs | 89 - substrate/primitives/src/uint.rs | 99 - substrate/pwasm-alloc/Cargo.toml | 23 - substrate/pwasm-alloc/README.adoc | 25 - substrate/pwasm-alloc/build.rs | 14 - substrate/pwasm-alloc/src/lib.rs | 34 - substrate/pwasm-libc/Cargo.toml | 15 - substrate/pwasm-libc/README.adoc | 24 - substrate/pwasm-libc/src/lib.rs | 55 - substrate/rpc-servers/Cargo.toml | 14 - substrate/rpc-servers/README.adoc | 14 - substrate/rpc-servers/src/lib.rs | 93 - substrate/rpc/Cargo.toml | 27 - substrate/rpc/README.adoc | 13 - substrate/rpc/src/author/error.rs | 68 - substrate/rpc/src/author/mod.rs | 162 -- substrate/rpc/src/author/tests.rs | 178 -- substrate/rpc/src/chain/error.rs | 42 - substrate/rpc/src/chain/mod.rs | 165 -- substrate/rpc/src/chain/tests.rs | 207 -- substrate/rpc/src/errors.rs | 34 - substrate/rpc/src/helpers.rs | 25 - substrate/rpc/src/lib.rs | 59 - substrate/rpc/src/metadata.rs | 54 - substrate/rpc/src/state/error.rs | 54 - substrate/rpc/src/state/mod.rs | 277 --- substrate/rpc/src/state/tests.rs | 186 -- substrate/rpc/src/subscriptions.rs | 85 - substrate/rpc/src/system/error.rs | 40 - substrate/rpc/src/system/mod.rs | 41 - substrate/rpc/src/system/tests.rs | 54 - substrate/runtime-io/Cargo.toml | 34 - substrate/runtime-io/README.adoc | 13 - substrate/runtime-io/build.rs | 14 - substrate/runtime-io/src/lib.rs | 35 - substrate/runtime-io/with_std.rs | 1 - substrate/runtime-io/without_std.rs | 329 ---- substrate/runtime-sandbox/Cargo.toml | 31 - substrate/runtime-sandbox/README.adoc | 13 - substrate/runtime-sandbox/build.rs | 14 - substrate/runtime-sandbox/src/lib.rs | 221 --- substrate/runtime-sandbox/with_std.rs | 482 ----- substrate/runtime-sandbox/without_std.rs | 303 --- substrate/runtime-std/Cargo.toml | 18 - substrate/runtime-std/README.adoc | 14 - substrate/runtime-std/build.rs | 14 - substrate/runtime-std/src/lib.rs | 51 - substrate/runtime-std/with_std.rs | 38 - substrate/runtime-std/without_std.rs | 46 - substrate/runtime-support/Cargo.toml | 34 - substrate/runtime-support/README.adoc | 14 - substrate/runtime-support/src/dispatch.rs | 584 ------ substrate/runtime-support/src/event.rs | 298 --- substrate/runtime-support/src/hashable.rs | 38 - substrate/runtime-support/src/lib.rs | 191 -- substrate/runtime-support/src/metadata.rs | 459 ----- .../runtime-support/src/storage/generator.rs | 1659 ----------------- substrate/runtime-support/src/storage/mod.rs | 609 ------ substrate/runtime/README.adoc | 6 - substrate/runtime/balances/Cargo.toml | 36 - substrate/runtime/balances/src/address.rs | 111 -- .../runtime/balances/src/genesis_config.rs | 84 - substrate/runtime/balances/src/lib.rs | 660 ------- substrate/runtime/balances/src/mock.rs | 77 - substrate/runtime/balances/src/tests.rs | 324 ---- substrate/runtime/consensus/Cargo.toml | 32 - substrate/runtime/consensus/src/lib.rs | 267 --- substrate/runtime/contract/Cargo.toml | 41 - substrate/runtime/contract/src/account_db.rs | 180 -- substrate/runtime/contract/src/double_map.rs | 90 - substrate/runtime/contract/src/exec.rs | 266 --- substrate/runtime/contract/src/gas.rs | 178 -- .../runtime/contract/src/genesis_config.rs | 54 - substrate/runtime/contract/src/lib.rs | 278 --- substrate/runtime/contract/src/tests.rs | 670 ------- .../runtime/contract/src/vm/env_def/macros.rs | 285 --- .../runtime/contract/src/vm/env_def/mod.rs | 315 ---- substrate/runtime/contract/src/vm/mod.rs | 553 ------ substrate/runtime/contract/src/vm/prepare.rs | 286 --- substrate/runtime/council/Cargo.toml | 43 - substrate/runtime/council/src/lib.rs | 238 --- substrate/runtime/council/src/motions.rs | 372 ---- substrate/runtime/council/src/seats.rs | 1355 -------------- substrate/runtime/council/src/voting.rs | 505 ----- substrate/runtime/democracy/Cargo.toml | 37 - substrate/runtime/democracy/src/lib.rs | 681 ------- .../runtime/democracy/src/vote_threshold.rs | 97 - substrate/runtime/example/Cargo.toml | 34 - substrate/runtime/example/src/lib.rs | 415 ----- substrate/runtime/executive/Cargo.toml | 33 - substrate/runtime/executive/src/lib.rs | 359 ---- substrate/runtime/primitives/Cargo.toml | 34 - substrate/runtime/primitives/src/bft.rs | 195 -- .../runtime/primitives/src/generic/block.rs | 105 -- .../src/generic/checked_extrinsic.rs | 59 - .../runtime/primitives/src/generic/digest.rs | 150 -- .../runtime/primitives/src/generic/header.rs | 167 -- .../runtime/primitives/src/generic/mod.rs | 33 - .../runtime/primitives/src/generic/tests.rs | 105 -- .../src/generic/unchecked_extrinsic.rs | 159 -- substrate/runtime/primitives/src/lib.rs | 434 ----- substrate/runtime/primitives/src/testing.rs | 138 -- substrate/runtime/primitives/src/traits.rs | 462 ----- substrate/runtime/session/Cargo.toml | 40 - substrate/runtime/session/src/lib.rs | 467 ----- substrate/runtime/staking/Cargo.toml | 45 - .../runtime/staking/src/genesis_config.rs | 77 - substrate/runtime/staking/src/lib.rs | 576 ------ substrate/runtime/staking/src/mock.rs | 126 -- substrate/runtime/staking/src/tests.rs | 502 ----- substrate/runtime/system/Cargo.toml | 32 - substrate/runtime/system/src/lib.rs | 426 ----- substrate/runtime/timestamp/Cargo.toml | 35 - substrate/runtime/timestamp/src/lib.rs | 210 --- substrate/runtime/treasury/Cargo.toml | 34 - substrate/runtime/treasury/src/lib.rs | 546 ------ substrate/runtime/version/Cargo.toml | 22 - substrate/runtime/version/src/lib.rs | 130 -- substrate/serializer/Cargo.toml | 8 - substrate/serializer/README.adoc | 14 - substrate/serializer/src/lib.rs | 46 - substrate/service/Cargo.toml | 31 - substrate/service/README.adoc | 14 - substrate/service/src/chain_ops.rs | 139 -- substrate/service/src/chain_spec.rs | 170 -- substrate/service/src/components.rs | 256 --- substrate/service/src/config.rs | 121 -- substrate/service/src/error.rs | 35 - substrate/service/src/lib.rs | 424 ----- substrate/state-db/Cargo.toml | 14 - substrate/state-db/README.adoc | 13 - substrate/state-db/src/lib.rs | 407 ---- substrate/state-db/src/pruning.rs | 267 --- substrate/state-db/src/test.rs | 83 - substrate/state-db/src/unfinalized.rs | 533 ------ substrate/state-machine/Cargo.toml | 21 - substrate/state-machine/README.adoc | 13 - substrate/state-machine/src/backend.rs | 193 -- substrate/state-machine/src/ext.rs | 171 -- substrate/state-machine/src/lib.rs | 691 ------- .../state-machine/src/proving_backend.rs | 183 -- substrate/state-machine/src/testing.rs | 118 -- substrate/state-machine/src/trie_backend.rs | 362 ---- substrate/telemetry/Cargo.toml | 15 - substrate/telemetry/README.adoc | 13 - substrate/telemetry/src/lib.rs | 160 -- substrate/test-client/Cargo.toml | 18 - substrate/test-client/README.adoc | 13 - .../test-client/src/block_builder_ext.rs | 42 - substrate/test-client/src/client_ext.rs | 103 - substrate/test-client/src/lib.rs | 66 - substrate/test-runtime/Cargo.toml | 38 - substrate/test-runtime/README.adoc | 13 - substrate/test-runtime/src/genesismap.rs | 67 - substrate/test-runtime/src/lib.rs | 158 -- substrate/test-runtime/src/system.rs | 281 --- substrate/test-runtime/wasm/Cargo.lock | 828 -------- substrate/test-runtime/wasm/Cargo.toml | 42 - substrate/test-runtime/wasm/build.sh | 8 - substrate/test-runtime/wasm/src | 1 - .../substrate_test_runtime.compact.wasm | Bin 52573 -> 0 bytes .../release/substrate_test_runtime.wasm | Bin 52705 -> 0 bytes 341 files changed, 276 insertions(+), 58035 deletions(-) delete mode 100644 demo/Cargo.toml delete mode 100644 demo/api/Cargo.toml delete mode 100644 demo/api/src/lib.rs delete mode 100644 demo/build.rs delete mode 100644 demo/cli/Cargo.toml delete mode 100644 demo/cli/src/cli.yml delete mode 100644 demo/cli/src/error.rs delete mode 100644 demo/cli/src/lib.rs delete mode 100644 demo/consensus/Cargo.toml delete mode 100644 demo/consensus/README.adoc delete mode 100644 demo/consensus/src/error.rs delete mode 100644 demo/consensus/src/evaluation.rs delete mode 100644 demo/consensus/src/lib.rs delete mode 100644 demo/consensus/src/offline_tracker.rs delete mode 100644 demo/consensus/src/service.rs delete mode 100644 demo/executor/Cargo.toml delete mode 100644 demo/executor/src/lib.rs delete mode 100644 demo/network/Cargo.toml delete mode 100644 demo/network/src/consensus.rs delete mode 100644 demo/network/src/lib.rs delete mode 100644 demo/primitives/Cargo.toml delete mode 100644 demo/primitives/src/lib.rs delete mode 100644 demo/runtime/Cargo.toml delete mode 100644 demo/runtime/src/checked_block.rs delete mode 100644 demo/runtime/src/lib.rs delete mode 100644 demo/runtime/wasm/Cargo.lock delete mode 100644 demo/runtime/wasm/Cargo.toml delete mode 100755 demo/runtime/wasm/build.sh delete mode 120000 demo/runtime/wasm/src delete mode 100644 demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm delete mode 100755 demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm delete mode 100644 demo/service/Cargo.toml delete mode 100644 demo/service/src/chain_spec.rs delete mode 100644 demo/service/src/lib.rs delete mode 100644 demo/src/main.rs delete mode 100644 demo/transaction-pool/Cargo.toml delete mode 100644 demo/transaction-pool/src/error.rs delete mode 100644 demo/transaction-pool/src/lib.rs delete mode 100755 scripts/build-demos.sh delete mode 100644 substrate/bft/Cargo.toml delete mode 100644 substrate/bft/README.adoc delete mode 100644 substrate/bft/src/error.rs delete mode 100644 substrate/bft/src/lib.rs delete mode 100644 substrate/cli/Cargo.toml delete mode 100644 substrate/cli/README.adoc delete mode 100644 substrate/cli/build.rs delete mode 100644 substrate/cli/doc/shell-completion.adoc delete mode 100644 substrate/cli/src/cli.yml delete mode 100644 substrate/cli/src/error.rs delete mode 100644 substrate/cli/src/informant.rs delete mode 100644 substrate/cli/src/lib.rs delete mode 100644 substrate/cli/src/panic_hook.rs delete mode 100644 substrate/client/Cargo.toml delete mode 100644 substrate/client/README.adoc delete mode 100644 substrate/client/db/Cargo.toml delete mode 100644 substrate/client/db/src/cache.rs delete mode 100644 substrate/client/db/src/lib.rs delete mode 100644 substrate/client/db/src/light.rs delete mode 100644 substrate/client/db/src/utils.rs delete mode 100644 substrate/client/src/backend.rs delete mode 100644 substrate/client/src/block_builder.rs delete mode 100644 substrate/client/src/blockchain.rs delete mode 100644 substrate/client/src/call_executor.rs delete mode 100644 substrate/client/src/cht.rs delete mode 100644 substrate/client/src/client.rs delete mode 100644 substrate/client/src/error.rs delete mode 100644 substrate/client/src/in_mem.rs delete mode 100644 substrate/client/src/lib.rs delete mode 100644 substrate/client/src/light/backend.rs delete mode 100644 substrate/client/src/light/blockchain.rs delete mode 100644 substrate/client/src/light/call_executor.rs delete mode 100644 substrate/client/src/light/fetcher.rs delete mode 100644 substrate/client/src/light/mod.rs delete mode 100644 substrate/client/src/notifications.rs delete mode 100644 substrate/codec/Cargo.toml delete mode 100644 substrate/codec/README.adoc delete mode 100644 substrate/codec/derive/Cargo.toml delete mode 100644 substrate/codec/derive/src/decode.rs delete mode 100644 substrate/codec/derive/src/encode.rs delete mode 100644 substrate/codec/derive/src/lib.rs delete mode 100644 substrate/codec/derive/tests/mod.rs delete mode 100644 substrate/codec/src/codec.rs delete mode 100644 substrate/codec/src/joiner.rs delete mode 100644 substrate/codec/src/keyedvec.rs delete mode 100644 substrate/codec/src/lib.rs delete mode 100644 substrate/ed25519/Cargo.toml delete mode 100644 substrate/ed25519/README.adoc delete mode 100644 substrate/ed25519/src/lib.rs delete mode 100644 substrate/environmental/Cargo.toml delete mode 100644 substrate/environmental/README.adoc delete mode 100644 substrate/environmental/src/lib.rs delete mode 100644 substrate/environmental/with_std.rs delete mode 100644 substrate/environmental/without_std.rs delete mode 100644 substrate/executor/Cargo.toml delete mode 100644 substrate/executor/README.adoc delete mode 100644 substrate/executor/src/error.rs delete mode 100644 substrate/executor/src/lib.rs delete mode 100644 substrate/executor/src/native_executor.rs delete mode 100644 substrate/executor/src/sandbox.rs delete mode 100644 substrate/executor/src/wasm_executor.rs delete mode 100644 substrate/executor/src/wasm_utils.rs delete mode 100644 substrate/executor/wasm/Cargo.lock delete mode 100644 substrate/executor/wasm/Cargo.toml delete mode 100755 substrate/executor/wasm/build.sh delete mode 100644 substrate/executor/wasm/src/lib.rs delete mode 100644 substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm delete mode 100755 substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.wasm delete mode 100644 substrate/extrinsic-pool/Cargo.toml delete mode 100644 substrate/extrinsic-pool/README.adoc delete mode 100644 substrate/extrinsic-pool/src/error.rs delete mode 100644 substrate/extrinsic-pool/src/lib.rs delete mode 100644 substrate/extrinsic-pool/src/listener.rs delete mode 100644 substrate/extrinsic-pool/src/pool.rs delete mode 100644 substrate/extrinsic-pool/src/rotator.rs delete mode 100644 substrate/extrinsic-pool/src/watcher.rs delete mode 100644 substrate/keyring/Cargo.toml delete mode 100644 substrate/keyring/README.adoc delete mode 100644 substrate/keyring/src/lib.rs delete mode 100644 substrate/keystore/Cargo.toml delete mode 100644 substrate/keystore/README.adoc delete mode 100644 substrate/keystore/src/lib.rs delete mode 100644 substrate/misbehavior-check/Cargo.toml delete mode 100644 substrate/misbehavior-check/README.adoc delete mode 100644 substrate/misbehavior-check/src/lib.rs delete mode 100644 substrate/network-libp2p/Cargo.toml delete mode 100644 substrate/network-libp2p/README.adoc delete mode 100644 substrate/network-libp2p/src/connection_filter.rs delete mode 100644 substrate/network-libp2p/src/custom_proto.rs delete mode 100644 substrate/network-libp2p/src/error.rs delete mode 100644 substrate/network-libp2p/src/lib.rs delete mode 100644 substrate/network-libp2p/src/network_state.rs delete mode 100644 substrate/network-libp2p/src/service.rs delete mode 100644 substrate/network-libp2p/src/timeouts.rs delete mode 100644 substrate/network-libp2p/src/topology.rs delete mode 100644 substrate/network-libp2p/src/traits.rs delete mode 100644 substrate/network-libp2p/src/transport.rs delete mode 100644 substrate/network-libp2p/tests/tests.rs delete mode 100644 substrate/network/Cargo.toml delete mode 100644 substrate/network/README.adoc delete mode 100644 substrate/network/src/blocks.rs delete mode 100644 substrate/network/src/chain.rs delete mode 100644 substrate/network/src/config.rs delete mode 100644 substrate/network/src/consensus_gossip.rs delete mode 100644 substrate/network/src/error.rs delete mode 100644 substrate/network/src/import_queue.rs delete mode 100644 substrate/network/src/io.rs delete mode 100644 substrate/network/src/lib.rs delete mode 100644 substrate/network/src/message.rs delete mode 100644 substrate/network/src/on_demand.rs delete mode 100644 substrate/network/src/protocol.rs delete mode 100644 substrate/network/src/service.rs delete mode 100644 substrate/network/src/specialization.rs delete mode 100644 substrate/network/src/sync.rs delete mode 100644 substrate/network/src/test/mod.rs delete mode 100644 substrate/network/src/test/sync.rs delete mode 100644 substrate/primitives/Cargo.toml delete mode 100644 substrate/primitives/README.adoc delete mode 100644 substrate/primitives/src/authority_id.rs delete mode 100644 substrate/primitives/src/bytes.rs delete mode 100644 substrate/primitives/src/hash.rs delete mode 100644 substrate/primitives/src/hasher.rs delete mode 100644 substrate/primitives/src/hashing.rs delete mode 100644 substrate/primitives/src/hexdisplay.rs delete mode 100644 substrate/primitives/src/lib.rs delete mode 100644 substrate/primitives/src/rlp_codec.rs delete mode 100644 substrate/primitives/src/sandbox.rs delete mode 100644 substrate/primitives/src/storage.rs delete mode 100644 substrate/primitives/src/tests.rs delete mode 100644 substrate/primitives/src/u32_trait.rs delete mode 100644 substrate/primitives/src/uint.rs delete mode 100644 substrate/pwasm-alloc/Cargo.toml delete mode 100644 substrate/pwasm-alloc/README.adoc delete mode 100644 substrate/pwasm-alloc/build.rs delete mode 100644 substrate/pwasm-alloc/src/lib.rs delete mode 100644 substrate/pwasm-libc/Cargo.toml delete mode 100644 substrate/pwasm-libc/README.adoc delete mode 100644 substrate/pwasm-libc/src/lib.rs delete mode 100644 substrate/rpc-servers/Cargo.toml delete mode 100644 substrate/rpc-servers/README.adoc delete mode 100644 substrate/rpc-servers/src/lib.rs delete mode 100644 substrate/rpc/Cargo.toml delete mode 100644 substrate/rpc/README.adoc delete mode 100644 substrate/rpc/src/author/error.rs delete mode 100644 substrate/rpc/src/author/mod.rs delete mode 100644 substrate/rpc/src/author/tests.rs delete mode 100644 substrate/rpc/src/chain/error.rs delete mode 100644 substrate/rpc/src/chain/mod.rs delete mode 100644 substrate/rpc/src/chain/tests.rs delete mode 100644 substrate/rpc/src/errors.rs delete mode 100644 substrate/rpc/src/helpers.rs delete mode 100644 substrate/rpc/src/lib.rs delete mode 100644 substrate/rpc/src/metadata.rs delete mode 100644 substrate/rpc/src/state/error.rs delete mode 100644 substrate/rpc/src/state/mod.rs delete mode 100644 substrate/rpc/src/state/tests.rs delete mode 100644 substrate/rpc/src/subscriptions.rs delete mode 100644 substrate/rpc/src/system/error.rs delete mode 100644 substrate/rpc/src/system/mod.rs delete mode 100644 substrate/rpc/src/system/tests.rs delete mode 100644 substrate/runtime-io/Cargo.toml delete mode 100644 substrate/runtime-io/README.adoc delete mode 100644 substrate/runtime-io/build.rs delete mode 100644 substrate/runtime-io/src/lib.rs delete mode 100644 substrate/runtime-io/without_std.rs delete mode 100755 substrate/runtime-sandbox/Cargo.toml delete mode 100644 substrate/runtime-sandbox/README.adoc delete mode 100755 substrate/runtime-sandbox/build.rs delete mode 100755 substrate/runtime-sandbox/src/lib.rs delete mode 100755 substrate/runtime-sandbox/with_std.rs delete mode 100755 substrate/runtime-sandbox/without_std.rs delete mode 100644 substrate/runtime-std/Cargo.toml delete mode 100644 substrate/runtime-std/README.adoc delete mode 100644 substrate/runtime-std/build.rs delete mode 100644 substrate/runtime-std/src/lib.rs delete mode 100644 substrate/runtime-std/with_std.rs delete mode 100644 substrate/runtime-std/without_std.rs delete mode 100644 substrate/runtime-support/Cargo.toml delete mode 100644 substrate/runtime-support/README.adoc delete mode 100644 substrate/runtime-support/src/dispatch.rs delete mode 100644 substrate/runtime-support/src/event.rs delete mode 100644 substrate/runtime-support/src/hashable.rs delete mode 100644 substrate/runtime-support/src/lib.rs delete mode 100644 substrate/runtime-support/src/metadata.rs delete mode 100644 substrate/runtime-support/src/storage/generator.rs delete mode 100644 substrate/runtime-support/src/storage/mod.rs delete mode 100644 substrate/runtime/README.adoc delete mode 100644 substrate/runtime/balances/Cargo.toml delete mode 100644 substrate/runtime/balances/src/address.rs delete mode 100644 substrate/runtime/balances/src/genesis_config.rs delete mode 100644 substrate/runtime/balances/src/lib.rs delete mode 100644 substrate/runtime/balances/src/mock.rs delete mode 100644 substrate/runtime/balances/src/tests.rs delete mode 100644 substrate/runtime/consensus/Cargo.toml delete mode 100644 substrate/runtime/consensus/src/lib.rs delete mode 100644 substrate/runtime/contract/Cargo.toml delete mode 100644 substrate/runtime/contract/src/account_db.rs delete mode 100644 substrate/runtime/contract/src/double_map.rs delete mode 100644 substrate/runtime/contract/src/exec.rs delete mode 100644 substrate/runtime/contract/src/gas.rs delete mode 100644 substrate/runtime/contract/src/genesis_config.rs delete mode 100644 substrate/runtime/contract/src/lib.rs delete mode 100644 substrate/runtime/contract/src/tests.rs delete mode 100644 substrate/runtime/contract/src/vm/env_def/macros.rs delete mode 100644 substrate/runtime/contract/src/vm/env_def/mod.rs delete mode 100644 substrate/runtime/contract/src/vm/mod.rs delete mode 100644 substrate/runtime/contract/src/vm/prepare.rs delete mode 100644 substrate/runtime/council/Cargo.toml delete mode 100644 substrate/runtime/council/src/lib.rs delete mode 100644 substrate/runtime/council/src/motions.rs delete mode 100644 substrate/runtime/council/src/seats.rs delete mode 100644 substrate/runtime/council/src/voting.rs delete mode 100644 substrate/runtime/democracy/Cargo.toml delete mode 100644 substrate/runtime/democracy/src/lib.rs delete mode 100644 substrate/runtime/democracy/src/vote_threshold.rs delete mode 100644 substrate/runtime/example/Cargo.toml delete mode 100644 substrate/runtime/example/src/lib.rs delete mode 100644 substrate/runtime/executive/Cargo.toml delete mode 100644 substrate/runtime/executive/src/lib.rs delete mode 100644 substrate/runtime/primitives/Cargo.toml delete mode 100644 substrate/runtime/primitives/src/bft.rs delete mode 100644 substrate/runtime/primitives/src/generic/block.rs delete mode 100644 substrate/runtime/primitives/src/generic/checked_extrinsic.rs delete mode 100644 substrate/runtime/primitives/src/generic/digest.rs delete mode 100644 substrate/runtime/primitives/src/generic/header.rs delete mode 100644 substrate/runtime/primitives/src/generic/mod.rs delete mode 100644 substrate/runtime/primitives/src/generic/tests.rs delete mode 100644 substrate/runtime/primitives/src/generic/unchecked_extrinsic.rs delete mode 100644 substrate/runtime/primitives/src/lib.rs delete mode 100644 substrate/runtime/primitives/src/testing.rs delete mode 100644 substrate/runtime/primitives/src/traits.rs delete mode 100644 substrate/runtime/session/Cargo.toml delete mode 100644 substrate/runtime/session/src/lib.rs delete mode 100644 substrate/runtime/staking/Cargo.toml delete mode 100644 substrate/runtime/staking/src/genesis_config.rs delete mode 100644 substrate/runtime/staking/src/lib.rs delete mode 100644 substrate/runtime/staking/src/mock.rs delete mode 100644 substrate/runtime/staking/src/tests.rs delete mode 100644 substrate/runtime/system/Cargo.toml delete mode 100644 substrate/runtime/system/src/lib.rs delete mode 100644 substrate/runtime/timestamp/Cargo.toml delete mode 100644 substrate/runtime/timestamp/src/lib.rs delete mode 100644 substrate/runtime/treasury/Cargo.toml delete mode 100644 substrate/runtime/treasury/src/lib.rs delete mode 100644 substrate/runtime/version/Cargo.toml delete mode 100644 substrate/runtime/version/src/lib.rs delete mode 100644 substrate/serializer/Cargo.toml delete mode 100644 substrate/serializer/README.adoc delete mode 100644 substrate/serializer/src/lib.rs delete mode 100644 substrate/service/Cargo.toml delete mode 100644 substrate/service/README.adoc delete mode 100644 substrate/service/src/chain_ops.rs delete mode 100644 substrate/service/src/chain_spec.rs delete mode 100644 substrate/service/src/components.rs delete mode 100644 substrate/service/src/config.rs delete mode 100644 substrate/service/src/error.rs delete mode 100644 substrate/service/src/lib.rs delete mode 100644 substrate/state-db/Cargo.toml delete mode 100644 substrate/state-db/README.adoc delete mode 100644 substrate/state-db/src/lib.rs delete mode 100644 substrate/state-db/src/pruning.rs delete mode 100644 substrate/state-db/src/test.rs delete mode 100644 substrate/state-db/src/unfinalized.rs delete mode 100644 substrate/state-machine/Cargo.toml delete mode 100644 substrate/state-machine/README.adoc delete mode 100644 substrate/state-machine/src/backend.rs delete mode 100644 substrate/state-machine/src/ext.rs delete mode 100644 substrate/state-machine/src/lib.rs delete mode 100644 substrate/state-machine/src/proving_backend.rs delete mode 100644 substrate/state-machine/src/testing.rs delete mode 100644 substrate/state-machine/src/trie_backend.rs delete mode 100644 substrate/telemetry/Cargo.toml delete mode 100644 substrate/telemetry/README.adoc delete mode 100644 substrate/telemetry/src/lib.rs delete mode 100644 substrate/test-client/Cargo.toml delete mode 100644 substrate/test-client/README.adoc delete mode 100644 substrate/test-client/src/block_builder_ext.rs delete mode 100644 substrate/test-client/src/client_ext.rs delete mode 100644 substrate/test-client/src/lib.rs delete mode 100644 substrate/test-runtime/Cargo.toml delete mode 100644 substrate/test-runtime/README.adoc delete mode 100644 substrate/test-runtime/src/genesismap.rs delete mode 100644 substrate/test-runtime/src/lib.rs delete mode 100644 substrate/test-runtime/src/system.rs delete mode 100644 substrate/test-runtime/wasm/Cargo.lock delete mode 100644 substrate/test-runtime/wasm/Cargo.toml delete mode 100755 substrate/test-runtime/wasm/build.sh delete mode 120000 substrate/test-runtime/wasm/src delete mode 100644 substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm delete mode 100755 substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.wasm diff --git a/.gitignore b/.gitignore index 2e0651ba435d1..8058ae508f8eb 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,7 @@ substrate/pwasm-alloc/target/ substrate/pwasm-libc/target/ substrate/pwasm-alloc/Cargo.lock substrate/pwasm-libc/Cargo.lock -demo/runtime/wasm/target/ +node/runtime/wasm/target/ **/._* .vscode polkadot.* diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f4d781880bffb..cc9e3985e62d0 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -50,7 +50,6 @@ test:rust:stable: &test - ./scripts/init.sh - export PATH="${CI_PROJECT_DIR}/cargo/bin/:$PATH" - ./scripts/build.sh - - ./scripts/build-demos.sh - time cargo test --all --release tags: - rust-stable @@ -74,7 +73,6 @@ build:linux:ubuntu:amd64: &build - ./scripts/init.sh - export PATH="${CI_PROJECT_DIR}/cargo/bin/:$PATH" - ./scripts/build.sh - - ./scripts/build-demos.sh - cargo build --release <<: *collect_artifacts tags: diff --git a/Cargo.lock b/Cargo.lock index 14ab7f229bb92..622086c6beeee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -402,189 +402,6 @@ dependencies = [ "tempfile 3.0.3 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "demo-api" -version = "0.1.0" -dependencies = [ - "demo-primitives 0.1.0", - "demo-runtime 0.1.0", - "substrate-client 0.1.0", - "substrate-keyring 0.1.0", - "substrate-primitives 0.1.0", -] - -[[package]] -name = "demo-cli" -version = "0.1.0" -dependencies = [ - "demo-service 0.1.0", - "exit-future 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-cli 0.3.0", - "tokio 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "demo-consensus" -version = "0.1.0" -dependencies = [ - "demo-api 0.1.0", - "demo-primitives 0.1.0", - "demo-runtime 0.1.0", - "demo-transaction-pool 0.1.0", - "ed25519 0.1.0", - "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "exit-future 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rhododendron 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-bft 0.1.0", - "substrate-client 0.1.0", - "substrate-codec 0.1.0", - "substrate-keyring 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-primitives 0.1.0", - "substrate-runtime-support 0.1.0", - "tokio 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "demo-executor" -version = "0.1.0" -dependencies = [ - "demo-primitives 0.1.0", - "demo-runtime 0.1.0", - "ed25519 0.1.0", - "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-executor 0.1.0", - "substrate-keyring 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-balances 0.1.0", - "substrate-runtime-consensus 0.1.0", - "substrate-runtime-io 0.1.0", - "substrate-runtime-primitives 0.1.0", - "substrate-runtime-session 0.1.0", - "substrate-runtime-staking 0.1.0", - "substrate-runtime-support 0.1.0", - "substrate-runtime-system 0.1.0", - "substrate-runtime-timestamp 0.1.0", - "substrate-runtime-treasury 0.1.0", - "substrate-state-machine 0.1.0", - "triehash 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "demo-network" -version = "0.1.0" -dependencies = [ - "demo-api 0.1.0", - "demo-consensus 0.1.0", - "demo-primitives 0.1.0", - "ed25519 0.1.0", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rhododendron 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-bft 0.1.0", - "substrate-network 0.1.0", - "tokio 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "demo-primitives" -version = "0.1.0" -dependencies = [ - "pretty_assertions 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-codec-derive 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-primitives 0.1.0", - "substrate-runtime-std 0.1.0", - "substrate-serializer 0.1.0", -] - -[[package]] -name = "demo-runtime" -version = "0.1.0" -dependencies = [ - "demo-primitives 0.1.0", - "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 1.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.70 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-codec-derive 0.1.0", - "substrate-keyring 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-balances 0.1.0", - "substrate-runtime-consensus 0.1.0", - "substrate-runtime-contract 0.1.0", - "substrate-runtime-council 0.1.0", - "substrate-runtime-democracy 0.1.0", - "substrate-runtime-executive 0.1.0", - "substrate-runtime-io 0.1.0", - "substrate-runtime-primitives 0.1.0", - "substrate-runtime-session 0.1.0", - "substrate-runtime-staking 0.1.0", - "substrate-runtime-std 0.1.0", - "substrate-runtime-support 0.1.0", - "substrate-runtime-system 0.1.0", - "substrate-runtime-timestamp 0.1.0", - "substrate-runtime-treasury 0.1.0", - "substrate-runtime-version 0.1.0", -] - -[[package]] -name = "demo-service" -version = "0.1.0" -dependencies = [ - "demo-api 0.1.0", - "demo-consensus 0.1.0", - "demo-executor 0.1.0", - "demo-network 0.1.0", - "demo-primitives 0.1.0", - "demo-runtime 0.1.0", - "demo-transaction-pool 0.1.0", - "ed25519 0.1.0", - "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "slog 2.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-client 0.1.0", - "substrate-network 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-io 0.1.0", - "substrate-service 0.3.0", - "substrate-telemetry 0.3.0", - "tokio 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "demo-transaction-pool" -version = "0.1.0" -dependencies = [ - "demo-api 0.1.0", - "demo-primitives 0.1.0", - "demo-runtime 0.1.0", - "ed25519 0.1.0", - "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-client 0.1.0", - "substrate-codec 0.1.0", - "substrate-extrinsic-pool 0.1.0", - "substrate-keyring 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-primitives 0.1.0", -] - [[package]] name = "difference" version = "1.0.0" @@ -608,18 +425,6 @@ name = "dtoa" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "ed25519" -version = "0.1.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)", - "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-primitives 0.1.0", - "untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "edit-distance" version = "2.0.1" @@ -1693,6 +1498,185 @@ dependencies = [ "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "node-api" +version = "0.1.0" +dependencies = [ + "node-primitives 0.1.0", + "node-runtime 0.1.0", + "substrate-client 0.1.0", + "substrate-keyring 0.1.0", + "substrate-primitives 0.1.0", +] + +[[package]] +name = "node-cli" +version = "0.1.0" +dependencies = [ + "exit-future 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "node-service 0.1.0", + "substrate-cli 0.3.0", + "tokio 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "node-consensus" +version = "0.1.0" +dependencies = [ + "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "exit-future 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "node-api 0.1.0", + "node-primitives 0.1.0", + "node-runtime 0.1.0", + "node-transaction-pool 0.1.0", + "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "rhododendron 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-bft 0.1.0", + "substrate-client 0.1.0", + "substrate-codec 0.1.0", + "substrate-keyring 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-primitives 0.1.0", + "substrate-runtime-support 0.1.0", + "tokio 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "node-executor" +version = "0.1.0" +dependencies = [ + "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "node-primitives 0.1.0", + "node-runtime 0.1.0", + "substrate-codec 0.1.0", + "substrate-executor 0.1.0", + "substrate-keyring 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-balances 0.1.0", + "substrate-runtime-consensus 0.1.0", + "substrate-runtime-io 0.1.0", + "substrate-runtime-primitives 0.1.0", + "substrate-runtime-session 0.1.0", + "substrate-runtime-staking 0.1.0", + "substrate-runtime-support 0.1.0", + "substrate-runtime-system 0.1.0", + "substrate-runtime-timestamp 0.1.0", + "substrate-runtime-treasury 0.1.0", + "substrate-state-machine 0.1.0", + "triehash 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "node-network" +version = "0.1.0" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "node-api 0.1.0", + "node-consensus 0.1.0", + "node-primitives 0.1.0", + "rhododendron 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-bft 0.1.0", + "substrate-network 0.1.0", + "substrate-primitives 0.1.0", + "tokio 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "node-primitives" +version = "0.1.0" +dependencies = [ + "pretty_assertions 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-codec-derive 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-primitives 0.1.0", + "substrate-runtime-std 0.1.0", + "substrate-serializer 0.1.0", +] + +[[package]] +name = "node-runtime" +version = "0.1.0" +dependencies = [ + "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "node-primitives 0.1.0", + "rustc-hex 1.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.70 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-codec-derive 0.1.0", + "substrate-keyring 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-balances 0.1.0", + "substrate-runtime-consensus 0.1.0", + "substrate-runtime-contract 0.1.0", + "substrate-runtime-council 0.1.0", + "substrate-runtime-democracy 0.1.0", + "substrate-runtime-executive 0.1.0", + "substrate-runtime-io 0.1.0", + "substrate-runtime-primitives 0.1.0", + "substrate-runtime-session 0.1.0", + "substrate-runtime-staking 0.1.0", + "substrate-runtime-std 0.1.0", + "substrate-runtime-support 0.1.0", + "substrate-runtime-system 0.1.0", + "substrate-runtime-timestamp 0.1.0", + "substrate-runtime-treasury 0.1.0", + "substrate-runtime-version 0.1.0", +] + +[[package]] +name = "node-service" +version = "0.1.0" +dependencies = [ + "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "node-api 0.1.0", + "node-consensus 0.1.0", + "node-executor 0.1.0", + "node-network 0.1.0", + "node-primitives 0.1.0", + "node-runtime 0.1.0", + "node-transaction-pool 0.1.0", + "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "slog 2.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-client 0.1.0", + "substrate-network 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-io 0.1.0", + "substrate-service 0.3.0", + "substrate-telemetry 0.3.0", + "tokio 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "node-transaction-pool" +version = "0.1.0" +dependencies = [ + "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "node-api 0.1.0", + "node-primitives 0.1.0", + "node-runtime 0.1.0", + "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-client 0.1.0", + "substrate-codec 0.1.0", + "substrate-extrinsic-pool 0.1.0", + "substrate-keyring 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-primitives 0.1.0", +] + [[package]] name = "nodrop" version = "0.1.12" @@ -2418,7 +2402,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "subkey" version = "0.1.0" dependencies = [ - "ed25519 0.1.0", "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "substrate-primitives 0.1.0", ] @@ -2428,9 +2411,9 @@ name = "substrate" version = "0.1.0" dependencies = [ "ctrlc 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "demo-cli 0.1.0", "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "node-cli 0.1.0", "vergen 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2438,7 +2421,6 @@ dependencies = [ name = "substrate-bft" version = "0.1.0" dependencies = [ - "ed25519 0.1.0", "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2488,7 +2470,6 @@ dependencies = [ name = "substrate-client" version = "0.1.0" dependencies = [ - "ed25519 0.1.0", "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2559,7 +2540,6 @@ version = "0.1.0" dependencies = [ "assert_matches 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ed25519 0.1.0", "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "hashdb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2601,16 +2581,15 @@ dependencies = [ name = "substrate-keyring" version = "0.1.0" dependencies = [ - "ed25519 0.1.0", "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-primitives 0.1.0", ] [[package]] name = "substrate-keystore" version = "0.1.0" dependencies = [ - "ed25519 0.1.0", "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "parity-crypto 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2641,7 +2620,6 @@ name = "substrate-network" version = "0.1.0" dependencies = [ "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ed25519 0.1.0", "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore-io 1.12.0 (git+https://github.com/paritytech/parity.git)", @@ -2692,6 +2670,7 @@ dependencies = [ name = "substrate-primitives" version = "0.1.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.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2699,9 +2678,11 @@ dependencies = [ "fixed-hash 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "hashdb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "patricia-trie 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "plain_hasher 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "pretty_assertions 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ring 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2712,6 +2693,7 @@ dependencies = [ "substrate-serializer 0.1.0", "twox-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "uint 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "wasmi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2894,7 +2876,6 @@ dependencies = [ name = "substrate-runtime-io" version = "0.1.0" dependencies = [ - "ed25519 0.1.0", "environmental 0.1.0", "hashdb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2996,7 +2977,6 @@ dependencies = [ name = "substrate-runtime-support" version = "0.1.0" dependencies = [ - "ed25519 0.1.0", "hex-literal 0.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.70 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3177,7 +3157,6 @@ dependencies = [ name = "substrate-test-runtime" version = "0.1.0" dependencies = [ - "ed25519 0.1.0", "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index a0550303dcccd..a833d41030b6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,63 +1,63 @@ [workspace] members = [ - "substrate/bft", - "substrate/cli", - "substrate/client", - "substrate/client/db", - "substrate/codec", - "substrate/codec/derive", - "substrate/environmental", - "substrate/executor", - "substrate/extrinsic-pool", - "substrate/keyring", - "substrate/misbehavior-check", - "substrate/network", - "substrate/primitives", - "substrate/rpc", - "substrate/rpc-servers", - "substrate/runtime-io", - "substrate/runtime-sandbox", - "substrate/runtime-std", - "substrate/runtime-support", - "substrate/runtime/balances", - "substrate/runtime/consensus", - "substrate/runtime/contract", - "substrate/runtime/council", - "substrate/runtime/democracy", - "substrate/runtime/example", - "substrate/runtime/executive", - "substrate/runtime/primitives", - "substrate/runtime/session", - "substrate/runtime/staking", - "substrate/runtime/system", - "substrate/runtime/timestamp", - "substrate/runtime/treasury", - "substrate/runtime/version", - "substrate/serializer", - "substrate/service", - "substrate/state-db", - "substrate/state-machine", - "substrate/test-runtime", - "substrate/telemetry", - "substrate/keystore", - "demo", - "demo/cli", - "demo/api", - "demo/consensus", - "demo/executor", - "demo/network", - "demo/primitives", - "demo/runtime", - "demo/service", - "demo/transaction-pool", + "core/bft", + "core/cli", + "core/client", + "core/client/db", + "core/codec", + "core/codec/derive", + "core/environmental", + "core/executor", + "core/extrinsic-pool", + "core/keyring", + "core/misbehavior-check", + "core/network", + "core/primitives", + "core/rpc", + "core/rpc-servers", + "core/runtime-io", + "core/runtime-sandbox", + "core/runtime-std", + "core/serializer", + "core/service", + "core/state-db", + "core/state-machine", + "core/test-runtime", + "core/telemetry", + "core/keystore", + "runtime/support", + "runtime/primitives", + "runtime/version", + "runtime/balances", + "runtime/consensus", + "runtime/contract", + "runtime/council", + "runtime/democracy", + "runtime/example", + "runtime/executive", + "runtime/session", + "runtime/staking", + "runtime/system", + "runtime/timestamp", + "runtime/treasury", + "node", + "node/cli", + "node/api", + "node/consensus", + "node/executor", + "node/network", + "node/primitives", + "node/runtime", + "node/service", + "node/transaction-pool", "subkey", ] exclude = [ - "demo/runtime/wasm", - "substrate/executor/wasm", - "substrate/pwasm-alloc", - "substrate/pwasm-libc", - "substrate/test-runtime/wasm", + "node/runtime/wasm", + "core/executor/wasm", + "core/pwasm-alloc", + "core/pwasm-libc", + "core/test-runtime/wasm", ] [badges] diff --git a/README.adoc b/README.adoc index f1502ca3313fc..a6d7ab30c643a 100644 --- a/README.adoc +++ b/README.adoc @@ -145,7 +145,6 @@ Then build the code: [source, shell] ---- ./scripts/build.sh # Builds the WebAssembly binaries -./scripts/build-demos.sh # Builds the WebAssembly binaries cargo build # Builds all native code ---- diff --git a/ci/script.sh b/ci/script.sh index 812da2d816805..7eee447bb06cb 100755 --- a/ci/script.sh +++ b/ci/script.sh @@ -24,6 +24,5 @@ case $TARGET in # Install prerequisites and build all wasm projects ./scripts/init.sh ./scripts/build.sh - ./scripts/build-demos.sh ;; esac diff --git a/demo/Cargo.toml b/demo/Cargo.toml deleted file mode 100644 index 326844dc9002f..0000000000000 --- a/demo/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[[bin]] -name = "substrate" -path = "src/main.rs" - -[package] -name = "substrate" -version = "0.1.0" -authors = ["Parity Technologies "] -build = "build.rs" - -[dependencies] -error-chain = "0.12" -demo-cli = { path = "cli" } -futures = "0.1" -ctrlc = { version = "3.0", features = ["termination"] } - -[build-dependencies] -vergen = "0.1" diff --git a/demo/api/Cargo.toml b/demo/api/Cargo.toml deleted file mode 100644 index b03eb27a72541..0000000000000 --- a/demo/api/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "demo-api" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -demo-runtime = { path = "../runtime" } -demo-primitives = { path = "../primitives" } -substrate-client = { path = "../../substrate/client" } -substrate-primitives = { path = "../../substrate/primitives" } - -[dev-dependencies] -substrate-keyring = { path = "../../substrate/keyring" } diff --git a/demo/api/src/lib.rs b/demo/api/src/lib.rs deleted file mode 100644 index f83a6d228d3c9..0000000000000 --- a/demo/api/src/lib.rs +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. - -// Substrate Demo 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 Demo 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 Demo. If not, see . - -//! Strongly typed API for Substrate Demo runtime. - -#![warn(missing_docs)] -#![warn(unused_extern_crates)] - -extern crate demo_primitives as primitives; -extern crate demo_runtime as runtime; -extern crate substrate_client as client; -extern crate substrate_primitives; - -pub use client::error::{Error, ErrorKind, Result}; -use runtime::Address; -use client::backend::Backend; -use client::block_builder::BlockBuilder as ClientBlockBuilder; -use client::{Client, CallExecutor}; -use primitives::{ - AccountId, Block, BlockId, Hash, Index, InherentData, - SessionKey, Timestamp, UncheckedExtrinsic, -}; -use substrate_primitives::{Blake2Hasher, RlpCodec}; - -/// Build new blocks. -pub trait BlockBuilder { - /// Push an extrinsic onto the block. Fails if the extrinsic is invalid. - fn push_extrinsic(&mut self, extrinsic: UncheckedExtrinsic) -> Result<()>; - - /// Bake the block with provided extrinsics. - fn bake(self) -> Result; -} - -/// Trait encapsulating the demo API. -/// -/// All calls should fail when the exact runtime is unknown. -pub trait Api { - /// The block builder for this API type. - type BlockBuilder: BlockBuilder; - - /// Get session keys at a given block. - fn session_keys(&self, at: &BlockId) -> Result>; - - /// Get validators at a given block. - fn validators(&self, at: &BlockId) -> Result>; - - /// Get the value of the randomness beacon at a given block. - fn random_seed(&self, at: &BlockId) -> Result; - - /// Get the timestamp registered at a block. - fn timestamp(&self, at: &BlockId) -> Result; - - /// Get the nonce (né index) of an account at a block. - fn index(&self, at: &BlockId, account: AccountId) -> Result; - - /// Get the account id of an address at a block. - fn lookup(&self, at: &BlockId, address: Address) -> Result>; - - /// Evaluate a block. Returns true if the block is good, false if it is known to be bad, - /// and an error if we can't evaluate for some reason. - fn evaluate_block(&self, at: &BlockId, block: Block) -> Result; - - /// Build a block on top of the given, with inherent extrinsics pre-pushed. - fn build_block(&self, at: &BlockId, inherent_data: InherentData) -> Result; - - /// Attempt to produce the (encoded) inherent extrinsics for a block being built upon the given. - /// This may vary by runtime and will fail if a runtime doesn't follow the same API. - fn inherent_extrinsics(&self, at: &BlockId, inherent_data: InherentData) -> Result>; -} - -impl BlockBuilder for ClientBlockBuilder -where - B: Backend, - E: CallExecutor+ Clone, -{ - fn push_extrinsic(&mut self, extrinsic: UncheckedExtrinsic) -> Result<()> { - self.push(extrinsic).map_err(Into::into) - } - - /// Bake the block with provided extrinsics. - fn bake(self) -> Result { - ClientBlockBuilder::bake(self).map_err(Into::into) - } -} - -impl Api for Client -where - B: Backend, - E: CallExecutor + Clone, -{ - type BlockBuilder = ClientBlockBuilder; - - fn session_keys(&self, at: &BlockId) -> Result> { - Ok(self.authorities_at(at)?) - } - - fn validators(&self, at: &BlockId) -> Result> { - self.call_api(at, "validators", &()) - } - - fn random_seed(&self, at: &BlockId) -> Result { - self.call_api(at, "random_seed", &()) - } - - fn timestamp(&self, at: &BlockId) -> Result { - self.call_api(at, "timestamp", &()) - } - - fn evaluate_block(&self, at: &BlockId, block: Block) -> Result { - let res: Result<()> = self.call_api(at, "execute_block", &block); - match res { - Ok(()) => Ok(true), - Err(err) => match err.kind() { - &client::error::ErrorKind::Execution(_) => Ok(false), - _ => Err(err) - } - } - } - - fn index(&self, at: &BlockId, account: AccountId) -> Result { - self.call_api(at, "account_nonce", &account) - } - - fn lookup(&self, at: &BlockId, address: Address) -> Result> { - self.call_api(at, "lookup_address", &address) - } - - fn build_block(&self, at: &BlockId, inherent_data: InherentData) -> Result { - let mut block_builder = self.new_block_at(at)?; - for inherent in self.inherent_extrinsics(at, inherent_data)? { - block_builder.push(inherent)?; - } - - Ok(block_builder) - } - - fn inherent_extrinsics(&self, at: &BlockId, inherent_data: InherentData) -> Result> { - let runtime_version = self.runtime_version_at(at)?; - self.call_api(at, "inherent_extrinsics", &(inherent_data, runtime_version.spec_version)) - } -} - diff --git a/demo/build.rs b/demo/build.rs deleted file mode 100644 index 0f2f3b9bdcaf8..0000000000000 --- a/demo/build.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2015-2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. - -// Substrate Demo 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 Demo 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 Demo. If not, see . - -extern crate vergen; - -const ERROR_MSG: &'static str = "Failed to generate metadata files"; - -fn main() { - vergen::vergen(vergen::SHORT_SHA).expect(ERROR_MSG); - println!("cargo:rerun-if-changed=../../.git/HEAD"); -} diff --git a/demo/cli/Cargo.toml b/demo/cli/Cargo.toml deleted file mode 100644 index 5c5b0f7a36f41..0000000000000 --- a/demo/cli/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "demo-cli" -version = "0.1.0" -authors = ["Parity Technologies "] -description = "Substrate Demo node implementation in Rust." - -[dependencies] -log = "0.3" -tokio = "0.1.7" -exit-future = "0.1" -substrate-cli = { path = "../../substrate/cli" } -demo-service = { path = "../service" } diff --git a/demo/cli/src/cli.yml b/demo/cli/src/cli.yml deleted file mode 100644 index 263af9d9ef300..0000000000000 --- a/demo/cli/src/cli.yml +++ /dev/null @@ -1,12 +0,0 @@ -name: substrate-demo -author: "Parity Team " -about: Substrate Demo Node Rust Implementation -args: - - log: - short: l - value_name: LOG_PATTERN - help: Sets a custom logging - takes_value: true -subcommands: - - validator: - about: Run validator node diff --git a/demo/cli/src/error.rs b/demo/cli/src/error.rs deleted file mode 100644 index c8b4fdedde099..0000000000000 --- a/demo/cli/src/error.rs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. - -// Substrate Demo 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 Demo 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 Demo. If not, see . - -//! Initialization errors. - -use client; - -error_chain! { - foreign_links { - Io(::std::io::Error) #[doc="IO error"]; - Cli(::clap::Error) #[doc="CLI error"]; - } - links { - Client(client::error::Error, client::error::ErrorKind) #[doc="Client error"]; - } -} diff --git a/demo/cli/src/lib.rs b/demo/cli/src/lib.rs deleted file mode 100644 index 09c09e1cc6ccb..0000000000000 --- a/demo/cli/src/lib.rs +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. - -// Substrate Demo 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 Demo 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 Demo. If not, see . - -//! Substrate Demo CLI library. - -#![warn(missing_docs)] -#![warn(unused_extern_crates)] - -extern crate tokio; - -extern crate substrate_cli as cli; -extern crate demo_service as service; -extern crate exit_future; - -#[macro_use] -extern crate log; - -pub use cli::error; - -use tokio::runtime::Runtime; -pub use service::{Components as ServiceComponents, Service, CustomConfiguration}; -pub use cli::{VersionInfo, IntoExit}; - -/// The chain specification option. -#[derive(Clone, Debug)] -pub enum ChainSpec { - /// Whatever the current runtime is, with just Alice as an auth. - Development, - /// Whatever the current runtime is, with simple Alice/Bob auths. - LocalTestnet, - /// The PoC-1 & PoC-2 era testnet. - Testnet, - /// Whatever the current runtime is with the "global testnet" defaults. - StagingTestnet, -} - -/// Get a chain config from a spec setting. -impl ChainSpec { - pub(crate) fn load(self) -> Result { - Ok(match self { - ChainSpec::Testnet => service::chain_spec::testnet_config()?, - ChainSpec::Development => service::chain_spec::development_config(), - ChainSpec::LocalTestnet => service::chain_spec::local_testnet_config(), - ChainSpec::StagingTestnet => service::chain_spec::staging_testnet_config(), - }) - } - - pub(crate) fn from(s: &str) -> Option { - match s { - "dev" => Some(ChainSpec::Development), - "local" => Some(ChainSpec::LocalTestnet), - "" | "test" => Some(ChainSpec::Testnet), - "staging" => Some(ChainSpec::StagingTestnet), - _ => None, - } - } -} - -fn load_spec(id: &str) -> Result, String> { - Ok(match ChainSpec::from(id) { - Some(spec) => Some(spec.load()?), - None => None, - }) -} - -/// Parse command line arguments into service configuration. -pub fn run(args: I, exit: E, version: cli::VersionInfo) -> error::Result<()> where - I: IntoIterator, - T: Into + Clone, - E: IntoExit, -{ - match cli::prepare_execution::(args, exit, version, load_spec, "substrate-demo")? { - cli::Action::ExecutedInternally => (), - cli::Action::RunService((config, exit)) => { - info!("Parity ·:· Substrate"); - info!(" version {}", config.full_version()); - info!(" by Parity Technologies, 2017, 2018"); - info!("Chain specification: {}", config.chain_spec.name()); - info!("Node name: {}", config.name); - info!("Roles: {:?}", config.roles); - let mut runtime = Runtime::new()?; - let executor = runtime.executor(); - match config.roles == service::Roles::LIGHT { - true => run_until_exit(&mut runtime, service::new_light(config, executor)?, exit)?, - false => run_until_exit(&mut runtime, service::new_full(config, executor)?, exit)?, - } - } - } - Ok(()) -} - -fn run_until_exit( - runtime: &mut Runtime, - service: service::Service, - e: E, -) -> error::Result<()> - where - C: service::Components, - E: IntoExit, -{ - let (exit_send, exit) = exit_future::signal(); - - let executor = runtime.executor(); - cli::informant::start(&service, exit.clone(), executor.clone()); - - let _ = runtime.block_on(e.into_exit()); - exit_send.fire(); - Ok(()) -} diff --git a/demo/consensus/Cargo.toml b/demo/consensus/Cargo.toml deleted file mode 100644 index 1dc8eb8367711..0000000000000 --- a/demo/consensus/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "demo-consensus" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -futures = "0.1.17" -parking_lot = "0.4" -tokio = "0.1.7" -ed25519 = { path = "../../substrate/ed25519" } -error-chain = "0.12" -log = "0.3" -exit-future = "0.1" -rhododendron = "0.3" -demo-api = { path = "../api" } -demo-primitives = { path = "../primitives" } -demo-runtime = { path = "../runtime" } -demo-transaction-pool = { path = "../transaction-pool" } -substrate-bft = { path = "../../substrate/bft" } -substrate-codec = { path = "../../substrate/codec" } -substrate-primitives = { path = "../../substrate/primitives" } -substrate-runtime-support = { path = "../../substrate/runtime-support" } -substrate-client = { path = "../../substrate/client" } -substrate-runtime-primitives = { path = "../../substrate/runtime/primitives" } - -[dev-dependencies] -substrate-keyring = { path = "../../substrate/keyring" } diff --git a/demo/consensus/README.adoc b/demo/consensus/README.adoc deleted file mode 100644 index a3ac5f631c38c..0000000000000 --- a/demo/consensus/README.adoc +++ /dev/null @@ -1,5 +0,0 @@ - -= Polkadot Consensus - -placeholder -//TODO Write content :) diff --git a/demo/consensus/src/error.rs b/demo/consensus/src/error.rs deleted file mode 100644 index e8b60c847c34d..0000000000000 --- a/demo/consensus/src/error.rs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. - -// Substrate Demo 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 Demo 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 Demo. If not, see . - -//! Errors that can occur during the consensus process. - -use primitives::AuthorityId; - -error_chain! { - links { - Api(::demo_api::Error, ::demo_api::ErrorKind); - Bft(::bft::Error, ::bft::ErrorKind); - } - - errors { - NotValidator(id: AuthorityId) { - description("Local account ID not a validator at this block."), - display("Local account ID ({:?}) not a validator at this block.", id), - } - PrematureDestruction { - description("Proposer destroyed before finishing proposing or evaluating"), - display("Proposer destroyed before finishing proposing or evaluating"), - } - Timer(e: ::tokio::timer::Error) { - description("Failed to register or resolve async timer."), - display("Timer failed: {}", e), - } - Executor(e: ::futures::future::ExecuteErrorKind) { - description("Unable to dispatch agreement future"), - display("Unable to dispatch agreement future: {:?}", e), - } - } -} - -impl From<::bft::InputStreamConcluded> for Error { - fn from(err: ::bft::InputStreamConcluded) -> Self { - ::bft::Error::from(err).into() - } -} diff --git a/demo/consensus/src/evaluation.rs b/demo/consensus/src/evaluation.rs deleted file mode 100644 index 0d073aa52109c..0000000000000 --- a/demo/consensus/src/evaluation.rs +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. - -// Substrate Demo 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 Demo 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 Demo. If not, see . - -//! Block evaluation and evaluation errors. - -use super::MAX_TRANSACTIONS_SIZE; - -use codec::{Decode, Encode}; -use demo_runtime::{Block as GenericBlock, CheckedBlock}; -use demo_primitives::{Block, Hash, BlockNumber, Timestamp}; - -error_chain! { - links { - Api(::demo_api::Error, ::demo_api::ErrorKind); - } - - errors { - BadProposalFormat { - description("Proposal provided not a block."), - display("Proposal provided not a block."), - } - TimestampInFuture { - description("Proposal had timestamp too far in the future."), - display("Proposal had timestamp too far in the future."), - } - WrongParentHash(expected: Hash, got: Hash) { - description("Proposal had wrong parent hash."), - display("Proposal had wrong parent hash. Expected {:?}, got {:?}", expected, got), - } - WrongNumber(expected: BlockNumber, got: BlockNumber) { - description("Proposal had wrong number."), - display("Proposal had wrong number. Expected {:?}, got {:?}", expected, got), - } - ProposalTooLarge(size: usize) { - description("Proposal exceeded the maximum size."), - display( - "Proposal exceeded the maximum size of {} by {} bytes.", - MAX_TRANSACTIONS_SIZE, size.saturating_sub(MAX_TRANSACTIONS_SIZE) - ), - } - } -} - -/// Attempt to evaluate a substrate block as a demo block, returning error -/// upon any initial validity checks failing. -pub fn evaluate_initial( - proposal: &Block, - now: Timestamp, - parent_hash: &Hash, - parent_number: BlockNumber, -) -> Result { - const MAX_TIMESTAMP_DRIFT: Timestamp = 60; - - let encoded = Encode::encode(proposal); - let proposal = GenericBlock::decode(&mut &encoded[..]) - .and_then(|b| CheckedBlock::new(b).ok()) - .ok_or_else(|| ErrorKind::BadProposalFormat)?; - - let transactions_size = proposal.extrinsics.iter().fold(0, |a, tx| { - a + Encode::encode(tx).len() - }); - - if transactions_size > MAX_TRANSACTIONS_SIZE { - bail!(ErrorKind::ProposalTooLarge(transactions_size)) - } - - if proposal.header.parent_hash != *parent_hash { - bail!(ErrorKind::WrongParentHash(*parent_hash, proposal.header.parent_hash)); - } - - if proposal.header.number != parent_number + 1 { - bail!(ErrorKind::WrongNumber(parent_number + 1, proposal.header.number)); - } - - let block_timestamp = proposal.timestamp(); - - // lenient maximum -- small drifts will just be delayed using a timer. - if block_timestamp > now + MAX_TIMESTAMP_DRIFT { - bail!(ErrorKind::TimestampInFuture) - } - - Ok(proposal) -} diff --git a/demo/consensus/src/lib.rs b/demo/consensus/src/lib.rs deleted file mode 100644 index 7f55bc1e68fef..0000000000000 --- a/demo/consensus/src/lib.rs +++ /dev/null @@ -1,446 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. - -// Substrate Demo 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 Demo 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 Demo. If not, see . - -//! This service uses BFT consensus provided by the substrate. - -extern crate ed25519; -extern crate parking_lot; -extern crate demo_api; -extern crate demo_transaction_pool as transaction_pool; -extern crate demo_runtime; -extern crate demo_primitives; - -extern crate substrate_bft as bft; -extern crate substrate_codec as codec; -extern crate substrate_primitives as primitives; -extern crate substrate_runtime_support as runtime_support; -extern crate substrate_runtime_primitives as runtime_primitives; -extern crate substrate_client as client; - -extern crate exit_future; -extern crate tokio; -extern crate rhododendron; - -#[macro_use] -extern crate error_chain; -extern crate futures; - -#[macro_use] -extern crate log; - -#[cfg(test)] -extern crate substrate_keyring; - -use std::sync::Arc; -use std::time::{self, Duration, Instant}; - -use codec::{Decode, Encode}; -use demo_api::Api; -use demo_primitives::{AccountId, Hash, Block, BlockId, BlockNumber, Header, Timestamp, SessionKey}; -use primitives::AuthorityId; -use transaction_pool::TransactionPool; -use tokio::runtime::TaskExecutor; -use tokio::timer::Delay; - -use futures::prelude::*; -use futures::future; -use parking_lot::RwLock; - -pub use self::error::{ErrorKind, Error}; -pub use self::offline_tracker::OfflineTracker; -pub use service::Service; - -mod evaluation; -mod error; -mod offline_tracker; -mod service; - -/// Shared offline validator tracker. -pub type SharedOfflineTracker = Arc>; - -// block size limit. -const MAX_TRANSACTIONS_SIZE: usize = 4 * 1024 * 1024; - -/// A long-lived network which can create BFT message routing processes on demand. -pub trait Network { - /// The input stream of BFT messages. Should never logically conclude. - type Input: Stream,Error=Error>; - /// The output sink of BFT messages. Messages sent here should eventually pass to all - /// current authorities. - type Output: Sink,SinkError=Error>; - - /// Instantiate input and output streams. - fn communication_for( - &self, - validators: &[SessionKey], - local_id: SessionKey, - parent_hash: Hash, - task_executor: TaskExecutor - ) -> (Self::Input, Self::Output); -} - -/// Proposer factory. -pub struct ProposerFactory - where - P: Api + Send + Sync + 'static -{ - /// The client instance. - pub client: Arc

, - /// The transaction pool. - pub transaction_pool: Arc>, - /// The backing network handle. - pub network: N, - /// handle to remote task executor - pub handle: TaskExecutor, - /// Offline-tracker. - pub offline: SharedOfflineTracker, -} - -impl bft::Environment for ProposerFactory - where - N: Network, - P: Api + Send + Sync + 'static, -{ - type Proposer = Proposer

; - type Input = N::Input; - type Output = N::Output; - type Error = Error; - - fn init( - &self, - parent_header: &Header, - authorities: &[AuthorityId], - sign_with: Arc, - ) -> Result<(Self::Proposer, Self::Input, Self::Output), Error> { - use runtime_primitives::traits::{Hash as HashT, BlakeTwo256}; - - // force delay in evaluation this long. - const FORCE_DELAY: Timestamp = 5; - - let parent_hash = parent_header.hash().into(); - - let id = BlockId::hash(parent_hash); - let random_seed = self.client.random_seed(&id)?; - let random_seed = BlakeTwo256::hash(&*random_seed); - - let validators = self.client.validators(&id)?; - self.offline.write().note_new_block(&validators[..]); - - info!("Starting consensus session on top of parent {:?}", parent_hash); - - let local_id = sign_with.public().0.into(); - let (input, output) = self.network.communication_for( - authorities, - local_id, - parent_hash.clone(), - self.handle.clone(), - ); - let now = Instant::now(); - let proposer = Proposer { - client: self.client.clone(), - start: now, - local_key: sign_with, - parent_hash, - parent_id: id, - parent_number: parent_header.number, - random_seed, - transaction_pool: self.transaction_pool.clone(), - offline: self.offline.clone(), - validators, - minimum_timestamp: current_timestamp() + FORCE_DELAY, - }; - - Ok((proposer, input, output)) - } -} - -/// The proposer logic. -pub struct Proposer { - client: Arc, - start: Instant, - local_key: Arc, - parent_hash: Hash, - parent_id: BlockId, - parent_number: BlockNumber, - random_seed: Hash, - transaction_pool: Arc>, - offline: SharedOfflineTracker, - validators: Vec, - minimum_timestamp: u64, -} - -impl Proposer { - fn primary_index(&self, round_number: usize, len: usize) -> usize { - use primitives::uint::U256; - - let big_len = U256::from(len); - let offset = U256::from_big_endian(&self.random_seed.0) % big_len; - let offset = offset.low_u64() as usize + round_number; - offset % len - } -} - -impl bft::Proposer for Proposer - where - C: Api + Send + Sync, -{ - type Create = Result; - type Error = Error; - type Evaluate = Box>; - - fn propose(&self) -> Result { - use demo_api::BlockBuilder; - use runtime_primitives::traits::{Hash as HashT, BlakeTwo256}; - use demo_primitives::InherentData; - - const MAX_VOTE_OFFLINE_SECONDS: Duration = Duration::from_secs(60); - - // TODO: handle case when current timestamp behind that in state. - let timestamp = ::std::cmp::max(self.minimum_timestamp, current_timestamp()); - - let elapsed_since_start = self.start.elapsed(); - let offline_indices = if elapsed_since_start > MAX_VOTE_OFFLINE_SECONDS { - Vec::new() - } else { - self.offline.read().reports(&self.validators[..]) - }; - - if !offline_indices.is_empty() { - info!( - "Submitting offline validators {:?} for slash-vote", - offline_indices.iter().map(|&i| self.validators[i as usize]).collect::>(), - ) - } - - let inherent_data = InherentData { - timestamp, - offline_indices, - }; - - let mut block_builder = self.client.build_block(&self.parent_id, inherent_data)?; - - { - let mut unqueue_invalid = Vec::new(); - let result = self.transaction_pool.cull_and_get_pending(&BlockId::hash(self.parent_hash), |pending_iterator| { - let mut pending_size = 0; - for pending in pending_iterator { - if pending_size + pending.verified.encoded_size() >= MAX_TRANSACTIONS_SIZE { break } - - match block_builder.push_extrinsic(pending.original.clone()) { - Ok(()) => { - pending_size += pending.verified.encoded_size(); - } - Err(e) => { - trace!(target: "transaction-pool", "Invalid transaction: {}", e); - unqueue_invalid.push(pending.verified.hash().clone()); - } - } - } - }); - if let Err(e) = result { - warn!("Unable to get the pending set: {:?}", e); - } - - self.transaction_pool.remove(&unqueue_invalid, false); - } - - let block = block_builder.bake()?; - - info!("Proposing block [number: {}; hash: {}; parent_hash: {}; extrinsics: [{}]]", - block.header.number, - Hash::from(block.header.hash()), - block.header.parent_hash, - block.extrinsics.iter() - .map(|xt| format!("{}", BlakeTwo256::hash_of(xt))) - .collect::>() - .join(", ") - ); - - let substrate_block = Decode::decode(&mut block.encode().as_slice()) - .expect("blocks are defined to serialize to substrate blocks correctly; qed"); - - assert!(evaluation::evaluate_initial( - &substrate_block, - timestamp, - &self.parent_hash, - self.parent_number, - ).is_ok()); - - Ok(substrate_block) - } - - fn evaluate(&self, unchecked_proposal: &Block) -> Self::Evaluate { - debug!(target: "bft", "evaluating block on top of parent ({}, {:?})", self.parent_number, self.parent_hash); - - let current_timestamp = current_timestamp(); - - // do initial serialization and structural integrity checks. - let maybe_proposal = evaluation::evaluate_initial( - unchecked_proposal, - current_timestamp, - &self.parent_hash, - self.parent_number, - ); - - let proposal = match maybe_proposal { - Ok(p) => p, - Err(e) => { - // TODO: these errors are easily re-checked in runtime. - debug!(target: "bft", "Invalid proposal: {:?}", e); - return Box::new(future::ok(false)); - } - }; - - let vote_delays = { - let now = Instant::now(); - - // the duration until the given timestamp is current - let proposed_timestamp = ::std::cmp::max(self.minimum_timestamp, proposal.timestamp()); - let timestamp_delay = if proposed_timestamp > current_timestamp { - let delay_s = proposed_timestamp - current_timestamp; - debug!(target: "bft", "Delaying evaluation of proposal for {} seconds", delay_s); - Some(now + Duration::from_secs(delay_s)) - } else { - None - }; - - match timestamp_delay { - Some(duration) => future::Either::A( - Delay::new(duration).map_err(|e| Error::from(ErrorKind::Timer(e))) - ), - None => future::Either::B(future::ok(())), - } - }; - - // refuse to vote if this block says a validator is offline that we - // think isn't. - let offline = proposal.noted_offline(); - if !self.offline.read().check_consistency(&self.validators[..], offline) { - return Box::new(futures::empty()); - } - - // evaluate whether the block is actually valid. - // TODO: is it better to delay this until the delays are finished? - let evaluated = self.client - .evaluate_block(&self.parent_id, unchecked_proposal.clone()) - .map_err(Into::into); - - let future = future::result(evaluated).and_then(move |good| { - let end_result = future::ok(good); - if good { - // delay a "good" vote. - future::Either::A(vote_delays.and_then(|_| end_result)) - } else { - // don't delay a "bad" evaluation. - future::Either::B(end_result) - } - }); - - Box::new(future) as Box<_> - } - - fn round_proposer(&self, round_number: usize, authorities: &[AuthorityId]) -> AuthorityId { - let offset = self.primary_index(round_number, authorities.len()); - let proposer = authorities[offset].clone(); - trace!(target: "bft", "proposer for round {} is {}", round_number, proposer); - - proposer - } - - fn import_misbehavior(&self, misbehavior: Vec<(AuthorityId, bft::Misbehavior)>) { - use rhododendron::Misbehavior as GenericMisbehavior; - use runtime_primitives::bft::{MisbehaviorKind, MisbehaviorReport}; - use demo_primitives::UncheckedExtrinsic as GenericExtrinsic; - use demo_runtime::{Call, UncheckedExtrinsic, ConsensusCall}; - - let local_id = self.local_key.public().0.into(); - let mut next_index = { - let cur_index = self.transaction_pool.cull_and_get_pending(&BlockId::hash(self.parent_hash), |pending| pending - .filter(|tx| tx.verified.sender == local_id) - .last() - .map(|tx| Ok(tx.verified.index())) - .unwrap_or_else(|| self.client.index(&self.parent_id, local_id)) - ); - - match cur_index { - Ok(Ok(cur_index)) => cur_index + 1, - Ok(Err(e)) => { - warn!(target: "consensus", "Error computing next transaction index: {}", e); - return; - } - Err(e) => { - warn!(target: "consensus", "Error computing next transaction index: {}", e); - return; - } - } - }; - - for (target, misbehavior) in misbehavior { - let report = MisbehaviorReport { - parent_hash: self.parent_hash, - parent_number: self.parent_number, - target, - misbehavior: match misbehavior { - GenericMisbehavior::ProposeOutOfTurn(_, _, _) => continue, - GenericMisbehavior::DoublePropose(_, _, _) => continue, - GenericMisbehavior::DoublePrepare(round, (h1, s1), (h2, s2)) - => MisbehaviorKind::BftDoublePrepare(round as u32, (h1, s1.signature), (h2, s2.signature)), - GenericMisbehavior::DoubleCommit(round, (h1, s1), (h2, s2)) - => MisbehaviorKind::BftDoubleCommit(round as u32, (h1, s1.signature), (h2, s2.signature)), - } - }; - let payload = (next_index, Call::Consensus(ConsensusCall::report_misbehavior(report))); - let signature = self.local_key.sign(&payload.encode()).into(); - next_index += 1; - - let local_id = self.local_key.public().0.into(); - let extrinsic = UncheckedExtrinsic { - signature: Some((demo_runtime::RawAddress::Id(local_id), signature)), - index: payload.0, - function: payload.1, - }; - let uxt: GenericExtrinsic = Decode::decode(&mut extrinsic.encode().as_slice()).expect("Encoded extrinsic is valid"); - self.transaction_pool.submit_one(&BlockId::hash(self.parent_hash), uxt) - .expect("locally signed extrinsic is valid; qed"); - } - } - - fn on_round_end(&self, round_number: usize, was_proposed: bool) { - let primary_validator = self.validators[ - self.primary_index(round_number, self.validators.len()) - ]; - - - // alter the message based on whether we think the empty proposer was forced to skip the round. - // this is determined by checking if our local validator would have been forced to skip the round. - if !was_proposed { - let public = ::ed25519::Public::from_raw(primary_validator.0); - info!( - "Potential Offline Validator: {} failed to propose during assigned slot: {}", - public, - round_number, - ); - } - - self.offline.write().note_round_end(primary_validator, was_proposed); - } -} - -fn current_timestamp() -> Timestamp { - time::SystemTime::now().duration_since(time::UNIX_EPOCH) - .expect("now always later than unix epoch; qed") - .as_secs() -} diff --git a/demo/consensus/src/offline_tracker.rs b/demo/consensus/src/offline_tracker.rs deleted file mode 100644 index 243b801bcec0c..0000000000000 --- a/demo/consensus/src/offline_tracker.rs +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. - -// Substrate Demo 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 Demo 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 Demo. If not, see . - -//! Tracks offline validators. - -use demo_primitives::AccountId; - -use std::collections::HashMap; -use std::time::{Instant, Duration}; - -// time before we report a validator. -const REPORT_TIME: Duration = Duration::from_secs(60 * 5); - -struct Observed { - last_round_end: Instant, - offline_since: Instant, -} - -impl Observed { - fn new() -> Observed { - let now = Instant::now(); - Observed { - last_round_end: now, - offline_since: now, - } - } - - fn note_round_end(&mut self, was_online: bool) { - let now = Instant::now(); - - self.last_round_end = now; - if was_online { - self.offline_since = now; - } - } - - fn is_active(&self) -> bool { - // can happen if clocks are not monotonic - if self.offline_since > self.last_round_end { return true } - self.last_round_end.duration_since(self.offline_since) < REPORT_TIME - } -} - -/// Tracks offline validators and can issue a report for those offline. -pub struct OfflineTracker { - observed: HashMap, -} - -impl OfflineTracker { - /// Create a new tracker. - pub fn new() -> Self { - OfflineTracker { observed: HashMap::new() } - } - - /// Note new consensus is starting with the given set of validators. - pub fn note_new_block(&mut self, validators: &[AccountId]) { - use std::collections::HashSet; - - let set: HashSet<_> = validators.iter().cloned().collect(); - self.observed.retain(|k, _| set.contains(k)); - } - - /// Note that a round has ended. - pub fn note_round_end(&mut self, validator: AccountId, was_online: bool) { - self.observed.entry(validator) - .or_insert_with(Observed::new) - .note_round_end(was_online); - } - - /// Generate a vector of indices for offline account IDs. - pub fn reports(&self, validators: &[AccountId]) -> Vec { - validators.iter() - .enumerate() - .filter_map(|(i, v)| if self.is_online(v) { - None - } else { - Some(i as u32) - }) - .collect() - } - - /// Whether reports on a validator set are consistent with our view of things. - pub fn check_consistency(&self, validators: &[AccountId], reports: &[u32]) -> bool { - reports.iter().cloned().all(|r| { - let v = match validators.get(r as usize) { - Some(v) => v, - None => return false, - }; - - // we must think all validators reported externally are offline. - let thinks_online = self.is_online(v); - !thinks_online - }) - } - - fn is_online(&self, v: &AccountId) -> bool { - self.observed.get(v).map(Observed::is_active).unwrap_or(true) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn validator_offline() { - let mut tracker = OfflineTracker::new(); - let v = [0; 32].into(); - let v2 = [1; 32].into(); - let v3 = [2; 32].into(); - tracker.note_round_end(v, true); - tracker.note_round_end(v2, true); - tracker.note_round_end(v3, true); - - let slash_time = REPORT_TIME + Duration::from_secs(5); - tracker.observed.get_mut(&v).unwrap().offline_since -= slash_time; - tracker.observed.get_mut(&v2).unwrap().offline_since -= slash_time; - - assert_eq!(tracker.reports(&[v, v2, v3]), vec![0, 1]); - - tracker.note_new_block(&[v, v3]); - assert_eq!(tracker.reports(&[v, v2, v3]), vec![0]); - } -} diff --git a/demo/consensus/src/service.rs b/demo/consensus/src/service.rs deleted file mode 100644 index e70bc78ab2154..0000000000000 --- a/demo/consensus/src/service.rs +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. - -// Substrate Demo 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 Demo 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 Demo. If not, see . - -//! Consensus service. - -/// Consensus service. A long running service that manages BFT agreement -/// the network. -use std::thread; -use std::time::{Duration, Instant}; -use std::sync::Arc; - -use bft::{self, BftService}; -use client::{BlockchainEvents, ChainHead, BlockBody}; -use ed25519; -use futures::prelude::*; -use demo_api::Api; -use demo_primitives::{Block, Header}; -use transaction_pool::TransactionPool; - -use tokio::executor::current_thread::TaskExecutor as LocalThreadHandle; -use tokio::runtime::TaskExecutor as ThreadPoolHandle; -use tokio::runtime::current_thread::Runtime as LocalRuntime; -use tokio::timer::Interval; - -use super::{Network, ProposerFactory}; -use error; - -const TIMER_DELAY_MS: u64 = 5000; -const TIMER_INTERVAL_MS: u64 = 500; - -// spin up an instance of BFT agreement on the current thread's executor. -// panics if there is no current thread executor. -fn start_bft( - header: Header, - bft_service: Arc>, -) where - F: bft::Environment + 'static, - C: bft::BlockImport + bft::Authorities + 'static, - F::Error: ::std::fmt::Debug, - >::Error: ::std::fmt::Display + Into, - >::Error: ::std::fmt::Display -{ - let mut handle = LocalThreadHandle::current(); - match bft_service.build_upon(&header) { - Ok(Some(bft_work)) => if let Err(e) = handle.spawn_local(Box::new(bft_work)) { - warn!(target: "bft", "Couldn't initialize BFT agreement: {:?}", e); - } - Ok(None) => trace!(target: "bft", "Could not start agreement on top of {}", header.hash()), - Err(e) => warn!(target: "bft", "BFT agreement error: {}", e), - } -} - -/// Consensus service. Starts working when created. -pub struct Service { - thread: Option>, - exit_signal: Option<::exit_future::Signal>, -} - -impl Service { - /// Create and start a new instance. - pub fn new( - client: Arc, - api: Arc, - network: N, - transaction_pool: Arc>, - thread_pool: ThreadPoolHandle, - key: ed25519::Pair, - ) -> Service - where - A: Api + Send + Sync + 'static, - C: BlockchainEvents + ChainHead + BlockBody, - C: bft::BlockImport + bft::Authorities + Send + Sync + 'static, - N: Network + Send + 'static, - { - use parking_lot::RwLock; - use super::OfflineTracker; - - let (signal, exit) = ::exit_future::signal(); - let thread = thread::spawn(move || { - let mut runtime = LocalRuntime::new().expect("Could not create local runtime"); - let key = Arc::new(key); - - let factory = ProposerFactory { - client: api.clone(), - transaction_pool: transaction_pool.clone(), - network, - handle: thread_pool.clone(), - offline: Arc::new(RwLock::new(OfflineTracker::new())), - }; - let bft_service = Arc::new(BftService::new(client.clone(), key, factory)); - - let notifications = { - let client = client.clone(); - let bft_service = bft_service.clone(); - - client.import_notification_stream().for_each(move |notification| { - if notification.is_new_best { - start_bft(notification.header, bft_service.clone()); - } - Ok(()) - }) - }; - - let interval = Interval::new( - Instant::now() + Duration::from_millis(TIMER_DELAY_MS), - Duration::from_millis(TIMER_INTERVAL_MS), - ); - - let mut prev_best = match client.best_block_header() { - Ok(header) => header.hash(), - Err(e) => { - warn!("Cant's start consensus service. Error reading best block header: {:?}", e); - return; - } - }; - - let timed = { - let c = client.clone(); - let s = bft_service.clone(); - - interval.map_err(|e| debug!(target: "bft", "Timer error: {:?}", e)).for_each(move |_| { - if let Ok(best_block) = c.best_block_header() { - let hash = best_block.hash(); - - if hash == prev_best { - debug!(target: "bft", "Starting consensus round after a timeout"); - start_bft(best_block, s.clone()); - } - prev_best = hash; - } - Ok(()) - }) - }; - - runtime.spawn(notifications); - runtime.spawn(timed); - - if let Err(e) = runtime.block_on(exit) { - debug!("BFT event loop error {:?}", e); - } - }); - Service { - thread: Some(thread), - exit_signal: Some(signal), - } - } -} - -impl Drop for Service { - fn drop(&mut self) { - if let Some(signal) = self.exit_signal.take() { - signal.fire(); - } - - if let Some(thread) = self.thread.take() { - thread.join().expect("The service thread has panicked"); - } - } -} diff --git a/demo/executor/Cargo.toml b/demo/executor/Cargo.toml deleted file mode 100644 index 5bf56e9a8f0c4..0000000000000 --- a/demo/executor/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "demo-executor" -version = "0.1.0" -authors = ["Parity Technologies "] -description = "Substrate Demo node implementation in Rust." - -[dependencies] -hex-literal = "0.1" -triehash = "0.2" -ed25519 = { path = "../../substrate/ed25519" } -substrate-codec = { path = "../../substrate/codec" } -substrate-runtime-io = { path = "../../substrate/runtime-io" } -substrate-runtime-support = { path = "../../substrate/runtime-support" } -substrate-state-machine = { path = "../../substrate/state-machine" } -substrate-executor = { path = "../../substrate/executor" } -substrate-primitives = { path = "../../substrate/primitives" } -demo-primitives = { path = "../primitives" } -demo-runtime = { path = "../runtime" } - -[dev-dependencies] -substrate-keyring = { path = "../../substrate/keyring" } -substrate-runtime-primitives = { path = "../../substrate/runtime/primitives" } -substrate-runtime-balances = { path = "../../substrate/runtime/balances" } -substrate-runtime-session = { path = "../../substrate/runtime/session" } -substrate-runtime-staking = { path = "../../substrate/runtime/staking" } -substrate-runtime-system = { path = "../../substrate/runtime/system" } -substrate-runtime-consensus = { path = "../../substrate/runtime/consensus" } -substrate-runtime-timestamp = { path = "../../substrate/runtime/timestamp" } -substrate-runtime-treasury = { path = "../../substrate/runtime/treasury" } diff --git a/demo/executor/src/lib.rs b/demo/executor/src/lib.rs deleted file mode 100644 index 83f64f35169fd..0000000000000 --- a/demo/executor/src/lib.rs +++ /dev/null @@ -1,525 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. - -// Substrate Demo 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 Demo 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 Demo. If not, see . - -//! A `CodeExecutor` specialisation which uses natively compiled runtime when the wasm to be -//! executed is equivalent to the natively compiled code. - -extern crate demo_runtime; -#[macro_use] extern crate substrate_executor; -extern crate substrate_codec as codec; -extern crate substrate_state_machine as state_machine; -extern crate substrate_runtime_io as runtime_io; -extern crate substrate_primitives as primitives; -extern crate demo_primitives; -extern crate ed25519; -extern crate triehash; - -#[cfg(test)] extern crate substrate_keyring as keyring; -#[cfg(test)] extern crate substrate_runtime_primitives as runtime_primitives; -#[cfg(test)] extern crate substrate_runtime_support as runtime_support; -#[cfg(test)] extern crate substrate_runtime_balances as balances; -#[cfg(test)] extern crate substrate_runtime_session as session; -#[cfg(test)] extern crate substrate_runtime_staking as staking; -#[cfg(test)] extern crate substrate_runtime_system as system; -#[cfg(test)] extern crate substrate_runtime_consensus as consensus; -#[cfg(test)] extern crate substrate_runtime_timestamp as timestamp; -#[cfg(test)] extern crate substrate_runtime_treasury as treasury; -#[cfg(test)] #[macro_use] extern crate hex_literal; - -pub use substrate_executor::NativeExecutor; -native_executor_instance!(pub Executor, demo_runtime::api::dispatch, demo_runtime::VERSION, include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm")); - -#[cfg(test)] -mod tests { - use runtime_io; - use super::Executor; - use substrate_executor::{WasmExecutor, NativeExecutionDispatch}; - use codec::{Encode, Decode, Joiner}; - use keyring::Keyring; - use runtime_support::{Hashable, StorageValue, StorageMap}; - use state_machine::{CodeExecutor, TestExternalities}; - use primitives::{twox_128, Blake2Hasher}; - use demo_primitives::{Hash, BlockNumber, AccountId}; - use runtime_primitives::traits::Header as HeaderT; - use runtime_primitives::{ApplyOutcome, ApplyError, ApplyResult}; - use {balances, staking, session, system, consensus, timestamp, treasury}; - use system::{EventRecord, Phase}; - use demo_runtime::{Header, Block, UncheckedExtrinsic, CheckedExtrinsic, Call, Runtime, Balances, - BuildStorage, GenesisConfig, BalancesConfig, SessionConfig, StakingConfig, System, Event}; - use ed25519::{Public, Pair}; - - const BLOATY_CODE: &[u8] = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm"); - const COMPACT_CODE: &[u8] = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm"); - - // TODO: move into own crate. - macro_rules! map { - ($( $name:expr => $value:expr ),*) => ( - vec![ $( ( $name, $value ) ),* ].into_iter().collect() - ) - } - - fn alice() -> AccountId { - AccountId::from(Keyring::Alice.to_raw_public()) - } - - fn bob() -> AccountId { - AccountId::from(Keyring::Bob.to_raw_public()) - } - - fn sign(xt: CheckedExtrinsic) -> UncheckedExtrinsic { - match xt.signed { - Some(signed) => { - let payload = (xt.index, xt.function); - let pair = Pair::from(Keyring::from_public(Public::from_raw(signed.clone().into())).unwrap()); - let signature = pair.sign(&payload.encode()).into(); - UncheckedExtrinsic { - signature: Some((balances::address::Address::Id(signed), signature)), - index: payload.0, - function: payload.1, - } - } - None => UncheckedExtrinsic { - signature: None, - index: xt.index, - function: xt.function, - }, - } - } - - fn xt() -> UncheckedExtrinsic { - sign(CheckedExtrinsic { - signed: Some(alice()), - index: 0, - function: Call::Balances(balances::Call::transfer::(bob().into(), 69)), - }) - } - - fn from_block_number(n: u64) -> Header { - Header::new(n, Default::default(), Default::default(), [69; 32].into(), Default::default()) - } - - fn executor() -> ::substrate_executor::NativeExecutor { - ::substrate_executor::NativeExecutor::new() - } - - #[test] - fn panic_execution_with_foreign_code_gives_error() { - let mut t: TestExternalities = map![ - twox_128(&>::key_for(alice())).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0], - twox_128(>::key()).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0], - twox_128(>::key()).to_vec() => vec![70u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32] - ]; - - let r = executor().call(&mut t, 8, BLOATY_CODE, "initialise_block", &vec![].and(&from_block_number(1u64)), true).0; - assert!(r.is_ok()); - let v = executor().call(&mut t, 8, BLOATY_CODE, "apply_extrinsic", &vec![].and(&xt()), true).0.unwrap(); - let r = ApplyResult::decode(&mut &v[..]).unwrap(); - assert_eq!(r, Err(ApplyError::CantPay)); - } - - #[test] - fn bad_extrinsic_with_native_equivalent_code_gives_error() { - let mut t: TestExternalities = map![ - twox_128(&>::key_for(alice())).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0], - twox_128(>::key()).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0], - twox_128(>::key()).to_vec() => vec![70u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32] - ]; - - let r = executor().call(&mut t, 8, COMPACT_CODE, "initialise_block", &vec![].and(&from_block_number(1u64)), true).0; - assert!(r.is_ok()); - let v = executor().call(&mut t, 8, COMPACT_CODE, "apply_extrinsic", &vec![].and(&xt()), true).0.unwrap(); - let r = ApplyResult::decode(&mut &v[..]).unwrap(); - assert_eq!(r, Err(ApplyError::CantPay)); - } - - #[test] - fn successful_execution_with_native_equivalent_code_gives_ok() { - let mut t: TestExternalities = map![ - twox_128(&>::key_for(alice())).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0], - twox_128(>::key()).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32] - ]; - - let r = executor().call(&mut t, 8, COMPACT_CODE, "initialise_block", &vec![].and(&from_block_number(1u64)), true).0; - assert!(r.is_ok()); - let r = executor().call(&mut t, 8, COMPACT_CODE, "apply_extrinsic", &vec![].and(&xt()), true).0; - assert!(r.is_ok()); - - runtime_io::with_externalities(&mut t, || { - assert_eq!(Balances::total_balance(&alice()), 42); - assert_eq!(Balances::total_balance(&bob()), 69); - }); - } - - #[test] - fn successful_execution_with_foreign_code_gives_ok() { - let mut t: TestExternalities = map![ - twox_128(&>::key_for(alice())).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0], - twox_128(>::key()).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32] - ]; - - let r = executor().call(&mut t, 8, BLOATY_CODE, "initialise_block", &vec![].and(&from_block_number(1u64)), true).0; - assert!(r.is_ok()); - let r = executor().call(&mut t, 8, BLOATY_CODE, "apply_extrinsic", &vec![].and(&xt()), true).0; - assert!(r.is_ok()); - - runtime_io::with_externalities(&mut t, || { - assert_eq!(Balances::total_balance(&alice()), 42); - assert_eq!(Balances::total_balance(&bob()), 69); - }); - } - - fn new_test_ext() -> TestExternalities { - use keyring::Keyring::*; - let three = [3u8; 32].into(); - GenesisConfig { - consensus: Some(Default::default()), - system: Some(Default::default()), - balances: Some(BalancesConfig { - balances: vec![(alice(), 111)], - transaction_base_fee: 1, - transaction_byte_fee: 0, - existential_deposit: 0, - transfer_fee: 0, - creation_fee: 0, - reclaim_rebate: 0, - }), - session: Some(SessionConfig { - session_length: 2, - validators: vec![One.to_raw_public().into(), Two.to_raw_public().into(), three], - }), - staking: Some(StakingConfig { - sessions_per_era: 2, - current_era: 0, - intentions: vec![alice(), bob(), Charlie.to_raw_public().into()], - validator_count: 3, - minimum_validator_count: 0, - bonding_duration: 0, - offline_slash: 0, - session_reward: 0, - offline_slash_grace: 0, - }), - democracy: Some(Default::default()), - council: Some(Default::default()), - timestamp: Some(Default::default()), - treasury: Some(Default::default()), - }.build_storage().unwrap().into() - } - - fn construct_block(number: BlockNumber, parent_hash: Hash, state_root: Hash, extrinsics: Vec) -> (Vec, Hash) { - use triehash::ordered_trie_root; - - let extrinsics = extrinsics.into_iter().map(sign).collect::>(); - let extrinsics_root = ordered_trie_root::(extrinsics.iter().map(Encode::encode)).0.into(); - - let header = Header { - parent_hash, - number, - state_root, - extrinsics_root, - digest: Default::default(), - }; - let hash = header.blake2_256(); - - (Block { header, extrinsics }.encode(), hash.into()) - } - - fn block1() -> (Vec, Hash) { - construct_block( - 1, - [69u8; 32].into(), - hex!("c2fcc528c92b3c958b0e0914f26e05f151903ed43c87f29b20f9c8f0450d7484").into(), - vec![ - CheckedExtrinsic { - signed: None, - index: 0, - function: Call::Timestamp(timestamp::Call::set(42)), - }, - CheckedExtrinsic { - signed: Some(alice()), - index: 0, - function: Call::Balances(balances::Call::transfer(bob().into(), 69)), - }, - ] - ) - } - - fn block2() -> (Vec, Hash) { - construct_block( - 2, - block1().1, - hex!("62e5879f10338fa6136161c60ae0ffc35936f7b8c3fdb38095ddd0e044309762").into(), - vec![ - CheckedExtrinsic { - signed: None, - index: 0, - function: Call::Timestamp(timestamp::Call::set(52)), - }, - CheckedExtrinsic { - signed: Some(bob()), - index: 0, - function: Call::Balances(balances::Call::transfer(alice().into(), 5)), - }, - CheckedExtrinsic { - signed: Some(alice()), - index: 1, - function: Call::Balances(balances::Call::transfer(bob().into(), 15)), - } - ] - ) - } - - fn block1big() -> (Vec, Hash) { - construct_block( - 1, - [69u8; 32].into(), - hex!("789b19bc7beaa83ae70412f65ad0ac02435fd79e0226ba3394865a052e56fbd8").into(), - vec![ - CheckedExtrinsic { - signed: None, - index: 0, - function: Call::Timestamp(timestamp::Call::set(42)), - }, - CheckedExtrinsic { - signed: Some(alice()), - index: 0, - function: Call::Consensus(consensus::Call::remark(vec![0; 120000])), - } - ] - ) - } - - #[test] - fn full_native_block_import_works() { - let mut t = new_test_ext(); - - executor().call(&mut t, 8, COMPACT_CODE, "execute_block", &block1().0, true).0.unwrap(); - - runtime_io::with_externalities(&mut t, || { - assert_eq!(Balances::total_balance(&alice()), 41); - assert_eq!(Balances::total_balance(&bob()), 69); - assert_eq!(System::events(), vec![ - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: Event::system(system::Event::ExtrinsicSuccess) - }, - EventRecord { - phase: Phase::ApplyExtrinsic(1), - event: Event::balances(balances::RawEvent::NewAccount(bob(), 1, balances::NewAccountOutcome::NoHint)) - }, - EventRecord { - phase: Phase::ApplyExtrinsic(1), - event: Event::balances(balances::RawEvent::Transfer( - hex!["d172a74cda4c865912c32ba0a80a57ae69abae410e5ccb59dee84e2f4432db4f"].into(), - hex!["d7568e5f0a7eda67a82691ff379ac4bba4f9c9b859fe779b5d46363b61ad2db9"].into(), - 69, - 0 - )) - }, - EventRecord { - phase: Phase::ApplyExtrinsic(1), - event: Event::system(system::Event::ExtrinsicSuccess) - }, - EventRecord { - phase: Phase::Finalization, - event: Event::treasury(treasury::RawEvent::Spending(0)) - }, - EventRecord { - phase: Phase::Finalization, - event: Event::treasury(treasury::RawEvent::Burnt(0)) - }, - EventRecord { - phase: Phase::Finalization, - event: Event::treasury(treasury::RawEvent::Rollover(0)) - } - ]); - }); - - executor().call(&mut t, 8, COMPACT_CODE, "execute_block", &block2().0, true).0.unwrap(); - - runtime_io::with_externalities(&mut t, || { - assert_eq!(Balances::total_balance(&alice()), 30); - assert_eq!(Balances::total_balance(&bob()), 78); - assert_eq!(System::events(), vec![ - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: Event::system(system::Event::ExtrinsicSuccess) - }, - EventRecord { - phase: Phase::ApplyExtrinsic(1), - event: Event::balances( - balances::RawEvent::Transfer( - hex!["d7568e5f0a7eda67a82691ff379ac4bba4f9c9b859fe779b5d46363b61ad2db9"].into(), - hex!["d172a74cda4c865912c32ba0a80a57ae69abae410e5ccb59dee84e2f4432db4f"].into(), - 5, - 0 - ) - ) - }, - EventRecord { - phase: Phase::ApplyExtrinsic(1), - event: Event::system(system::Event::ExtrinsicSuccess) - }, - EventRecord { - phase: Phase::ApplyExtrinsic(2), - event: Event::balances( - balances::RawEvent::Transfer( - hex!["d172a74cda4c865912c32ba0a80a57ae69abae410e5ccb59dee84e2f4432db4f"].into(), - hex!["d7568e5f0a7eda67a82691ff379ac4bba4f9c9b859fe779b5d46363b61ad2db9"].into(), - 15, - 0 - ) - ) - }, - EventRecord { - phase: Phase::ApplyExtrinsic(2), - event: Event::system(system::Event::ExtrinsicSuccess) - }, - EventRecord { - phase: Phase::Finalization, - event: Event::session(session::RawEvent::NewSession(1)) - }, - EventRecord { - phase: Phase::Finalization, - event: Event::staking(staking::RawEvent::Reward(0)) - }, - EventRecord { - phase: Phase::Finalization, - event: Event::treasury(treasury::RawEvent::Spending(0)) - }, - EventRecord { - phase: Phase::Finalization, - event: Event::treasury(treasury::RawEvent::Burnt(0)) - }, - EventRecord { - phase: Phase::Finalization, - event: Event::treasury(treasury::RawEvent::Rollover(0)) - } - ]); - }); - } - - #[test] - fn full_wasm_block_import_works() { - let mut t = new_test_ext(); - - WasmExecutor::new().call(&mut t, 8, COMPACT_CODE, "execute_block", &block1().0).unwrap(); - - runtime_io::with_externalities(&mut t, || { - assert_eq!(Balances::total_balance(&alice()), 41); - assert_eq!(Balances::total_balance(&bob()), 69); - }); - - WasmExecutor::new().call(&mut t, 8, COMPACT_CODE, "execute_block", &block2().0).unwrap(); - - runtime_io::with_externalities(&mut t, || { - assert_eq!(Balances::total_balance(&alice()), 30); - assert_eq!(Balances::total_balance(&bob()), 78); - }); - } - - #[test] - fn wasm_big_block_import_fails() { - let mut t = new_test_ext(); - - let r = WasmExecutor::new().call(&mut t, 8, COMPACT_CODE, "execute_block", &block1big().0); - assert!(!r.is_ok()); - } - - #[test] - fn native_big_block_import_succeeds() { - let mut t = new_test_ext(); - - let r = Executor::new().call(&mut t, 8, COMPACT_CODE, "execute_block", &block1big().0, true).0; - assert!(r.is_ok()); - } - - #[test] - fn native_big_block_import_fails_on_fallback() { - let mut t = new_test_ext(); - - let r = Executor::new().call(&mut t, 8, COMPACT_CODE, "execute_block", &block1big().0, false).0; - assert!(!r.is_ok()); - } - - #[test] - fn panic_execution_gives_error() { - let mut t: TestExternalities = map![ - twox_128(&>::key_for(alice())).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0], - twox_128(>::key()).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0], - twox_128(>::key()).to_vec() => vec![70u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32] - ]; - - let foreign_code = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm"); - let r = WasmExecutor::new().call(&mut t, 8, &foreign_code[..], "initialise_block", &vec![].and(&from_block_number(1u64))); - assert!(r.is_ok()); - let r = WasmExecutor::new().call(&mut t, 8, &foreign_code[..], "apply_extrinsic", &vec![].and(&xt())).unwrap(); - let r = ApplyResult::decode(&mut &r[..]).unwrap(); - assert_eq!(r, Err(ApplyError::CantPay)); - } - - #[test] - fn successful_execution_gives_ok() { - let mut t: TestExternalities = map![ - twox_128(&>::key_for(alice())).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0], - twox_128(>::key()).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32] - ]; - - let foreign_code = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm"); - let r = WasmExecutor::new().call(&mut t, 8, &foreign_code[..], "initialise_block", &vec![].and(&from_block_number(1u64))); - assert!(r.is_ok()); - let r = WasmExecutor::new().call(&mut t, 8, &foreign_code[..], "apply_extrinsic", &vec![].and(&xt())).unwrap(); - let r = ApplyResult::decode(&mut &r[..]).unwrap(); - 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); - }); - } -} diff --git a/demo/network/Cargo.toml b/demo/network/Cargo.toml deleted file mode 100644 index 3329f779b4719..0000000000000 --- a/demo/network/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "demo-network" -version = "0.1.0" -authors = ["Parity Technologies "] -description = "Substrate demo networking protocol" - -[dependencies] -demo-api = { path = "../api" } -demo-consensus = { path = "../consensus" } -demo-primitives = { path = "../primitives" } -substrate-bft = { path = "../../substrate/bft" } -substrate-network = { path = "../../substrate/network" } -ed25519 = { path = "../../substrate/ed25519" } -futures = "0.1" -tokio = "0.1.7" -log = "0.4" -rhododendron = "0.3" diff --git a/demo/network/src/consensus.rs b/demo/network/src/consensus.rs deleted file mode 100644 index d7c5fd92c570a..0000000000000 --- a/demo/network/src/consensus.rs +++ /dev/null @@ -1,297 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. - -// Substrate Demo 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 Demo 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 Demo. If not, see . - -//! The "consensus" networking code built on top of the base network service. -//! This fulfills the `demo_consensus::Network` trait, providing a hook to be called -//! each time consensus begins on a new chain head. - -use bft; -use ed25519; -use substrate_network::{self as net, generic_message as msg}; -use substrate_network::consensus_gossip::ConsensusMessage; -use demo_api::Api; -use demo_consensus::Network; -use demo_primitives::{Block, Hash, SessionKey}; -use rhododendron; - -use futures::prelude::*; -use futures::sync::mpsc; - -use std::sync::Arc; - -use tokio::runtime::TaskExecutor; - -use super::NetworkService; - -/// Sink for output BFT messages. -pub struct BftSink { - network: Arc, - parent_hash: Hash, - _marker: ::std::marker::PhantomData, -} - -impl Sink for BftSink { - type SinkItem = bft::Communication; - // TODO: replace this with the ! type when that's stabilized - type SinkError = E; - - fn start_send(&mut self, message: bft::Communication) - -> ::futures::StartSend, E> - { - let network_message = net::LocalizedBftMessage { - message: match message { - rhododendron::Communication::Consensus(c) => msg::BftMessage::Consensus(match c { - rhododendron::LocalizedMessage::Propose(proposal) => msg::SignedConsensusMessage::Propose(msg::SignedConsensusProposal { - round_number: proposal.round_number as u32, - proposal: proposal.proposal, - digest: proposal.digest, - sender: proposal.sender, - digest_signature: proposal.digest_signature.signature, - full_signature: proposal.full_signature.signature, - }), - rhododendron::LocalizedMessage::Vote(vote) => msg::SignedConsensusMessage::Vote(msg::SignedConsensusVote { - sender: vote.sender, - signature: vote.signature.signature, - vote: match vote.vote { - rhododendron::Vote::Prepare(r, h) => msg::ConsensusVote::Prepare(r as u32, h), - rhododendron::Vote::Commit(r, h) => msg::ConsensusVote::Commit(r as u32, h), - rhododendron::Vote::AdvanceRound(r) => msg::ConsensusVote::AdvanceRound(r as u32), - } - }), - }), - rhododendron::Communication::Auxiliary(justification) => { - let unchecked: bft::UncheckedJustification<_> = justification.uncheck().into(); - msg::BftMessage::Auxiliary(unchecked.into()) - } - }, - parent_hash: self.parent_hash, - }; - self.network.with_spec( - move |spec, ctx| spec.consensus_gossip.multicast_bft_message(ctx, network_message) - ); - Ok(::futures::AsyncSink::Ready) - } - - fn poll_complete(&mut self) -> ::futures::Poll<(), E> { - Ok(Async::Ready(())) - } -} - -// check signature and authority validity of message. -fn process_bft_message( - msg: msg::LocalizedBftMessage, - local_id: &SessionKey, - authorities: &[SessionKey] - ) -> Result>, bft::Error> -{ - Ok(Some(match msg.message { - msg::BftMessage::Consensus(c) => rhododendron::Communication::Consensus(match c { - msg::SignedConsensusMessage::Propose(proposal) => rhododendron::LocalizedMessage::Propose({ - if &proposal.sender == local_id { return Ok(None) } - let proposal = rhododendron::LocalizedProposal { - round_number: proposal.round_number as usize, - proposal: proposal.proposal, - digest: proposal.digest, - sender: proposal.sender, - digest_signature: ed25519::LocalizedSignature { - signature: proposal.digest_signature, - signer: ed25519::Public(proposal.sender.into()), - }, - full_signature: ed25519::LocalizedSignature { - signature: proposal.full_signature, - signer: ed25519::Public(proposal.sender.into()), - } - }; - bft::check_proposal(authorities, &msg.parent_hash, &proposal)?; - - trace!(target: "bft", "importing proposal message for round {} from {}", proposal.round_number, Hash::from(proposal.sender.0)); - proposal - }), - msg::SignedConsensusMessage::Vote(vote) => rhododendron::LocalizedMessage::Vote({ - if &vote.sender == local_id { return Ok(None) } - let vote = rhododendron::LocalizedVote { - sender: vote.sender, - signature: ed25519::LocalizedSignature { - signature: vote.signature, - signer: ed25519::Public(vote.sender.0), - }, - vote: match vote.vote { - msg::ConsensusVote::Prepare(r, h) => rhododendron::Vote::Prepare(r as usize, h), - msg::ConsensusVote::Commit(r, h) => rhododendron::Vote::Commit(r as usize, h), - msg::ConsensusVote::AdvanceRound(r) => rhododendron::Vote::AdvanceRound(r as usize), - } - }; - bft::check_vote::(authorities, &msg.parent_hash, &vote)?; - - trace!(target: "bft", "importing vote {:?} from {}", vote.vote, Hash::from(vote.sender.0)); - vote - }), - }), - msg::BftMessage::Auxiliary(a) => { - let justification = bft::UncheckedJustification::from(a); - // TODO: get proper error - let justification: Result<_, bft::Error> = bft::check_prepare_justification::(authorities, msg.parent_hash, justification) - .map_err(|_| bft::ErrorKind::InvalidJustification.into()); - rhododendron::Communication::Auxiliary(justification?) - }, - })) -} - -// task that processes all gossipped consensus messages, -// checking signatures -struct MessageProcessTask { - inner_stream: mpsc::UnboundedReceiver>, - bft_messages: mpsc::UnboundedSender>, - validators: Vec, - local_id: SessionKey, -} - -impl MessageProcessTask { - fn process_message(&self, msg: ConsensusMessage) -> Option> { - match msg { - ConsensusMessage::Bft(msg) => { - match process_bft_message(msg, &self.local_id, &self.validators[..]) { - Ok(Some(msg)) => { - if let Err(_) = self.bft_messages.unbounded_send(msg) { - // if the BFT receiving stream has ended then - // we should just bail. - trace!(target: "bft", "BFT message stream appears to have closed"); - return Some(Async::Ready(())); - } - } - Ok(None) => {} // ignored local message - Err(e) => { - debug!("Message validation failed: {:?}", e); - } - } - } - ConsensusMessage::ChainSpecific(_, _) => { - panic!("ChainSpecific messages are not allowed by the top level message handler"); - } - } - - None - } -} - -impl Future for MessageProcessTask { - type Item = (); - type Error = (); - - fn poll(&mut self) -> Poll<(), ()> { - loop { - match self.inner_stream.poll() { - Ok(Async::Ready(Some(val))) => if let Some(async) = self.process_message(val) { - return Ok(async); - }, - Ok(Async::Ready(None)) => return Ok(Async::Ready(())), - Ok(Async::NotReady) => return Ok(Async::NotReady), - Err(e) => { - debug!(target: "demo-network", "Error getting consensus message: {:?}", e); - return Err(e); - }, - } - } - } -} - -/// Input stream from the consensus network. -pub struct InputAdapter { - input: mpsc::UnboundedReceiver>, -} - -impl Stream for InputAdapter { - type Item = bft::Communication; - type Error = ::demo_consensus::Error; - - fn poll(&mut self) -> Poll, Self::Error> { - match self.input.poll() { - Err(_) | Ok(Async::Ready(None)) => Err(bft::InputStreamConcluded.into()), - Ok(x) => Ok(x) - } - } -} - -/// Wrapper around the network service -pub struct ConsensusNetwork

{ - network: Arc, - api: Arc

, -} - -impl

ConsensusNetwork

{ - /// Create a new consensus networking object. - pub fn new(network: Arc, api: Arc

) -> Self { - ConsensusNetwork { network, api } - } -} - -impl

Clone for ConsensusNetwork

{ - fn clone(&self) -> Self { - ConsensusNetwork { - network: self.network.clone(), - api: self.api.clone(), - } - } -} - -/// A long-lived network which can create parachain statement and BFT message routing processes on demand. -impl Network for ConsensusNetwork

{ - /// The input stream of BFT messages. Should never logically conclude. - type Input = InputAdapter; - /// The output sink of BFT messages. Messages sent here should eventually pass to all - /// current validators. - type Output = BftSink<::demo_consensus::Error>; - - /// Get input and output streams of BFT messages. - fn communication_for( - &self, validators: &[SessionKey], - local_id: SessionKey, - parent_hash: Hash, - task_executor: TaskExecutor - ) -> (Self::Input, Self::Output) - { - let sink = BftSink { - network: self.network.clone(), - parent_hash, - _marker: Default::default(), - }; - - let (bft_send, bft_recv) = mpsc::unbounded(); - - // spin up a task in the background that processes all incoming statements - // TODO: propagate statements on a timer? - let process_task = self.network.with_spec(|spec, _ctx| { - spec.new_consensus(parent_hash); - MessageProcessTask { - inner_stream: spec.consensus_gossip.messages_for(parent_hash), - bft_messages: bft_send, - validators: validators.to_vec(), - local_id, - } - }); - - match process_task { - Some(task) => task_executor.spawn(task), - None => warn!(target: "demo-network", "Cannot process incoming messages: network appears to be down"), - } - - (InputAdapter { input: bft_recv }, sink) - } -} - -/// Error when the network appears to be down. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct NetworkDown; diff --git a/demo/network/src/lib.rs b/demo/network/src/lib.rs deleted file mode 100644 index f32c039669f3a..0000000000000 --- a/demo/network/src/lib.rs +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. - -// Substrate Demo 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 Demo 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 Demo. If not, see . - -//! Substrate Demo-specific network implementation. -//! -//! This manages gossip of consensus messages for BFT. - -#![warn(unused_extern_crates)] - -extern crate substrate_bft as bft; -extern crate substrate_network; - -extern crate demo_api; -extern crate demo_consensus; -extern crate demo_primitives; - -extern crate ed25519; -extern crate futures; -extern crate tokio; -extern crate rhododendron; - -#[macro_use] -extern crate log; - -pub mod consensus; - -use demo_primitives::{Block, Hash, Header}; -use substrate_network::{NodeIndex, Context, Severity}; -use substrate_network::consensus_gossip::ConsensusGossip; -use substrate_network::{message, generic_message}; -use substrate_network::specialization::Specialization; -use substrate_network::StatusMessage as GenericFullStatus; - -/// Demo protocol id. -pub const PROTOCOL_ID: ::substrate_network::ProtocolId = *b"dot"; - -type FullStatus = GenericFullStatus; - -/// Specialization of the network service for the demo protocol. -pub type NetworkService = ::substrate_network::Service; - - -/// Demo protocol attachment for substrate. -pub struct Protocol { - consensus_gossip: ConsensusGossip, - live_consensus: Option, -} - -impl Protocol { - /// Instantiate a demo protocol handler. - pub fn new() -> Self { - Protocol { - consensus_gossip: ConsensusGossip::new(), - live_consensus: None, - } - } - - /// Note new consensus session. - fn new_consensus(&mut self, parent_hash: Hash) { - let old_consensus = self.live_consensus.take(); - self.live_consensus = Some(parent_hash); - self.consensus_gossip.collect_garbage(old_consensus.as_ref()); - } -} - -impl Specialization for Protocol { - fn status(&self) -> Vec { - Vec::new() - } - - fn on_connect(&mut self, ctx: &mut Context, who: NodeIndex, status: FullStatus) { - self.consensus_gossip.new_peer(ctx, who, status.roles); - } - - fn on_disconnect(&mut self, ctx: &mut Context, who: NodeIndex) { - self.consensus_gossip.peer_disconnected(ctx, who); - } - - fn on_message(&mut self, ctx: &mut Context, who: NodeIndex, message: message::Message) { - match message { - generic_message::Message::BftMessage(msg) => { - trace!(target: "demo-network", "BFT message from {}: {:?}", who, msg); - // TODO: check signature here? what if relevant block is unknown? - self.consensus_gossip.on_bft_message(ctx, who, msg) - } - generic_message::Message::ChainSpecific(_) => { - trace!(target: "demo-network", "Bad message from {}", who); - ctx.report_peer(who, Severity::Bad("Invalid demo protocol message format")); - } - _ => {} - } - } - - fn on_abort(&mut self) { - self.consensus_gossip.abort(); - } - - fn maintain_peers(&mut self, _ctx: &mut Context) { - } - - fn on_block_imported(&mut self, _ctx: &mut Context, _hash: Hash, _header: &Header) { - } -} - diff --git a/demo/primitives/Cargo.toml b/demo/primitives/Cargo.toml deleted file mode 100644 index b517210d1951b..0000000000000 --- a/demo/primitives/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "demo-primitives" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -serde = { version = "1.0", default_features = false } -serde_derive = { version = "1.0", optional = true } -substrate-codec = { path = "../../substrate/codec", default_features = false } -substrate-codec-derive = { path = "../../substrate/codec/derive", default_features = false } -substrate-primitives = { path = "../../substrate/primitives", default_features = false } -substrate-runtime-std = { path = "../../substrate/runtime-std", default_features = false } -substrate-runtime-primitives = { path = "../../substrate/runtime/primitives", default_features = false } - -[dev-dependencies] -substrate-serializer = { path = "../../substrate/serializer" } -pretty_assertions = "0.4" - -[features] -default = ["std"] -std = [ - "substrate-codec-derive/std", - "substrate-codec/std", - "substrate-primitives/std", - "substrate-runtime-std/std", - "substrate-runtime-primitives/std", - "serde_derive", - "serde/std", -] diff --git a/demo/primitives/src/lib.rs b/demo/primitives/src/lib.rs deleted file mode 100644 index d93b75f3954e4..0000000000000 --- a/demo/primitives/src/lib.rs +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. - -// Substrate Demo 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 Demo 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 Demo. If not, see . - -//! Low-level types used throughout the Substrate Demo code. - -#![warn(missing_docs)] - -#![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(not(feature = "std"), feature(alloc))] - -#[cfg(feature = "std")] -extern crate serde; - -#[cfg(feature = "std")] -#[macro_use] -extern crate serde_derive; - -#[macro_use] -extern crate substrate_codec_derive; - -extern crate substrate_runtime_std as rstd; -extern crate substrate_runtime_primitives as runtime_primitives; -extern crate substrate_primitives as primitives; -extern crate substrate_codec as codec; - -use rstd::prelude::*; -use runtime_primitives::generic; -#[cfg(feature = "std")] -use primitives::bytes; -use runtime_primitives::traits::BlakeTwo256; - -/// An index to a block. -pub type BlockNumber = u64; - -/// Alias to Ed25519 pubkey that identifies an account on the chain. This will almost -/// certainly continue to be the same as the substrate's `AuthorityId`. -pub type AccountId = ::primitives::H256; - -/// The type for looking up accounts. We don't expect more than 4 billion of them, but you -/// never know... -pub type AccountIndex = u32; - -/// Balance of an account. -pub type Balance = u64; - -/// The Ed25519 pub key of an session that belongs to an authority of the chain. This is -/// exactly equivalent to what the substrate calls an "authority". -pub type SessionKey = primitives::AuthorityId; - -/// Index of a transaction in the chain. -pub type Index = u64; - -/// A hash of some data used by the chain. -pub type Hash = primitives::H256; - -/// Alias to 512-bit hash when used in the context of a signature on the chain. -pub type Signature = runtime_primitives::Ed25519Signature; - -/// A timestamp: seconds since the unix epoch. -pub type Timestamp = u64; - -/// Header type. -pub type Header = generic::Header>; -/// Block type. -pub type Block = generic::Block; -/// Block ID. -pub type BlockId = generic::BlockId; - -/// Opaque, encoded, unchecked extrinsic. -#[derive(PartialEq, Eq, Clone, Default, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] -pub struct UncheckedExtrinsic(#[cfg_attr(feature = "std", serde(with="bytes"))] pub Vec); -/// -/// Inherent data to include in a block. -#[derive(Encode, Decode)] -pub struct InherentData { - /// Current timestamp. - pub timestamp: Timestamp, - /// Indices of offline validators. - pub offline_indices: Vec, -} diff --git a/demo/runtime/Cargo.toml b/demo/runtime/Cargo.toml deleted file mode 100644 index 3bf3b5af928db..0000000000000 --- a/demo/runtime/Cargo.toml +++ /dev/null @@ -1,61 +0,0 @@ -[package] -name = "demo-runtime" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -rustc-hex = "1.0" -hex-literal = "0.1.0" -log = { version = "0.3", optional = true } -serde = { version = "1.0", default_features = false } -serde_derive = { version = "1.0", optional = true } -safe-mix = { version = "1.0", default_features = false} -substrate-codec = { path = "../../substrate/codec" } -substrate-codec-derive = { path = "../../substrate/codec/derive" } -substrate-runtime-std = { path = "../../substrate/runtime-std" } -substrate-runtime-io = { path = "../../substrate/runtime-io" } -substrate-runtime-support = { path = "../../substrate/runtime-support" } -substrate-primitives = { path = "../../substrate/primitives" } -substrate-keyring = { path = "../../substrate/keyring" } -substrate-runtime-balances = { path = "../../substrate/runtime/balances" } -substrate-runtime-consensus = { path = "../../substrate/runtime/consensus" } -substrate-runtime-contract = { path = "../../substrate/runtime/contract" } -substrate-runtime-council = { path = "../../substrate/runtime/council" } -substrate-runtime-democracy = { path = "../../substrate/runtime/democracy" } -substrate-runtime-executive = { path = "../../substrate/runtime/executive" } -substrate-runtime-primitives = { path = "../../substrate/runtime/primitives" } -substrate-runtime-session = { path = "../../substrate/runtime/session" } -substrate-runtime-staking = { path = "../../substrate/runtime/staking" } -substrate-runtime-system = { path = "../../substrate/runtime/system" } -substrate-runtime-timestamp = { path = "../../substrate/runtime/timestamp" } -substrate-runtime-treasury = { path = "../../substrate/runtime/treasury" } -substrate-runtime-version = { path = "../../substrate/runtime/version" } -demo-primitives = { path = "../primitives" } - -[features] -default = ["std"] -std = [ - "substrate-codec/std", - "substrate-primitives/std", - "substrate-runtime-std/std", - "substrate-runtime-io/std", - "substrate-runtime-support/std", - "substrate-runtime-balances/std", - "substrate-runtime-consensus/std", - "substrate-runtime-contract/std", - "substrate-runtime-council/std", - "substrate-runtime-democracy/std", - "substrate-runtime-executive/std", - "substrate-runtime-primitives/std", - "substrate-runtime-session/std", - "substrate-runtime-staking/std", - "substrate-runtime-system/std", - "substrate-runtime-timestamp/std", - "substrate-runtime-treasury/std", - "substrate-runtime-version/std", - "demo-primitives/std", - "serde_derive", - "serde/std", - "log", - "safe-mix/std" -] diff --git a/demo/runtime/src/checked_block.rs b/demo/runtime/src/checked_block.rs deleted file mode 100644 index 281a9e4136fca..0000000000000 --- a/demo/runtime/src/checked_block.rs +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. - -// Substrate Demo 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 Demo 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 Demo. If not, see . - -//! Typesafe block interaction. - -use super::{Call, Block, TIMESTAMP_SET_POSITION, NOTE_OFFLINE_POSITION}; -use timestamp::Call as TimestampCall; -use consensus::Call as ConsensusCall; - -/// Provides a type-safe wrapper around a structurally valid block. -pub struct CheckedBlock { - inner: Block, - file_line: Option<(&'static str, u32)>, -} - -impl CheckedBlock { - /// Create a new checked block. Fails if the block is not structurally valid. - pub fn new(block: Block) -> Result { - let has_timestamp = block.extrinsics.get(TIMESTAMP_SET_POSITION as usize).map_or(false, |xt| { - !xt.is_signed() && match xt.function { - Call::Timestamp(TimestampCall::set(_)) => true, - _ => false, - } - }); - - if !has_timestamp { return Err(block) } - - Ok(CheckedBlock { - inner: block, - file_line: None, - }) - } - - // Creates a new checked block, asserting that it is valid. - #[doc(hidden)] - pub fn new_unchecked(block: Block, file: &'static str, line: u32) -> Self { - CheckedBlock { - inner: block, - file_line: Some((file, line)), - } - } - - /// Extract the timestamp from the block. - pub fn timestamp(&self) -> ::demo_primitives::Timestamp { - let x = self.inner.extrinsics.get(TIMESTAMP_SET_POSITION as usize).and_then(|xt| match xt.function { - Call::Timestamp(TimestampCall::set(x)) => Some(x), - _ => None - }); - - match x { - Some(x) => x, - None => panic!("Invalid block asserted at {:?}", self.file_line), - } - } - - /// Extract the noted missed proposal validator indices (if any) from the block. - pub fn noted_offline(&self) -> &[u32] { - self.inner.extrinsics.get(NOTE_OFFLINE_POSITION as usize).and_then(|xt| match xt.function { - Call::Consensus(ConsensusCall::note_offline(ref x)) => Some(&x[..]), - _ => None, - }).unwrap_or(&[]) - } - - /// Convert into inner block. - pub fn into_inner(self) -> Block { self.inner } -} - -impl ::std::ops::Deref for CheckedBlock { - type Target = Block; - - fn deref(&self) -> &Block { &self.inner } -} - -/// Assert that a block is structurally valid. May lead to panic in the future -/// in case it isn't. -#[macro_export] -macro_rules! assert_demo_block { - ($block: expr) => { - $crate::CheckedBlock::new_unchecked($block, file!(), line!()) - } -} diff --git a/demo/runtime/src/lib.rs b/demo/runtime/src/lib.rs deleted file mode 100644 index c0502eed0460f..0000000000000 --- a/demo/runtime/src/lib.rs +++ /dev/null @@ -1,386 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. - -// Substrate Demo 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 Demo 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 Demo. If not, see . - -//! The Substrate Demo runtime. This can be compiled with ``#[no_std]`, ready for Wasm. - -#![cfg_attr(not(feature = "std"), no_std)] - -#[macro_use] -extern crate substrate_runtime_io as runtime_io; - -#[macro_use] -extern crate substrate_runtime_support; - -#[macro_use] -extern crate substrate_runtime_primitives as runtime_primitives; - -#[cfg(feature = "std")] -#[macro_use] -extern crate serde_derive; - -#[cfg(feature = "std")] -extern crate serde; - -extern crate substrate_codec as codec; -extern crate substrate_primitives; - -#[macro_use] -extern crate substrate_codec_derive; - -#[cfg_attr(not(feature = "std"), macro_use)] -extern crate substrate_runtime_std as rstd; -extern crate substrate_runtime_balances as balances; -extern crate substrate_runtime_consensus as consensus; -extern crate substrate_runtime_contract as contract; -extern crate substrate_runtime_council as council; -extern crate substrate_runtime_democracy as democracy; -extern crate substrate_runtime_executive as executive; -extern crate substrate_runtime_session as session; -extern crate substrate_runtime_staking as staking; -extern crate substrate_runtime_system as system; -extern crate substrate_runtime_timestamp as timestamp; -extern crate substrate_runtime_treasury as treasury; -#[macro_use] -extern crate substrate_runtime_version as version; -extern crate demo_primitives; - -#[cfg(feature = "std")] -mod checked_block; - -use rstd::prelude::*; -use substrate_primitives::u32_trait::{_2, _4}; -use codec::{Encode, Decode, Input}; -use demo_primitives::{AccountId, AccountIndex, Balance, BlockNumber, Hash, Index, SessionKey, Signature, InherentData}; -use runtime_primitives::generic; -use runtime_primitives::traits::{Convert, BlakeTwo256, DigestItem}; -use version::RuntimeVersion; -use council::{motions as council_motions, voting as council_voting}; - -#[cfg(any(feature = "std", test))] -pub use runtime_primitives::BuildStorage; -pub use consensus::Call as ConsensusCall; -pub use timestamp::Call as TimestampCall; -pub use runtime_primitives::Permill; -#[cfg(any(feature = "std", test))] -pub use checked_block::CheckedBlock; - -const TIMESTAMP_SET_POSITION: u32 = 0; -const NOTE_OFFLINE_POSITION: u32 = 1; - -// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. -#[derive(Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] -/// Runtime type used to collate and parameterize the various modules. -pub struct Runtime; - -/// Runtime version. -pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: ver_str!("demo"), - impl_name: ver_str!("substrate-demo"), - authoring_version: 1, - spec_version: 1, - impl_version: 0, -}; - -impl system::Trait for Runtime { - type Origin = Origin; - type Index = Index; - type BlockNumber = BlockNumber; - type Hash = Hash; - type Hashing = BlakeTwo256; - type Digest = generic::Digest; - type AccountId = AccountId; - type Header = generic::Header; - type Event = Event; -} - -/// System module for this concrete runtime. -pub type System = system::Module; - -impl balances::Trait for Runtime { - type Balance = Balance; - type AccountIndex = AccountIndex; - type OnFreeBalanceZero = (Staking, Contract); - type EnsureAccountLiquid = Staking; - type Event = Event; -} - -/// Staking module for this concrete runtime. -pub type Balances = balances::Module; - -impl consensus::Trait for Runtime { - const NOTE_OFFLINE_POSITION: u32 = NOTE_OFFLINE_POSITION; - type Log = Log; - type SessionKey = SessionKey; - type OnOfflineValidator = Staking; -} - -/// Consensus module for this concrete runtime. -pub type Consensus = consensus::Module; - -impl timestamp::Trait for Runtime { - const TIMESTAMP_SET_POSITION: u32 = TIMESTAMP_SET_POSITION; - type Moment = u64; -} - -/// Timestamp module for this concrete runtime. -pub type Timestamp = timestamp::Module; - -/// Session key conversion. -pub struct SessionKeyConversion; -impl Convert for SessionKeyConversion { - fn convert(a: AccountId) -> SessionKey { - a.0.into() - } -} - -impl session::Trait for Runtime { - type ConvertAccountIdToSessionKey = SessionKeyConversion; - type OnSessionChange = Staking; - type Event = Event; -} - -/// Session module for this concrete runtime. -pub type Session = session::Module; - -impl staking::Trait for Runtime { - type OnRewardMinted = Treasury; - type Event = Event; -} - -/// Staking module for this concrete runtime. -pub type Staking = staking::Module; - -impl democracy::Trait for Runtime { - type Proposal = Call; - type Event = Event; -} - -/// Democracy module for this concrete runtime. -pub type Democracy = democracy::Module; - -impl council::Trait for Runtime { - type Event = Event; -} - -/// Council module for this concrete runtime. -pub type Council = council::Module; - -impl council::voting::Trait for Runtime { - type Event = Event; -} - -/// Council voting module for this concrete runtime. -pub type CouncilVoting = council::voting::Module; - -impl council::motions::Trait for Runtime { - type Origin = Origin; - type Proposal = Call; - type Event = Event; -} - -/// Council motions module for this concrete runtime. -pub type CouncilMotions = council_motions::Module; - -impl treasury::Trait for Runtime { - type ApproveOrigin = council_motions::EnsureMembers<_4>; - type RejectOrigin = council_motions::EnsureMembers<_2>; - type Event = Event; -} - -/// Treasury module for this concrete runtime. -pub type Treasury = treasury::Module; - -/// Address calculated from the code (of the constructor), input data to the constructor -/// and account id which requested the account creation. -/// -/// Formula: `blake2_256(blake2_256(code) + blake2_256(data) + origin)` -pub struct DetermineContractAddress; -impl contract::ContractAddressFor for DetermineContractAddress { - fn contract_address_for(code: &[u8], data: &[u8], origin: &AccountId) -> AccountId { - use runtime_primitives::traits::Hash; - - let code_hash = BlakeTwo256::hash(code); - let data_hash = BlakeTwo256::hash(data); - let mut buf = [0u8, 32 + 32 + 32]; - &mut buf[0..32].copy_from_slice(&code_hash); - &mut buf[32..64].copy_from_slice(&data_hash); - &mut buf[64..96].copy_from_slice(origin); - AccountId::from(BlakeTwo256::hash(&buf[..])) - } -} - -impl contract::Trait for Runtime { - type Gas = u64; - type DetermineContractAddress = DetermineContractAddress; -} - -/// Contract module for this concrete runtime. -pub type Contract = contract::Module; - -impl_outer_event! { - pub enum Event for Runtime { - //consensus, - balances, - //timetstamp, - session, - staking, - democracy, - council, - council_voting, - council_motions, - treasury - } -} - -impl_outer_log! { - pub enum Log(InternalLog: DigestItem) for Runtime { - consensus(AuthoritiesChange) - } -} - -impl_outer_origin! { - pub enum Origin for Runtime { - council_motions - } -} - -impl_outer_dispatch! { - pub enum Call where origin: Origin { - Consensus, - Balances, - Timestamp, - Session, - Staking, - Democracy, - Council, - CouncilVoting, - CouncilMotions, - Treasury, - Contract, - } -} - -impl_outer_config! { - pub struct GenesisConfig for Runtime { - SystemConfig => system, - ConsensusConfig => consensus, - BalancesConfig => balances, - TimestampConfig => timestamp, - SessionConfig => session, - StakingConfig => staking, - DemocracyConfig => democracy, - CouncilConfig => council, - TreasuryConfig => treasury, - } -} - -type AllModules = ( - Consensus, - Balances, - Timestamp, - Session, - Staking, - Democracy, - Council, - CouncilVoting, - CouncilMotions, - Treasury, - Contract, -); - -impl_json_metadata!( - for Runtime with modules - system::Module with Storage, - consensus::Module with Storage, - balances::Module with Storage, - timestamp::Module with Storage, - session::Module with Storage, - staking::Module with Storage, - democracy::Module with Storage, - council::Module with Storage, - council_voting::Module with Storage, - council_motions::Module with Storage, - treasury::Module with Storage, - contract::Module with Storage, -); - -impl DigestItem for Log { - type AuthorityId = SessionKey; - - fn as_authorities_change(&self) -> Option<&[Self::AuthorityId]> { - match self.0 { - InternalLog::consensus(ref item) => item.as_authorities_change(), - } - } -} - -/// The address format for describing accounts. -pub use balances::address::Address as RawAddress; -/// The address format for describing accounts. -pub type Address = balances::Address; -/// Block header type as expected by this runtime. -pub type Header = generic::Header; -/// Block type as expected by this runtime. -pub type Block = generic::Block; -/// BlockId type as expected by this runtime. -pub type BlockId = generic::BlockId; -/// Unchecked extrinsic type as expected by this runtime. -pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; -/// Extrinsic type that has already been checked. -pub type CheckedExtrinsic = generic::CheckedExtrinsic; -/// Executive: handles dispatch to the various modules. -pub type Executive = executive::Executive; - -pub mod api { - impl_stubs!( - version => |()| super::VERSION, - json_metadata => |()| super::Runtime::json_metadata(), - authorities => |()| super::Consensus::authorities(), - initialise_block => |header| super::Executive::initialise_block(&header), - apply_extrinsic => |extrinsic| super::Executive::apply_extrinsic(extrinsic), - execute_block => |block| super::Executive::execute_block(block), - finalise_block => |()| super::Executive::finalise_block(), - inherent_extrinsics => |(inherent, spec_version)| super::inherent_extrinsics(inherent, spec_version), - validator_count => |()| super::Session::validator_count(), - validators => |()| super::Session::validators(), - timestamp => |()| super::Timestamp::get(), - random_seed => |()| super::System::random_seed(), - account_nonce => |account| super::System::account_nonce(&account), - lookup_address => |address| super::Balances::lookup_address(address) - ); -} - -/// Produces the list of inherent extrinsics. -fn inherent_extrinsics(data: InherentData, _spec_version: u32) -> Vec { - let make_inherent = |function| UncheckedExtrinsic { - signature: Default::default(), - function, - index: 0, - }; - - let mut inherent = vec![ - make_inherent(Call::Timestamp(TimestampCall::set(data.timestamp))), - ]; - - if !data.offline_indices.is_empty() { - inherent.push(make_inherent( - Call::Consensus(ConsensusCall::note_offline(data.offline_indices)) - )); - } - - inherent -} diff --git a/demo/runtime/wasm/Cargo.lock b/demo/runtime/wasm/Cargo.lock deleted file mode 100644 index 89017d1a91d4d..0000000000000 --- a/demo/runtime/wasm/Cargo.lock +++ /dev/null @@ -1,1103 +0,0 @@ -[[package]] -name = "arrayvec" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "nodrop 0.1.12 (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 = "bitflags" -version = "1.0.3" -source = "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.7 (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 = "byteorder" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "cfg-if" -version = "0.1.3" -source = "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 = "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-epoch" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.3 (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.0.1 (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.12 (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-utils" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.3 (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 = "demo-primitives" -version = "0.1.0" -dependencies = [ - "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-codec-derive 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-primitives 0.1.0", - "substrate-runtime-std 0.1.0", -] - -[[package]] -name = "demo-runtime" -version = "0.1.0" -dependencies = [ - "demo-primitives 0.1.0", - "integer-sqrt 0.1.0 (git+https://github.com/paritytech/integer-sqrt-rs.git)", - "safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-codec-derive 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-balances 0.1.0", - "substrate-runtime-consensus 0.1.0", - "substrate-runtime-contract 0.1.0", - "substrate-runtime-council 0.1.0", - "substrate-runtime-democracy 0.1.0", - "substrate-runtime-executive 0.1.0", - "substrate-runtime-io 0.1.0", - "substrate-runtime-primitives 0.1.0", - "substrate-runtime-session 0.1.0", - "substrate-runtime-staking 0.1.0", - "substrate-runtime-std 0.1.0", - "substrate-runtime-support 0.1.0", - "substrate-runtime-system 0.1.0", - "substrate-runtime-timestamp 0.1.0", - "substrate-runtime-treasury 0.1.0", - "substrate-runtime-version 0.1.0", -] - -[[package]] -name = "ed25519" -version = "0.1.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)", - "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-primitives 0.1.0", - "untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "elastic-array" -version = "0.10.0" -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 = "environmental" -version = "0.1.0" - -[[package]] -name = "ethbloom" -version = "0.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)", - "ethereum-types-serialize 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fixed-hash 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ethereum-types" -version = "0.4.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)", - "ethbloom 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types-serialize 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fixed-hash 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "uint 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ethereum-types-serialize" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "fixed-hash" -version = "0.2.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)", - "libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 1.0.0 (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.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)", -] - -[[package]] -name = "fuchsia-zircon-sys" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "gcc" -version = "0.3.54" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "hashdb" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "elastic-array 0.10.0 (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.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hex-literal" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "hex-literal-impl 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hex-literal-impl" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "integer-sqrt" -version = "0.1.0" -source = "git+https://github.com/paritytech/integer-sqrt-rs.git#886e9cb983c46498003878afe965d55caa762025" - -[[package]] -name = "lazy_static" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "lazy_static" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "libc" -version = "0.2.41" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "log" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "log" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.3 (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_units" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "memorydb" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "hashdb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "plain_hasher 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "nan-preserving-float" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "nodrop" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "num-traits" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "num_cpus" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.41 (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.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parity-bytes" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "parity-wasm" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parking_lot" -version = "0.4.8" -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_core" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "patricia-trie" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "hashdb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "plain_hasher" -version = "0.2.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)", -] - -[[package]] -name = "proc-macro-hack" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro-hack-impl 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "proc-macro-hack-impl" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "proc-macro2" -version = "0.4.4" -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 = "pwasm-alloc" -version = "0.1.0" -dependencies = [ - "pwasm-libc 0.1.0", - "rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "pwasm-libc" -version = "0.1.0" - -[[package]] -name = "pwasm-utils" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-wasm 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "quote" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand" -version = "0.3.22" -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)", - "libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand" -version = "0.4.2" -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)", - "libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rayon" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rayon-core 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rayon-core" -version = "1.4.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)", - "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ring" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rlp" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rustc-hex" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "rustc-hex" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "rustc_version" -version = "0.2.2" -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 = "safe-mix" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rustc_version 0.2.2 (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 = "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.64" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "serde_derive" -version = "1.0.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "smallvec" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "stable_deref_trait" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "substrate-codec" -version = "0.1.0" -dependencies = [ - "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-codec-derive" -version = "0.1.0" -dependencies = [ - "proc-macro2 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-keyring" -version = "0.1.0" -dependencies = [ - "ed25519 0.1.0", - "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-primitives" -version = "0.1.0" -dependencies = [ - "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "fixed-hash 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hashdb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "patricia-trie 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "plain_hasher 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-codec-derive 0.1.0", - "substrate-runtime-std 0.1.0", - "twox-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "uint 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-runtime-balances" -version = "0.1.0" -dependencies = [ - "hex-literal 0.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.64 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-codec-derive 0.1.0", - "substrate-keyring 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-io 0.1.0", - "substrate-runtime-primitives 0.1.0", - "substrate-runtime-std 0.1.0", - "substrate-runtime-support 0.1.0", - "substrate-runtime-system 0.1.0", -] - -[[package]] -name = "substrate-runtime-consensus" -version = "0.1.0" -dependencies = [ - "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-codec-derive 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-io 0.1.0", - "substrate-runtime-primitives 0.1.0", - "substrate-runtime-std 0.1.0", - "substrate-runtime-support 0.1.0", - "substrate-runtime-system 0.1.0", -] - -[[package]] -name = "substrate-runtime-contract" -version = "0.1.0" -dependencies = [ - "parity-wasm 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pwasm-utils 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-balances 0.1.0", - "substrate-runtime-io 0.1.0", - "substrate-runtime-primitives 0.1.0", - "substrate-runtime-sandbox 0.1.0", - "substrate-runtime-std 0.1.0", - "substrate-runtime-support 0.1.0", - "substrate-runtime-system 0.1.0", -] - -[[package]] -name = "substrate-runtime-council" -version = "0.1.0" -dependencies = [ - "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "integer-sqrt 0.1.0 (git+https://github.com/paritytech/integer-sqrt-rs.git)", - "safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-codec-derive 0.1.0", - "substrate-keyring 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-balances 0.1.0", - "substrate-runtime-consensus 0.1.0", - "substrate-runtime-democracy 0.1.0", - "substrate-runtime-io 0.1.0", - "substrate-runtime-primitives 0.1.0", - "substrate-runtime-std 0.1.0", - "substrate-runtime-support 0.1.0", - "substrate-runtime-system 0.1.0", -] - -[[package]] -name = "substrate-runtime-democracy" -version = "0.1.0" -dependencies = [ - "hex-literal 0.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.64 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-codec-derive 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-balances 0.1.0", - "substrate-runtime-consensus 0.1.0", - "substrate-runtime-io 0.1.0", - "substrate-runtime-primitives 0.1.0", - "substrate-runtime-std 0.1.0", - "substrate-runtime-support 0.1.0", - "substrate-runtime-system 0.1.0", -] - -[[package]] -name = "substrate-runtime-executive" -version = "0.1.0" -dependencies = [ - "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-runtime-io 0.1.0", - "substrate-runtime-primitives 0.1.0", - "substrate-runtime-std 0.1.0", - "substrate-runtime-support 0.1.0", - "substrate-runtime-system 0.1.0", -] - -[[package]] -name = "substrate-runtime-io" -version = "0.1.0" -dependencies = [ - "ed25519 0.1.0", - "environmental 0.1.0", - "hashdb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-std 0.1.0", - "substrate-state-machine 0.1.0", - "triehash 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-runtime-primitives" -version = "0.1.0" -dependencies = [ - "integer-sqrt 0.1.0 (git+https://github.com/paritytech/integer-sqrt-rs.git)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-codec-derive 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-io 0.1.0", - "substrate-runtime-std 0.1.0", - "substrate-runtime-support 0.1.0", -] - -[[package]] -name = "substrate-runtime-sandbox" -version = "0.1.0" -dependencies = [ - "rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-io 0.1.0", - "substrate-runtime-std 0.1.0", - "wasmi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-runtime-session" -version = "0.1.0" -dependencies = [ - "hex-literal 0.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.64 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-codec-derive 0.1.0", - "substrate-keyring 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-consensus 0.1.0", - "substrate-runtime-io 0.1.0", - "substrate-runtime-primitives 0.1.0", - "substrate-runtime-std 0.1.0", - "substrate-runtime-support 0.1.0", - "substrate-runtime-system 0.1.0", - "substrate-runtime-timestamp 0.1.0", -] - -[[package]] -name = "substrate-runtime-staking" -version = "0.1.0" -dependencies = [ - "hex-literal 0.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.64 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-codec-derive 0.1.0", - "substrate-keyring 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-balances 0.1.0", - "substrate-runtime-consensus 0.1.0", - "substrate-runtime-io 0.1.0", - "substrate-runtime-primitives 0.1.0", - "substrate-runtime-sandbox 0.1.0", - "substrate-runtime-session 0.1.0", - "substrate-runtime-std 0.1.0", - "substrate-runtime-support 0.1.0", - "substrate-runtime-system 0.1.0", - "substrate-runtime-timestamp 0.1.0", -] - -[[package]] -name = "substrate-runtime-std" -version = "0.1.0" -dependencies = [ - "pwasm-alloc 0.1.0", - "pwasm-libc 0.1.0", - "rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-runtime-support" -version = "0.1.0" -dependencies = [ - "ed25519 0.1.0", - "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-io 0.1.0", - "substrate-runtime-std 0.1.0", -] - -[[package]] -name = "substrate-runtime-system" -version = "0.1.0" -dependencies = [ - "hex-literal 0.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.64 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-codec-derive 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-io 0.1.0", - "substrate-runtime-primitives 0.1.0", - "substrate-runtime-std 0.1.0", - "substrate-runtime-support 0.1.0", -] - -[[package]] -name = "substrate-runtime-timestamp" -version = "0.1.0" -dependencies = [ - "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-consensus 0.1.0", - "substrate-runtime-io 0.1.0", - "substrate-runtime-primitives 0.1.0", - "substrate-runtime-std 0.1.0", - "substrate-runtime-support 0.1.0", - "substrate-runtime-system 0.1.0", -] - -[[package]] -name = "substrate-runtime-treasury" -version = "0.1.0" -dependencies = [ - "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-codec-derive 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-balances 0.1.0", - "substrate-runtime-io 0.1.0", - "substrate-runtime-primitives 0.1.0", - "substrate-runtime-std 0.1.0", - "substrate-runtime-support 0.1.0", - "substrate-runtime-system 0.1.0", -] - -[[package]] -name = "substrate-runtime-version" -version = "0.1.0" -dependencies = [ - "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-codec-derive 0.1.0", - "substrate-runtime-std 0.1.0", - "substrate-runtime-support 0.1.0", -] - -[[package]] -name = "substrate-state-machine" -version = "0.1.0" -dependencies = [ - "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "hashdb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "memorydb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "patricia-trie 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-primitives 0.1.0", - "triehash 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "syn" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.3 (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 = "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 = "triehash" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "hashdb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "twox-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "uint" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "crunchy 0.1.6 (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.0 (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 = "unreachable" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "untrusted" -version = "0.5.1" -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 = "wasmi" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "nan-preserving-float 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-wasm 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "winapi" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[metadata] -"checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" -"checksum base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83" -"checksum bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c54bb8f454c567f21197eefcdbf5679d0bd99f2ddbe52e84c77061952e6789" -"checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" -"checksum byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "74c0b906e9446b0a2e4f760cdb3fa4b2c48cdc6db8766a845c54b6ff063fd2e9" -"checksum cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "405216fd8fe65f718daa7102ea808a946b6ce40c742998fbfd3463645552de18" -"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e" -"checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" -"checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150" -"checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" -"checksum crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" -"checksum elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "88d4851b005ef16de812ea9acdb7bece2f0a40dd86c07b85631d7dafa54537bb" -"checksum ethbloom 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a93a43ce2e9f09071449da36bfa7a1b20b950ee344b6904ff23de493b03b386" -"checksum ethereum-types 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "35b3c5a18bc5e73a32a110ac743ec04b02bbbcd3b71d3118d40a6113d509378a" -"checksum ethereum-types-serialize 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ac59a21a9ce98e188f3dace9eb67a6c4a3c67ec7fbc7218cb827852679dc002" -"checksum fixed-hash 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0d5ec8112f00ea8a483e04748a85522184418fd1cf02890b626d8fc28683f7de" -"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 gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb" -"checksum hashdb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f1c71fc577cde89b3345d5f2880fecaf462a32e96c619f431279bdaf1ba5ddb1" -"checksum heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1679e6ea370dee694f91f1dc469bf94cf8f52051d147aec3e1f9497c6fc22461" -"checksum hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4da5f0e01bd8a71a224a4eedecaacfcabda388dbb7a80faf04d3514287572d95" -"checksum hex-literal-impl 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1d340b6514f232f6db1bd16db65302a5278a04fef9ce867cb932e7e5fa21130a" -"checksum integer-sqrt 0.1.0 (git+https://github.com/paritytech/integer-sqrt-rs.git)" = "" -"checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" -"checksum lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e6412c5e2ad9584b0b8e979393122026cdd6d2a80b933f890dcd694ddbe73739" -"checksum libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)" = "ac8ebf8343a981e2fa97042b14768f02ed3e1d602eac06cae6166df3c8ced206" -"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" -"checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2" -"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" -"checksum memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" -"checksum memorydb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f72c93304ad51e21230ecbd0d2b58a3f94703bf9339d14aed88c3aaf5e8b7a56" -"checksum nan-preserving-float 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34d4f00fcc2f4c9efa8cc971db0da9e28290e28e97af47585e48691ef10ff31f" -"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" -"checksum num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "775393e285254d2f5004596d69bb8bc1149754570dcc08cf30cabeba67955e28" -"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" -"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" -"checksum parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa5168b4cf41f3835e4bc6ffb32f51bc9365dc50cb351904595b3931d917fd0c" -"checksum parity-wasm 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1c91199d14bd5b78ecade323d4a891d094799749c1b9e82d9c590c2e2849a40" -"checksum parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "149d8f5b97f3c1133e3cfcd8886449959e856b557ff281e292b733d7c69e005e" -"checksum parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4db1a8ccf734a7bce794cc19b3df06ed87ab2f3907036b693c68f56b4d4537fa" -"checksum patricia-trie 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fa27fc4a972a03d64e5170d7facd2c84c6ed425b38ce62ad98dcfee2f7845b3b" -"checksum plain_hasher 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "95fa6386b1d34aaf0adb9b7dd2885dbe7c34190e6263785e5a7ec2b19044a90f" -"checksum proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ba8d4f9257b85eb6cdf13f055cea3190520aab1409ca2ab43493ea4820c25f0" -"checksum proc-macro-hack-impl 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d5cb6f960ad471404618e9817c0e5d10b1ae74cfdf01fab89ea0641fe7fb2892" -"checksum proc-macro2 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1fa93823f53cfd0f5ac117b189aed6cfdfb2cfc0a9d82e956dd7927595ed7d46" -"checksum pwasm-utils 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "efd695333cfae6e9dbe2703a6d040e252b57a6fc3b9a65c712615ac042b2e0c5" -"checksum quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e44651a0dc4cdd99f71c83b561e221f714912d11af1a4dff0631f923d53af035" -"checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1" -"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" -"checksum rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b614fe08b6665cb9a231d07ac1364b0ef3cb3698f1239ee0c4c3a88a524f54c8" -"checksum rayon-core 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d24ad214285a7729b174ed6d3bcfcb80177807f959d95fafd5bfc5c4f201ac8" -"checksum ring 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6f7d28b30a72c01b458428e0ae988d4149c20d902346902be881e3edc4bb325c" -"checksum rlp 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "524c5ad554859785dfc8469df3ed5e0b5784d4d335877ed47c8d90fc0eb238fe" -"checksum rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0ceb8ce7a5e520de349e1fa172baeba4a9e8d5ef06c47471863530bc4972ee1e" -"checksum rustc-hex 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d2b03280c2813907a030785570c577fb27d3deec8da4c18566751ade94de0ace" -"checksum rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a54aa04a10c68c1c4eacb4337fd883b435997ede17a9385784b990777686b09a" -"checksum safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7f7bf422d23a88c16d5090d455f182bc99c60af4df6a345c63428acf5129e347" -"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" -"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.64 (registry+https://github.com/rust-lang/crates.io-index)" = "fba5be06346c5200249c8c8ca4ccba4a09e8747c71c16e420bd359a0db4d8f91" -"checksum serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)" = "79e4620ba6fbe051fc7506fab6f84205823564d55da18d55b695160fb3479cd8" -"checksum smallvec 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "211a489e65e94b103926d2054ae515a1cdb5d515ea0ef414fee23b7e043ce748" -"checksum stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15132e0e364248108c5e2c02e3ab539be8d6f5d52a01ca9bbf27ed657316f02b" -"checksum syn 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6dfd71b2be5a58ee30a6f8ea355ba8290d397131c00dfa55c3d34e6e13db5101" -"checksum tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e9175261fbdb60781fcd388a4d6cc7e14764a2b629a7ad94abb439aed223a44f" -"checksum triehash 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3da77dc2c88bac48769c53f2c7675d99d522a7fc8130da3fadf29d7c6f94c9ac" -"checksum twox-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "475352206e7a290c5fccc27624a163e8d0d115f7bb60ca18a64fc9ce056d7435" -"checksum uint 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "754ba11732b9161b94c41798e5197e5e75388d012f760c42adb5000353e98646" -"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" -"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" -"checksum untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f392d7819dbe58833e26872f5f6f0d68b7bbbe90fc3667e98731c4a15ad9a7ae" -"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum wasmi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "522fe3fdd44a56f25cd5ddcd8ccdb1cf2e982ceb28fcb00f41d8a018ae5245a8" -"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/demo/runtime/wasm/Cargo.toml b/demo/runtime/wasm/Cargo.toml deleted file mode 100644 index 198e8c6280ad9..0000000000000 --- a/demo/runtime/wasm/Cargo.toml +++ /dev/null @@ -1,63 +0,0 @@ -[package] -name = "demo-runtime" -version = "0.1.0" -authors = ["Parity Technologies "] - -[lib] -crate-type = ["cdylib"] - -[dependencies] -integer-sqrt = { git = "https://github.com/paritytech/integer-sqrt-rs.git", branch = "master" } -safe-mix = { version = "1.0", default_features = false} -substrate-codec-derive = { path = "../../../substrate/codec/derive" } -substrate-codec = { path = "../../../substrate/codec", default-features = false } -substrate-primitives = { path = "../../../substrate/primitives", default-features = false } -substrate-runtime-std = { path = "../../../substrate/runtime-std", default-features = false } -substrate-runtime-io = { path = "../../../substrate/runtime-io", default-features = false } -substrate-runtime-support = { path = "../../../substrate/runtime-support", default-features = false } -substrate-runtime-balances = { path = "../../../substrate/runtime/balances", default-features = false } -substrate-runtime-consensus = { path = "../../../substrate/runtime/consensus", default-features = false } -substrate-runtime-contract = { path = "../../../substrate/runtime/contract", default-features = false } -substrate-runtime-council = { path = "../../../substrate/runtime/council", default-features = false } -substrate-runtime-democracy = { path = "../../../substrate/runtime/democracy", default-features = false } -substrate-runtime-executive = { path = "../../../substrate/runtime/executive", default-features = false } -substrate-runtime-primitives = { path = "../../../substrate/runtime/primitives", default-features = false } -substrate-runtime-session = { path = "../../../substrate/runtime/session", default-features = false } -substrate-runtime-staking = { path = "../../../substrate/runtime/staking", default-features = false } -substrate-runtime-system = { path = "../../../substrate/runtime/system", default-features = false } -substrate-runtime-timestamp = { path = "../../../substrate/runtime/timestamp", default-features = false } -substrate-runtime-treasury = { path = "../../../substrate/runtime/treasury", default-features = false } -substrate-runtime-version = { path = "../../../substrate/runtime/version", default-features = false } -demo-primitives = { path = "../../primitives", default-features = false } - -[features] -default = [] -std = [ - "safe-mix/std", - "substrate-codec/std", - "substrate-primitives/std", - "substrate-runtime-std/std", - "substrate-runtime-io/std", - "substrate-runtime-support/std", - "substrate-runtime-balances/std", - "substrate-runtime-consensus/std", - "substrate-runtime-contract/std", - "substrate-runtime-council/std", - "substrate-runtime-democracy/std", - "substrate-runtime-executive/std", - "substrate-runtime-primitives/std", - "substrate-runtime-session/std", - "substrate-runtime-staking/std", - "substrate-runtime-system/std", - "substrate-runtime-timestamp/std", - "substrate-runtime-treasury/std", - "substrate-runtime-version/std", - "demo-primitives/std", -] - -[profile.release] -panic = "abort" -lto = true - -[workspace] -members = [] diff --git a/demo/runtime/wasm/build.sh b/demo/runtime/wasm/build.sh deleted file mode 100755 index 0769faeba9b3e..0000000000000 --- a/demo/runtime/wasm/build.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -set -e - -cargo +nightly build --target=wasm32-unknown-unknown --release -for i in demo_runtime -do - wasm-gc target/wasm32-unknown-unknown/release/$i.wasm target/wasm32-unknown-unknown/release/$i.compact.wasm -done diff --git a/demo/runtime/wasm/src b/demo/runtime/wasm/src deleted file mode 120000 index 5cd551cf2693e..0000000000000 --- a/demo/runtime/wasm/src +++ /dev/null @@ -1 +0,0 @@ -../src \ No newline at end of file diff --git a/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm b/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm deleted file mode 100644 index 0561462684fe86045a8c2dc760a34ed0c52b8321..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 696725 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

|5sjN{gxb?%?P@Ae13y0>o5+`>s;ye}7> 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

hIV*69qW?g>?rtE}9WuS4qbZYjC+eMl`n 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 diff --git a/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm b/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm deleted file mode 100755 index d0cf943ec482fda16f3d24fcefb30f7a0d67bda9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 696823 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"] - -[dependencies] -parking_lot = "0.4" -error-chain = "0.12" -lazy_static = "1.0" -log = "0.3" -slog = "^2" -tokio = "0.1.7" -hex-literal = "0.1" -ed25519 = { path = "../../substrate/ed25519" } -demo-api = { path = "../api" } -demo-primitives = { path = "../primitives" } -demo-runtime = { path = "../runtime" } -demo-executor = { path = "../executor" } -demo-consensus = { path = "../consensus" } -demo-network = { path = "../network" } -demo-transaction-pool = { path = "../transaction-pool" } -substrate-runtime-io = { path = "../../substrate/runtime-io" } -substrate-primitives = { path = "../../substrate/primitives" } -substrate-network = { path = "../../substrate/network" } -substrate-client = { path = "../../substrate/client" } -substrate-service = { path = "../../substrate/service" } -substrate-telemetry = { path = "../../substrate/telemetry" } diff --git a/demo/service/src/chain_spec.rs b/demo/service/src/chain_spec.rs deleted file mode 100644 index bd232ce1a124c..0000000000000 --- a/demo/service/src/chain_spec.rs +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. - -// Substrate Demo 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 Demo 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 Demo. If not, see . - -//! Substrate Demo chain configurations. - -use ed25519; -use primitives::AuthorityId; -use demo_runtime::{GenesisConfig, ConsensusConfig, CouncilConfig, DemocracyConfig, - SessionConfig, StakingConfig, TimestampConfig, BalancesConfig, TreasuryConfig, Permill}; -use service::ChainSpec; - -const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/"; - -pub fn testnet_config() -> Result, String> { - //ChainSpec::from_embedded(include_bytes!("../res/demo.json")) - Ok(staging_testnet_config()) -} - -fn staging_testnet_config_genesis() -> GenesisConfig { - let initial_authorities = vec![ - hex!["82c39b31a2b79a90f8e66e7a77fdb85a4ed5517f2ae39f6a80565e8ecae85cf5"].into(), - hex!["4de37a07567ebcbf8c64568428a835269a566723687058e017b6d69db00a77e7"].into(), - hex!["063d7787ebca768b7445dfebe7d62cbb1625ff4dba288ea34488da266dd6dca5"].into(), - hex!["8101764f45778d4980dadaceee6e8af2517d3ab91ac9bec9cd1714fa5994081c"].into(), - ]; - let endowed_accounts = vec![ - hex!["f295940fa750df68a686fcf4abd4111c8a9c5a5a5a83c4c8639c451a94a7adfd"].into(), - ]; - GenesisConfig { - consensus: Some(ConsensusConfig { - code: include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm").to_vec(), // TODO change - authorities: initial_authorities.clone(), - }), - system: None, - balances: Some(BalancesConfig { - transaction_base_fee: 100, - transaction_byte_fee: 1, - existential_deposit: 500, - transfer_fee: 0, - creation_fee: 0, - reclaim_rebate: 0, - balances: endowed_accounts.iter().map(|&k|(k, 1u64 << 60)).collect(), - }), - session: Some(SessionConfig { - validators: initial_authorities.iter().cloned().map(Into::into).collect(), - session_length: 60, // that's 5 minutes per session. - }), - staking: Some(StakingConfig { - current_era: 0, - intentions: initial_authorities.iter().cloned().map(Into::into).collect(), - offline_slash: 10000, - session_reward: 100, - validator_count: 12, - sessions_per_era: 12, // 1 hour per era - bonding_duration: 24 * 60 * 12, // 1 day per bond. - offline_slash_grace: 4, - minimum_validator_count: 4, - }), - democracy: Some(DemocracyConfig { - launch_period: 12 * 60 * 24, // 1 day per public referendum - voting_period: 12 * 60 * 24 * 3, // 3 days to discuss & vote on an active referendum - minimum_deposit: 5000, // 12000 as the minimum deposit for a referendum - }), - council: Some(CouncilConfig { - active_council: vec![], - candidacy_bond: 5000, // 5000 to become a council candidate - voter_bond: 1000, // 1000 down to vote for a candidate - present_slash_per_voter: 1, // slash by 1 per voter for an invalid presentation. - carry_count: 6, // carry over the 6 runners-up to the next council election - presentation_duration: 12 * 60 * 24, // one day for presenting winners. - approval_voting_period: 12 * 60 * 24 * 2, // two days period between possible council elections. - term_duration: 12 * 60 * 24 * 24, // 24 day term duration for the council. - desired_seats: 0, // start with no council: we'll raise this once the stake has been dispersed a bit. - inactive_grace_period: 1, // one addition vote should go by before an inactive voter can be reaped. - - cooloff_period: 12 * 60 * 24 * 4, // 4 day cooling off period if council member vetoes a proposal. - voting_period: 12 * 60 * 24, // 1 day voting period for council members. - }), - timestamp: Some(TimestampConfig { - period: 5, // 5 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), - }), - } -} - -/// Staging testnet config. -pub fn staging_testnet_config() -> ChainSpec { - let boot_nodes = vec![]; - ChainSpec::from_genesis( - "Staging Testnet", - "staging_testnet", - staging_testnet_config_genesis, - boot_nodes, - Some(STAGING_TELEMETRY_URL.into()), - ) -} - -fn testnet_genesis(initial_authorities: Vec) -> GenesisConfig { - let endowed_accounts = vec![ - ed25519::Pair::from_seed(b"Alice ").public().0.into(), - ed25519::Pair::from_seed(b"Bob ").public().0.into(), - ed25519::Pair::from_seed(b"Charlie ").public().0.into(), - ed25519::Pair::from_seed(b"Dave ").public().0.into(), - ed25519::Pair::from_seed(b"Eve ").public().0.into(), - ed25519::Pair::from_seed(b"Ferdie ").public().0.into(), - ]; - GenesisConfig { - consensus: Some(ConsensusConfig { - code: include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm").to_vec(), - authorities: initial_authorities.clone(), - }), - system: None, - balances: Some(BalancesConfig { - transaction_base_fee: 1, - transaction_byte_fee: 0, - existential_deposit: 500, - transfer_fee: 0, - creation_fee: 0, - reclaim_rebate: 0, - balances: endowed_accounts.iter().map(|&k|(k, (1u64 << 60))).collect(), - }), - session: Some(SessionConfig { - validators: initial_authorities.iter().cloned().map(Into::into).collect(), - session_length: 10, - }), - staking: Some(StakingConfig { - current_era: 0, - intentions: initial_authorities.iter().cloned().map(Into::into).collect(), - minimum_validator_count: 1, - validator_count: 2, - sessions_per_era: 5, - bonding_duration: 2 * 60 * 12, - offline_slash: 0, - session_reward: 0, - offline_slash_grace: 0, - }), - democracy: Some(DemocracyConfig { - launch_period: 9, - voting_period: 18, - minimum_deposit: 10, - }), - council: Some(CouncilConfig { - active_council: endowed_accounts.iter() - .filter(|a| initial_authorities.iter().find(|&b| a.0 == b.0).is_none()) - .map(|a| (a.clone(), 1000000)).collect(), - candidacy_bond: 10, - voter_bond: 2, - present_slash_per_voter: 1, - carry_count: 4, - presentation_duration: 10, - approval_voting_period: 20, - term_duration: 1000000, - desired_seats: (endowed_accounts.len() - initial_authorities.len()) as u32, - inactive_grace_period: 1, - - cooloff_period: 75, - voting_period: 20, - }), - timestamp: Some(TimestampConfig { - period: 5, // 5 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), - }), - } -} - -fn development_config_genesis() -> GenesisConfig { - testnet_genesis(vec![ - ed25519::Pair::from_seed(b"Alice ").public().into(), - ]) -} - -/// Development config (single validator Alice) -pub fn development_config() -> ChainSpec { - ChainSpec::from_genesis("Development", "development", development_config_genesis, vec![], None) -} - -fn local_testnet_genesis() -> GenesisConfig { - testnet_genesis(vec![ - ed25519::Pair::from_seed(b"Alice ").public().into(), - ed25519::Pair::from_seed(b"Bob ").public().into(), - ]) -} - -/// Local testnet config (multivalidator Alice + Bob) -pub fn local_testnet_config() -> ChainSpec { - ChainSpec::from_genesis("Local Testnet", "local_testnet", local_testnet_genesis, vec![], None) -} diff --git a/demo/service/src/lib.rs b/demo/service/src/lib.rs deleted file mode 100644 index 3f761b7542bc4..0000000000000 --- a/demo/service/src/lib.rs +++ /dev/null @@ -1,214 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. - -// Substrate Demo 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 Demo 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 Demo. If not, see . - -#![warn(unused_extern_crates)] - -//! Substrate Demo service. Specialized wrapper over substrate service. - -extern crate ed25519; -extern crate demo_api; -extern crate demo_primitives; -extern crate demo_runtime; -extern crate demo_executor; -extern crate demo_network; -extern crate demo_transaction_pool as transaction_pool; -extern crate demo_consensus as consensus; -extern crate substrate_primitives as primitives; -extern crate substrate_network as network; -extern crate substrate_client as client; -extern crate substrate_service as service; -extern crate tokio; - -#[macro_use] -extern crate log; -#[macro_use] -extern crate hex_literal; - -pub mod chain_spec; - -use std::sync::Arc; - -use transaction_pool::TransactionPool; -use demo_api::Api; -use demo_primitives::{Block, Hash}; -use demo_runtime::GenesisConfig; -use client::Client; -use demo_network::{Protocol as DemoProtocol, consensus::ConsensusNetwork}; -use tokio::runtime::TaskExecutor; -use service::FactoryFullConfiguration; -use primitives::{Blake2Hasher, RlpCodec}; - -pub use service::{Roles, PruningMode, ExtrinsicPoolOptions, - ErrorKind, Error, ComponentBlock, LightComponents, FullComponents}; -pub use client::ExecutionStrategy; - -/// Specialised `ChainSpec`. -pub type ChainSpec = service::ChainSpec; -/// Client type for specialised `Components`. -pub type ComponentClient = Client<::Backend, ::Executor, Block>; -pub type NetworkService = network::Service::NetworkProtocol, Hash>; - -/// A collection of type to generalise specific components over full / light client. -pub trait Components: service::Components { - /// Demo API. - type Api: 'static + Api + Send + Sync; - /// Client backend. - type Backend: 'static + client::backend::Backend; - /// Client executor. - type Executor: 'static + client::CallExecutor + Send + Sync; -} - -impl Components for service::LightComponents { - type Api = service::LightClient; - type Executor = service::LightExecutor; - type Backend = service::LightBackend; -} - -impl Components for service::FullComponents { - type Api = service::FullClient; - type Executor = service::FullExecutor; - type Backend = service::FullBackend; -} - -/// All configuration for the node. -pub type Configuration = FactoryFullConfiguration; - -/// Demo-specific configuration. -#[derive(Default)] -pub struct CustomConfiguration; - -/// Config for the substrate service. -pub struct Factory; - -impl service::ServiceFactory for Factory { - type Block = Block; - type ExtrinsicHash = Hash; - type NetworkProtocol = DemoProtocol; - type RuntimeDispatch = demo_executor::Executor; - type FullExtrinsicPoolApi = transaction_pool::ChainApi>; - type LightExtrinsicPoolApi = transaction_pool::ChainApi>; - type Genesis = GenesisConfig; - type Configuration = CustomConfiguration; - - const NETWORK_PROTOCOL_ID: network::ProtocolId = ::demo_network::PROTOCOL_ID; - - fn build_full_extrinsic_pool(config: ExtrinsicPoolOptions, client: Arc>) - -> Result>, Error> - { - Ok(TransactionPool::new(config, transaction_pool::ChainApi::new(client))) - } - - fn build_light_extrinsic_pool(config: ExtrinsicPoolOptions, client: Arc>) - -> Result>, Error> - { - Ok(TransactionPool::new(config, transaction_pool::ChainApi::new(client))) - } - - fn build_network_protocol(_config: &Configuration) - -> Result - { - Ok(DemoProtocol::new()) - } -} - -/// Demo service. -pub struct Service { - inner: service::Service, - client: Arc>, - network: Arc, - api: Arc<::Api>, - _consensus: Option, -} - -impl Service { - pub fn client(&self) -> Arc> { - self.client.clone() - } - - pub fn network(&self) -> Arc { - self.network.clone() - } - - pub fn api(&self) -> Arc<::Api> { - self.api.clone() - } -} - -/// Creates light client and register protocol with the network service -pub fn new_light(config: Configuration, executor: TaskExecutor) - -> Result>, Error> -{ - let service = service::Service::>::new(config, executor.clone())?; - let api = service.client(); - Ok(Service { - client: service.client(), - network: service.network(), - api: api, - inner: service, - _consensus: None, - }) -} - -/// Creates full client and register protocol with the network service -pub fn new_full(config: Configuration, executor: TaskExecutor) - -> Result>, Error> -{ - let is_validator = (config.roles & Roles::AUTHORITY) == Roles::AUTHORITY; - let service = service::Service::>::new(config, executor.clone())?; - // Spin consensus service if configured - let consensus = if is_validator { - // Load the first available key - let key = service.keystore().load(&service.keystore().contents()?[0], "")?; - info!("Using authority key {}", key.public()); - - let client = service.client(); - - let consensus_net = ConsensusNetwork::new(service.network(), client.clone()); - Some(consensus::Service::new( - client.clone(), - client.clone(), - consensus_net, - service.extrinsic_pool(), - executor, - key, - )) - } else { - None - }; - - Ok(Service { - client: service.client(), - network: service.network(), - api: service.client(), - inner: service, - _consensus: consensus, - }) -} - -/// Creates bare client without any networking. -pub fn new_client(config: Configuration) - -> Result>>, Error> -{ - service::new_client::(config) -} - -impl ::std::ops::Deref for Service { - type Target = service::Service; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} diff --git a/demo/src/main.rs b/demo/src/main.rs deleted file mode 100644 index 7b219728c38c7..0000000000000 --- a/demo/src/main.rs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. - -// Substrate Demo 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 Demo 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 Demo. If not, see . - -//! Substrate Demo CLI - -#![warn(missing_docs)] - -extern crate demo_cli as cli; -extern crate ctrlc; -extern crate futures; - -#[macro_use] -extern crate error_chain; - -use cli::VersionInfo; -use futures::sync::oneshot; -use futures::{future, Future}; - -use std::cell::RefCell; - -mod vergen { - #![allow(unused)] - include!(concat!(env!("OUT_DIR"), "/version.rs")); -} - -// handles ctrl-c -struct Exit; -impl cli::IntoExit for Exit { - type Exit = future::MapErr, fn(oneshot::Canceled) -> ()>; - fn into_exit(self) -> Self::Exit { - // can't use signal directly here because CtrlC takes only `Fn`. - let (exit_send, exit) = oneshot::channel(); - - let exit_send_cell = RefCell::new(Some(exit_send)); - ctrlc::set_handler(move || { - if let Some(exit_send) = exit_send_cell.try_borrow_mut().expect("signal handler not reentrant; qed").take() { - exit_send.send(()).expect("Error sending exit notification"); - } - }).expect("Error setting Ctrl-C handler"); - - exit.map_err(drop) - } -} - -quick_main!(run); - -fn run() -> cli::error::Result<()> { - let version = VersionInfo { - commit: vergen::short_sha(), - version: env!("CARGO_PKG_VERSION"), - executable_name: "substrate", - author: "Parity Team ", - description: "Generic substrate node", - }; - cli::run(::std::env::args(), Exit, version) -} diff --git a/demo/transaction-pool/Cargo.toml b/demo/transaction-pool/Cargo.toml deleted file mode 100644 index 94eaae111ae7d..0000000000000 --- a/demo/transaction-pool/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "demo-transaction-pool" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -log = "0.3.0" -error-chain = "0.12" -parking_lot = "0.4" -demo-api = { path = "../api" } -demo-primitives = { path = "../primitives" } -demo-runtime = { path = "../runtime" } -substrate-client = { path = "../../substrate/client" } -substrate-codec = { path = "../../substrate/codec" } -substrate-keyring = { path = "../../substrate/keyring" } -substrate-extrinsic-pool = { path = "../../substrate/extrinsic-pool" } -substrate-primitives = { path = "../../substrate/primitives" } -substrate-runtime-primitives = { path = "../../substrate/runtime/primitives" } -ed25519 = { path = "../../substrate/ed25519" } diff --git a/demo/transaction-pool/src/error.rs b/demo/transaction-pool/src/error.rs deleted file mode 100644 index 2303372084e13..0000000000000 --- a/demo/transaction-pool/src/error.rs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. - -// Substrate Demo 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 Demo 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 Demo. If not, see . - -use extrinsic_pool; -use demo_api; -use primitives::Hash; -use runtime::{Address, UncheckedExtrinsic}; - -error_chain! { - links { - Pool(extrinsic_pool::Error, extrinsic_pool::ErrorKind); - Api(demo_api::Error, demo_api::ErrorKind); - } - errors { - /// Unexpected extrinsic format submitted - InvalidExtrinsicFormat { - description("Invalid extrinsic format."), - display("Invalid extrinsic format."), - } - /// Attempted to queue an inherent transaction. - IsInherent(xt: UncheckedExtrinsic) { - description("Inherent transactions cannot be queued."), - display("Inherent transactions cannot be queued."), - } - /// Attempted to queue a transaction with bad signature. - BadSignature(e: &'static str) { - description("Transaction had bad signature."), - display("Transaction had bad signature: {}", e), - } - /// Attempted to queue a transaction that is already in the pool. - AlreadyImported(hash: Hash) { - description("Transaction is already in the pool."), - display("Transaction {:?} is already in the pool.", hash), - } - /// Import error. - Import(err: Box<::std::error::Error + Send>) { - description("Error importing transaction"), - display("Error importing transaction: {}", err.description()), - } - /// Runtime failure. - UnrecognisedAddress(who: Address) { - description("Unrecognised address in extrinsic"), - display("Unrecognised address in extrinsic: {}", who), - } - /// Extrinsic too large - TooLarge(got: usize, max: usize) { - description("Extrinsic too large"), - display("Extrinsic is too large ({} > {})", got, max), - } - } -} - -impl extrinsic_pool::IntoPoolError for Error { - fn into_pool_error(self) -> ::std::result::Result { - match self { - Error(ErrorKind::Pool(e), c) => Ok(extrinsic_pool::Error(e, c)), - e => Err(e), - } - } -} diff --git a/demo/transaction-pool/src/lib.rs b/demo/transaction-pool/src/lib.rs deleted file mode 100644 index 637356a098fb5..0000000000000 --- a/demo/transaction-pool/src/lib.rs +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. - -// Substrate Demo 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 Demo 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 Demo. If not, see . - -extern crate ed25519; -extern crate substrate_client as client; -extern crate substrate_codec as codec; -extern crate substrate_extrinsic_pool as extrinsic_pool; -extern crate substrate_primitives; -extern crate substrate_runtime_primitives; -extern crate demo_runtime as runtime; -extern crate demo_primitives as primitives; -extern crate demo_api; -extern crate parking_lot; - -#[cfg(test)] -extern crate substrate_keyring; - -#[macro_use] -extern crate error_chain; - -#[macro_use] -extern crate log; - -mod error; - -use std::{ - cmp::Ordering, - collections::HashMap, - sync::Arc, -}; - -use codec::{Decode, Encode}; -use extrinsic_pool::{Readiness, scoring::{Change, Choice}, VerifiedFor, ExtrinsicFor}; -use demo_api::Api; -use primitives::{AccountId, BlockId, Block, Hash, Index}; -use runtime::{Address, UncheckedExtrinsic, RawAddress}; -use substrate_runtime_primitives::traits::{Bounded, Checkable, Hash as HashT, BlakeTwo256}; - -pub use extrinsic_pool::{Options, Status, LightStatus, VerifiedTransaction as VerifiedTransactionOps}; -pub use error::{Error, ErrorKind, Result}; - -/// Maximal size of a single encoded extrinsic. -const MAX_TRANSACTION_SIZE: usize = 4 * 1024 * 1024; - -/// Type alias for convenience. -pub type CheckedExtrinsic = std::result::Result>>::Checked; - -/// Type alias for the transaction pool. -pub type TransactionPool = extrinsic_pool::Pool>; - -/// A verified transaction which should be includable and non-inherent. -#[derive(Clone, Debug)] -pub struct VerifiedTransaction { - /// Transaction hash. - pub hash: Hash, - /// Transaction sender. - pub sender: AccountId, - /// Transaction index. - pub index: Index, - encoded_size: usize, -} - -impl VerifiedTransaction { - /// Get the 256-bit hash of this transaction. - pub fn hash(&self) -> &Hash { - &self.hash - } - - /// Get the account ID of the sender of this transaction. - pub fn index(&self) -> Index { - self.index - } - - /// Get encoded size of the transaction. - pub fn encoded_size(&self) -> usize { - self.encoded_size - } -} - -impl extrinsic_pool::VerifiedTransaction for VerifiedTransaction { - type Hash = Hash; - type Sender = AccountId; - - fn hash(&self) -> &Self::Hash { - &self.hash - } - - fn sender(&self) -> &Self::Sender { - &self.sender - } - - fn mem_usage(&self) -> usize { - self.encoded_size // TODO - } -} - -/// The transaction pool logic. -pub struct ChainApi { - api: Arc, -} - -impl ChainApi where - A: Api, -{ - /// Create a new instance. - pub fn new(api: Arc) -> Self { - ChainApi { - api, - } - } -} - - -impl extrinsic_pool::ChainApi for ChainApi where - A: Api + Send + Sync, -{ - type Block = Block; - type Hash = Hash; - type Sender = AccountId; - type VEx = VerifiedTransaction; - type Ready = HashMap; - type Error = Error; - type Score = u64; - type Event = (); - - fn verify_transaction(&self, _at: &BlockId, xt: &ExtrinsicFor) -> Result { - let encoded = xt.encode(); - let uxt = UncheckedExtrinsic::decode(&mut encoded.as_slice()).ok_or_else(|| ErrorKind::InvalidExtrinsicFormat)?; - if !uxt.is_signed() { - bail!(ErrorKind::IsInherent(uxt)) - } - - let (encoded_size, hash) = (encoded.len(), BlakeTwo256::hash(&encoded)); - if encoded_size > MAX_TRANSACTION_SIZE { - bail!(ErrorKind::TooLarge(encoded_size, MAX_TRANSACTION_SIZE)); - } - - debug!(target: "transaction-pool", "Transaction submitted: {}", ::substrate_primitives::hexdisplay::HexDisplay::from(&encoded)); - let checked = uxt.clone().check_with(|a| { - match a { - RawAddress::Id(id) => Ok(id), - RawAddress::Index(_) => Err("Index based addresses are not supported".into()),// TODO: Make index addressing optional in substrate - } - })?; - let sender = checked.signed.expect("Only signed extrinsics are allowed at this point"); - - - if encoded_size < 1024 { - debug!(target: "transaction-pool", "Transaction verified: {} => {:?}", hash, uxt); - } else { - debug!(target: "transaction-pool", "Transaction verified: {} ({} bytes is too large to display)", hash, encoded_size); - } - - Ok(VerifiedTransaction { - index: checked.index, - sender, - hash, - encoded_size, - }) - } - - fn ready(&self) -> Self::Ready { - HashMap::default() - } - - fn is_ready(&self, at: &BlockId, known_nonces: &mut Self::Ready, xt: &VerifiedFor) -> Readiness { - let sender = xt.verified.sender().clone(); - trace!(target: "transaction-pool", "Checking readiness of {} (from {})", xt.verified.hash, sender); - - // TODO: find a way to handle index error properly -- will need changes to - // transaction-pool trait. - let api = &self.api; - let next_index = known_nonces.entry(sender) - .or_insert_with(|| api.index(at, sender).ok().unwrap_or_else(Bounded::max_value)); - - trace!(target: "transaction-pool", "Next index for sender is {}; xt index is {}", next_index, xt.verified.index); - - let result = match xt.verified.index.cmp(&next_index) { - // TODO: this won't work perfectly since accounts can now be killed, returning the nonce - // to zero. - // We should detect if the index was reset and mark all transactions as `Stale` for cull to work correctly. - // Otherwise those transactions will keep occupying the queue. - // Perhaps we could mark as stale if `index - state_index` > X? - Ordering::Greater => Readiness::Future, - Ordering::Equal => Readiness::Ready, - // TODO [ToDr] Should mark transactions referencing too old blockhash as `Stale` as well. - Ordering::Less => Readiness::Stale, - }; - - // remember to increment `next_index` - *next_index = next_index.saturating_add(1); - - result - } - - fn compare(old: &VerifiedFor, other: &VerifiedFor) -> Ordering { - old.verified.index().cmp(&other.verified.index()) - } - - fn choose(old: &VerifiedFor, new: &VerifiedFor) -> Choice { - if old.verified.index() == new.verified.index() { - return Choice::ReplaceOld; - } - Choice::InsertNew - } - - fn update_scores( - xts: &[extrinsic_pool::Transaction>], - scores: &mut [Self::Score], - _change: Change<()> - ) { - for i in 0..xts.len() { - // all the same score since there are no fees. - // TODO: prioritize things like misbehavior or fishermen reports - scores[i] = 1; - } - } - - fn should_replace(_old: &VerifiedFor, _new: &VerifiedFor) -> Choice { - // Don't allow new transactions if we are reaching the limit. - Choice::RejectNew - } -} - diff --git a/doc/packages/substrate.adoc b/doc/packages/substrate.adoc index 75f391e2b6409..e4c80e63c2671 100644 --- a/doc/packages/substrate.adoc +++ b/doc/packages/substrate.adoc @@ -3,64 +3,62 @@ :leveloffset: +3 -include::../../substrate/bft/README.adoc[] +include::../bft/README.adoc[] -include::../../substrate/cli/README.adoc[] +include::../cli/README.adoc[] -include::../../substrate/client/README.adoc[] +include::../client/README.adoc[] -include::../../substrate/codec/README.adoc[] +include::../codec/README.adoc[] -include::../../substrate/ed25519/README.adoc[] +include::../environmental/README.adoc[] -include::../../substrate/environmental/README.adoc[] +include::../executor/README.adoc[] -include::../../substrate/executor/README.adoc[] +include::../extrinsic-pool/README.adoc[] -include::../../substrate/extrinsic-pool/README.adoc[] +include::../keyring/README.adoc[] -include::../../substrate/keyring/README.adoc[] +include::../keystore/README.adoc[] -include::../../substrate/keystore/README.adoc[] +include::../misbehavior-check/README.adoc[] -include::../../substrate/misbehavior-check/README.adoc[] +include::../network/README.adoc[] -include::../../substrate/network/README.adoc[] +include::../network-libp2p/README.adoc[] -include::../../substrate/network-libp2p/README.adoc[] +include::../primitives/README.adoc[] -include::../../substrate/primitives/README.adoc[] +include::../pwasm-alloc/README.adoc[] -include::../../substrate/pwasm-alloc/README.adoc[] +include::../pwasm-libc/README.adoc[] -include::../../substrate/pwasm-libc/README.adoc[] +include::../rpc/README.adoc[] -include::../../substrate/rpc/README.adoc[] +include::../rpc-servers/README.adoc[] -include::../../substrate/rpc-servers/README.adoc[] +include::../runtime/README.adoc[] -include::../../substrate/runtime/README.adoc[] +include::../runtime-io/README.adoc[] -include::../../substrate/runtime-io/README.adoc[] +include::../runtime-sandbox/README.adoc[] -include::../../substrate/runtime-sandbox/README.adoc[] +include::../runtime-std/README.adoc[] -include::../../substrate/runtime-std/README.adoc[] +include::../runtime-support/README.adoc[] -include::../../substrate/runtime-support/README.adoc[] +include::../serializer/README.adoc[] -include::../../substrate/serializer/README.adoc[] +include::../service/README.adoc[] -include::../../substrate/service/README.adoc[] +include::../state-db/README.adoc[] -include::../../substrate/state-db/README.adoc[] +include::../state-machine/README.adoc[] -include::../../substrate/state-machine/README.adoc[] +include::../telemetry/README.adoc[] -include::../../substrate/telemetry/README.adoc[] +include::../test-client/README.adoc[] -include::../../substrate/test-client/README.adoc[] - -include::../../substrate/test-runtime/README.adoc[] +include::../test-runtime/README.adoc[] :leveloffset: -3 diff --git a/scripts/build-demos.sh b/scripts/build-demos.sh deleted file mode 100755 index 285da143c17d8..0000000000000 --- a/scripts/build-demos.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 DEMO in "${DEMOS[@]}" -do - echo "*** Building wasm binaries in $DEMO" - cd "$PROJECT_ROOT/$DEMO" - - ./build.sh - - cd - >> /dev/null -done - -# Restore initial directory. -popd diff --git a/scripts/common.sh b/scripts/common.sh index a75be8e406e80..042dba9e99c72 100644 --- a/scripts/common.sh +++ b/scripts/common.sh @@ -4,12 +4,12 @@ ROOT=`dirname "$0"` # A list of directories which contain wasm projects. SRCS=( - "substrate/executor/wasm" + "core/executor/wasm" + "node/runtime/wasm" + "core/test-runtime/wasm" ) DEMOS=( - "demo/runtime/wasm" - "substrate/test-runtime/wasm" ) # Make pushd/popd silent. diff --git a/subkey/Cargo.toml b/subkey/Cargo.toml index c9bb23962dea4..171c5163b2631 100644 --- a/subkey/Cargo.toml +++ b/subkey/Cargo.toml @@ -4,8 +4,7 @@ version = "0.1.0" authors = ["Parity Technologies "] [dependencies] -ed25519 = { version = "*", path = "../substrate/ed25519" } -substrate-primitives = { version = "*", path = "../substrate/primitives" } +substrate-primitives = { version = "*", path = "../core/primitives" } rand = "0.4" [features] diff --git a/subkey/src/main.rs b/subkey/src/main.rs index aa4cf48585049..349898602062b 100644 --- a/subkey/src/main.rs +++ b/subkey/src/main.rs @@ -1,14 +1,12 @@ #![cfg_attr(feature = "bench", feature(test))] #[cfg(feature = "bench")] extern crate test; -extern crate ed25519; extern crate substrate_primitives; extern crate rand; use rand::{OsRng, Rng}; use std::env::args; -use ed25519::Pair; -use substrate_primitives::hexdisplay::HexDisplay; +use substrate_primitives::{hexdisplay::HexDisplay, ed25519::Pair}; use std::cmp; fn good_waypoint(done: u64) -> u64 { diff --git a/substrate/bft/Cargo.toml b/substrate/bft/Cargo.toml deleted file mode 100644 index f15f24a0d5bd8..0000000000000 --- a/substrate/bft/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "substrate-bft" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -futures = "0.1.17" -substrate-codec = { path = "../codec" } -substrate-primitives = { path = "../primitives" } -substrate-runtime-support = { path = "../runtime-support" } -substrate-runtime-primitives = { path = "../runtime/primitives" } -substrate-runtime-version = { path = "../runtime/version" } -ed25519 = { path = "../ed25519" } -tokio = "0.1.7" -parking_lot = "0.4" -error-chain = "0.12" -log = "0.3" -rhododendron = "0.3" - -[dev-dependencies] -substrate-keyring = { path = "../keyring" } -substrate-executor = { path = "../executor" } diff --git a/substrate/bft/README.adoc b/substrate/bft/README.adoc deleted file mode 100644 index 8f4939087eed7..0000000000000 --- a/substrate/bft/README.adoc +++ /dev/null @@ -1,13 +0,0 @@ - -= Substrate BFT - -.Summary -[source, toml] ----- -include::Cargo.toml[lines=2..5] ----- - -.Description ----- -include::src/lib.rs[tag=description] ----- diff --git a/substrate/bft/src/error.rs b/substrate/bft/src/error.rs deleted file mode 100644 index d8de262364965..0000000000000 --- a/substrate/bft/src/error.rs +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2017 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 . - -//! Error types in the BFT service. -use runtime_version::RuntimeVersion; - -error_chain! { - errors { - /// Missing state at block with given descriptor. - StateUnavailable(b: String) { - description("State missing at given block."), - display("State unavailable at block {}", b), - } - - /// I/O terminated unexpectedly - IoTerminated { - description("I/O terminated unexpectedly."), - display("I/O terminated unexpectedly."), - } - - /// Unable to schedule wakeup. - FaultyTimer(e: ::tokio::timer::Error) { - description("Timer error"), - display("Timer error: {}", e), - } - - /// Unable to propose a block. - CannotPropose { - description("Unable to create block proposal."), - display("Unable to create block proposal."), - } - - /// Error checking signature - InvalidSignature(s: ::ed25519::Signature, a: ::primitives::AuthorityId) { - description("Message signature is invalid"), - display("Message signature {:?} by {:?} is invalid.", s, a), - } - - /// Account is not an authority. - InvalidAuthority(a: ::primitives::AuthorityId) { - description("Message sender is not a valid authority"), - display("Message sender {:?} is not a valid authority.", a), - } - - /// Authoring interface does not match the runtime. - IncompatibleAuthoringRuntime(native: RuntimeVersion, on_chain: RuntimeVersion) { - description("Authoring for current runtime is not supported"), - display("Authoring for current runtime is not supported. Native ({}) cannot author for on-chain ({}).", native, on_chain), - } - - /// Authoring interface does not match the runtime. - RuntimeVersionMissing { - description("Current runtime has no version"), - display("Authoring for current runtime is not supported since it has no version."), - } - - /// Authoring interface does not match the runtime. - NativeRuntimeMissing { - description("This build has no native runtime"), - display("Authoring in current build is not supported since it has no runtime."), - } - - /// Justification requirements not met. - InvalidJustification { - description("Invalid justification"), - display("Invalid justification."), - } - - /// Some other error. - Other(e: Box<::std::error::Error + Send>) { - description("Other error") - display("Other error: {}", e.description()) - } - } -} - -impl From<::rhododendron::InputStreamConcluded> for Error { - fn from(_: ::rhododendron::InputStreamConcluded) -> Error { - ErrorKind::IoTerminated.into() - } -} diff --git a/substrate/bft/src/lib.rs b/substrate/bft/src/lib.rs deleted file mode 100644 index 4b9d3be90c291..0000000000000 --- a/substrate/bft/src/lib.rs +++ /dev/null @@ -1,1126 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -// tag::description[] -//! BFT Agreement based on a rotating proposer in different rounds. -//! -//! Where this crate refers to input stream, should never logically conclude. -//! The logic in this crate assumes that messages flushed to the output stream -//! will eventually reach other nodes and that our own messages are not included -//! in the input stream. -//! -//! Note that it is possible to witness agreement being reached without ever -//! seeing the candidate. Any candidates seen will be checked for validity. -//! -//! Although technically the agreement will always complete (given the eventual -//! delivery of messages), in practice it is possible for this future to -//! conclude without having witnessed the conclusion. -//! In general, this future should be pre-empted by the import of a justification -//! set for this block height. -// end::description[] - -#![recursion_limit="128"] - -pub mod error; - -extern crate substrate_codec as codec; -extern crate substrate_primitives as primitives; -extern crate substrate_runtime_support as runtime_support; -extern crate substrate_runtime_primitives as runtime_primitives; -extern crate substrate_runtime_version as runtime_version; -extern crate ed25519; -extern crate tokio; -extern crate parking_lot; -extern crate rhododendron; - -#[macro_use] -extern crate log; - -extern crate futures; - -#[macro_use] -extern crate error_chain; - -use std::sync::Arc; -use std::sync::atomic::{AtomicUsize, Ordering}; -use std::time::{Instant, Duration}; - -use codec::Encode; -use ed25519::LocalizedSignature; -use runtime_primitives::generic::BlockId; -use runtime_primitives::traits::{Block, Header}; -use runtime_primitives::bft::{Message as PrimitiveMessage, Action as PrimitiveAction, Justification as PrimitiveJustification}; -use primitives::AuthorityId; - -use futures::{Async, Stream, Sink, Future, IntoFuture}; -use futures::sync::oneshot; -use tokio::timer::Delay; -use parking_lot::Mutex; - -pub use rhododendron::{InputStreamConcluded, AdvanceRoundReason}; -pub use error::{Error, ErrorKind}; - -// statuses for an agreement -mod status { - pub const LIVE: usize = 0; - pub const BAD: usize = 1; - pub const GOOD: usize = 2; -} - -/// Messages over the proposal. -/// Each message carries an associated round number. -pub type Message = rhododendron::Message::Hash>; - -/// Localized message type. -pub type LocalizedMessage = rhododendron::LocalizedMessage< - B, - ::Hash, - AuthorityId, - LocalizedSignature ->; - -/// Justification of some hash. -pub type Justification = rhododendron::Justification; - -/// Justification of a prepare message. -pub type PrepareJustification = rhododendron::PrepareJustification; - -/// Unchecked justification. -pub struct UncheckedJustification(rhododendron::UncheckedJustification); - -impl UncheckedJustification { - /// Create a new, unchecked justification. - pub fn new(digest: H, signatures: Vec, round_number: usize) -> Self { - UncheckedJustification(rhododendron::UncheckedJustification { - digest, - signatures, - round_number, - }) - } -} - -impl From> for UncheckedJustification { - fn from(inner: rhododendron::UncheckedJustification) -> Self { - UncheckedJustification(inner) - } -} - -impl From> for UncheckedJustification { - fn from(just: PrimitiveJustification) -> Self { - UncheckedJustification(rhododendron::UncheckedJustification { - round_number: just.round_number as usize, - digest: just.hash, - signatures: just.signatures.into_iter().map(|(from, sig)| LocalizedSignature { - signer: from.into(), - signature: sig, - }).collect(), - }) - } -} - -impl Into> for UncheckedJustification { - fn into(self) -> PrimitiveJustification { - PrimitiveJustification { - round_number: self.0.round_number as u32, - hash: self.0.digest, - signatures: self.0.signatures.into_iter().map(|s| (s.signer.into(), s.signature)).collect(), - } - } -} - -/// Result of a committed round of BFT -pub type Committed = rhododendron::Committed::Hash, LocalizedSignature>; - -/// Communication between BFT participants. -pub type Communication = rhododendron::Communication::Hash, AuthorityId, LocalizedSignature>; - -/// Misbehavior observed from BFT participants. -pub type Misbehavior = rhododendron::Misbehavior; - -/// Environment producer for a BFT instance. Creates proposer instance and communication streams. -pub trait Environment { - /// The proposer type this creates. - type Proposer: Proposer; - /// The input stream type. - type Input: Stream, Error=>::Error>; - /// The output stream type. - type Output: Sink, SinkError=>::Error>; - /// Error which can occur upon creation. - type Error: From; - - /// Initialize the proposal logic on top of a specific header. - /// Produces the proposer and message streams for this instance of BFT agreement. - // TODO: provide state context explicitly? - fn init(&self, parent_header: &B::Header, authorities: &[AuthorityId], sign_with: Arc) - -> Result<(Self::Proposer, Self::Input, Self::Output), Self::Error>; -} - -/// Logic for a proposer. -/// -/// This will encapsulate creation and evaluation of proposals at a specific -/// block. -pub trait Proposer { - /// Error type which can occur when proposing or evaluating. - type Error: From + From + ::std::fmt::Debug + 'static; - /// Future that resolves to a committed proposal. - type Create: IntoFuture; - /// Future that resolves when a proposal is evaluated. - type Evaluate: IntoFuture; - - /// Create a proposal. - fn propose(&self) -> Self::Create; - - /// Evaluate proposal. True means valid. - fn evaluate(&self, proposal: &B) -> Self::Evaluate; - - /// Import witnessed misbehavior. - fn import_misbehavior(&self, misbehavior: Vec<(AuthorityId, Misbehavior)>); - - /// Determine the proposer for a given round. This should be a deterministic function - /// with consistent results across all authorities. - fn round_proposer(&self, round_number: usize, authorities: &[AuthorityId]) -> AuthorityId; - - /// Hook called when a BFT round advances without a proposal. - fn on_round_end(&self, _round_number: usize, _proposed: bool) { } -} - -/// Block import trait. -pub trait BlockImport { - /// Import a block alongside its corresponding justification. - fn import_block(&self, block: B, justification: Justification, authorities: &[AuthorityId]) -> bool; -} - -/// Trait for getting the authorities at a given block. -pub trait Authorities { - /// Get the authorities at the given block. - fn authorities(&self, at: &BlockId) -> Result, Error>; -} - -// caches the round number to start at if we end up with BFT consensus on the same -// parent hash more than once (happens if block is bad). -// -// this will force a committed but locally-bad block to be considered analogous to -// a round advancement vote. -#[derive(Debug)] -struct RoundCache { - hash: Option, - start_round: usize, -} - -/// Instance of BFT agreement. -struct BftInstance { - key: Arc, - authorities: Vec, - parent_hash: B::Hash, - round_timeout_multiplier: u64, - cache: Arc>>, - proposer: P, -} - -impl> BftInstance - where - B: Clone + Eq, - B::Hash: ::std::hash::Hash - -{ - fn round_timeout_duration(&self, round: usize) -> Duration { - // 2^(min(6, x/8)) * 10 - // Grows exponentially starting from 10 seconds, capped at 640 seconds. - const ROUND_INCREMENT_STEP: usize = 8; - - let round = round / ROUND_INCREMENT_STEP; - let round = ::std::cmp::min(6, round) as u32; - - let timeout = 1u64.checked_shl(round) - .unwrap_or_else(u64::max_value) - .saturating_mul(self.round_timeout_multiplier); - - Duration::from_secs(timeout) - } - - fn update_round_cache(&self, current_round: usize) { - let mut cache = self.cache.lock(); - if cache.hash.as_ref() == Some(&self.parent_hash) { - cache.start_round = current_round + 1; - } - } -} - -impl> rhododendron::Context for BftInstance - where - B: Clone + Eq, - B::Hash: ::std::hash::Hash, -{ - type Error = P::Error; - type AuthorityId = AuthorityId; - type Digest = B::Hash; - type Signature = LocalizedSignature; - type Candidate = B; - type RoundTimeout = Box>; - type CreateProposal = ::Future; - type EvaluateProposal = ::Future; - - fn local_id(&self) -> AuthorityId { - self.key.public().into() - } - - fn proposal(&self) -> Self::CreateProposal { - self.proposer.propose().into_future() - } - - fn candidate_digest(&self, proposal: &B) -> B::Hash { - proposal.hash() - } - - fn sign_local(&self, message: Message) -> LocalizedMessage { - sign_message(message, &*self.key, self.parent_hash.clone()) - } - - fn round_proposer(&self, round: usize) -> AuthorityId { - self.proposer.round_proposer(round, &self.authorities[..]) - } - - fn proposal_valid(&self, proposal: &B) -> Self::EvaluateProposal { - self.proposer.evaluate(proposal).into_future() - } - - fn begin_round_timeout(&self, round: usize) -> Self::RoundTimeout { - let timeout = self.round_timeout_duration(round); - let fut = Delay::new(Instant::now() + timeout) - .map_err(|e| Error::from(ErrorKind::FaultyTimer(e))) - .map_err(Into::into); - - Box::new(fut) - } - - fn on_advance_round( - &self, - accumulator: &::rhododendron::Accumulator, - round: usize, - next_round: usize, - reason: AdvanceRoundReason, - ) { - use std::collections::HashSet; - - let collect_pubkeys = |participants: HashSet<&Self::AuthorityId>| participants.into_iter() - .map(|p| ::ed25519::Public::from_raw(p.0)) - .collect::>(); - - let round_timeout = self.round_timeout_duration(next_round); - debug!(target: "bft", "Advancing to round {} from {}", next_round, round); - debug!(target: "bft", "Participating authorities: {:?}", - collect_pubkeys(accumulator.participants())); - debug!(target: "bft", "Voting authorities: {:?}", - collect_pubkeys(accumulator.voters())); - debug!(target: "bft", "Round {} should end in at most {} seconds from now", next_round, round_timeout.as_secs()); - - self.update_round_cache(next_round); - - if let AdvanceRoundReason::Timeout = reason { - self.proposer.on_round_end(round, accumulator.proposal().is_some()); - } - } -} - -/// A future that resolves either when canceled (witnessing a block from the network at same height) -/// or when agreement completes. -pub struct BftFuture where - B: Block + Clone + Eq, - B::Hash: ::std::hash::Hash, - P: Proposer, - InStream: Stream, Error=P::Error>, - OutSink: Sink, SinkError=P::Error>, -{ - inner: rhododendron::Agreement, InStream, OutSink>, - status: Arc, - cancel: oneshot::Receiver<()>, - import: Arc, -} - -impl Future for BftFuture where - B: Block + Clone + Eq, - B::Hash: ::std::hash::Hash, - P: Proposer, - P::Error: ::std::fmt::Display, - I: BlockImport, - InStream: Stream, Error=P::Error>, - OutSink: Sink, SinkError=P::Error>, -{ - type Item = (); - type Error = (); - - fn poll(&mut self) -> ::futures::Poll<(), ()> { - // service has canceled the future. bail - let cancel = match self.cancel.poll() { - Ok(Async::Ready(())) | Err(_) => true, - Ok(Async::NotReady) => false, - }; - - // TODO: handle and log this error in a way which isn't noisy on exit. - let committed = match self.inner.poll().map_err(|_| ()) { - Ok(Async::Ready(x)) => x, - Ok(Async::NotReady) => - return Ok(if cancel { Async::Ready(()) } else { Async::NotReady }), - Err(()) => return Err(()), - }; - - // if something was committed, the round leader must have proposed. - self.inner.context().proposer.on_round_end(committed.round_number, true); - - // If we didn't see the proposal (very unlikely), - // we will get the block from the network later. - if let Some(justified_block) = committed.candidate { - let hash = justified_block.hash(); - info!(target: "bft", "Importing block #{} ({}) directly from BFT consensus", - justified_block.header().number(), hash); - - let import_ok = self.import.import_block( - justified_block, - committed.justification, - &self.inner.context().authorities - ); - - if !import_ok { - warn!(target: "bft", "{:?} was bad block agreed on in round #{}", - hash, committed.round_number); - self.status.store(status::BAD, Ordering::Release); - } else { - self.status.store(status::GOOD, Ordering::Release); - } - } else { - // assume good unless we received the proposal. - self.status.store(status::GOOD, Ordering::Release); - } - - self.inner.context().update_round_cache(committed.round_number); - - Ok(Async::Ready(())) - } -} - -impl Drop for BftFuture where - B: Block + Clone + Eq, - B::Hash: ::std::hash::Hash, - P: Proposer, - InStream: Stream, Error=P::Error>, - OutSink: Sink, SinkError=P::Error>, -{ - fn drop(&mut self) { - // TODO: have a trait member to pass misbehavior reports into. - let misbehavior = self.inner.drain_misbehavior().collect::>(); - self.inner.context().proposer.import_misbehavior(misbehavior); - } -} - -struct AgreementHandle { - status: Arc, - send_cancel: Option>, -} - -impl AgreementHandle { - fn status(&self) -> usize { - self.status.load(Ordering::Acquire) - } -} - -impl Drop for AgreementHandle { - fn drop(&mut self) { - if let Some(sender) = self.send_cancel.take() { - let _ = sender.send(()); - } - } -} - -/// The BftService kicks off the agreement process on top of any blocks it -/// is notified of. -/// -/// This assumes that it is being run in the context of a tokio runtime. -pub struct BftService { - client: Arc, - live_agreement: Mutex>, - round_cache: Arc>>, - round_timeout_multiplier: u64, - key: Arc, // TODO: key changing over time. - factory: P, -} - -impl BftService - where - B: Block + Clone + Eq, - P: Environment, - >::Error: ::std::fmt::Display, - I: BlockImport + Authorities, -{ - - /// Create a new service instance. - pub fn new(client: Arc, key: Arc, factory: P) -> BftService { - BftService { - client: client, - live_agreement: Mutex::new(None), - round_cache: Arc::new(Mutex::new(RoundCache { - hash: None, - start_round: 0, - })), - round_timeout_multiplier: 10, - key: key, // TODO: key changing over time. - factory, - } - } - - /// Get the local Authority ID. - pub fn local_id(&self) -> AuthorityId { - // TODO: based on a header and some keystore. - self.key.public().into() - } - - /// Signal that a valid block with the given header has been imported. - /// - /// If the local signing key is an authority, this will begin the consensus process to build a - /// block on top of it. If the executor fails to run the future, an error will be returned. - /// Returns `None` if the agreement on the block with given parent is already in progress. - pub fn build_upon(&self, header: &B::Header) - -> Result>::Proposer, - I, -

>::Input, -

>::Output, - >>, P::Error> - where - { - let hash = header.hash(); - - let mut live_agreement = self.live_agreement.lock(); - let can_build = live_agreement.as_ref() - .map_or(true, |x| self.can_build_on_inner(header, x)); - - if !can_build { - return Ok(None) - } - - let authorities = self.client.authorities(&BlockId::Hash(hash.clone()))?; - - let n = authorities.len(); - let max_faulty = max_faulty_of(n); - trace!(target: "bft", "Initiating agreement on top of #{}, {:?}", header.number(), hash); - trace!(target: "bft", "max_faulty_of({})={}", n, max_faulty); - - let local_id = self.local_id(); - - if !authorities.contains(&local_id) { - // cancel current agreement - live_agreement.take(); - Err(ErrorKind::InvalidAuthority(local_id).into())?; - } - - let (proposer, input, output) = self.factory.init(header, &authorities, self.key.clone())?; - - let bft_instance = BftInstance { - proposer, - parent_hash: hash.clone(), - cache: self.round_cache.clone(), - round_timeout_multiplier: self.round_timeout_multiplier, - key: self.key.clone(), - authorities: authorities, - }; - - let mut agreement = rhododendron::agree( - bft_instance, - n, - max_faulty, - input, - output, - ); - - // fast forward round number if necessary. - { - let mut cache = self.round_cache.lock(); - trace!(target: "bft", "Round cache: {:?}", &*cache); - if cache.hash.as_ref() == Some(&hash) { - trace!(target: "bft", "Fast-forwarding to round {}", cache.start_round); - let start_round = cache.start_round; - cache.start_round += 1; - - drop(cache); - agreement.fast_forward(start_round); - } else { - *cache = RoundCache { - hash: Some(hash.clone()), - start_round: 1, - }; - } - } - - let status = Arc::new(AtomicUsize::new(status::LIVE)); - let (tx, rx) = oneshot::channel(); - - // cancel current agreement. - *live_agreement = Some((header.clone(), AgreementHandle { - send_cancel: Some(tx), - status: status.clone(), - })); - - Ok(Some(BftFuture { - inner: agreement, - status: status, - cancel: rx, - import: self.client.clone(), - })) - } - - /// Cancel current agreement if any. - pub fn cancel_agreement(&self) { - self.live_agreement.lock().take(); - } - - /// Whether we can build using the given header. - pub fn can_build_on(&self, header: &B::Header) -> bool { - self.live_agreement.lock().as_ref() - .map_or(true, |x| self.can_build_on_inner(header, x)) - } - - /// Get a reference to the underyling client. - pub fn client(&self) -> &I { &*self.client } - - fn can_build_on_inner(&self, header: &B::Header, live: &(B::Header, AgreementHandle)) -> bool { - let hash = header.hash(); - let &(ref live_header, ref handle) = live; - match handle.status() { - _ if *header != *live_header && *live_header.parent_hash() != hash => true, // can always follow with next block. - status::BAD => hash == live_header.hash(), // bad block can be re-agreed on. - _ => false, // canceled won't appear since we overwrite the handle before returning. - } - } -} - -/// Given a total number of authorities, yield the maximum faulty that would be allowed. -/// This will always be under 1/3. -pub fn max_faulty_of(n: usize) -> usize { - n.saturating_sub(1) / 3 -} - -/// Given a total number of authorities, yield the minimum required signatures. -/// This will always be over 2/3. -pub fn bft_threshold(n: usize) -> usize { - n - max_faulty_of(n) -} - -fn check_justification_signed_message(authorities: &[AuthorityId], message: &[u8], just: UncheckedJustification) - -> Result, UncheckedJustification> -{ - // TODO: return additional error information. - just.0.check(authorities.len() - max_faulty_of(authorities.len()), |_, _, sig| { - let auth_id = sig.signer.clone().into(); - if !authorities.contains(&auth_id) { return None } - - if ed25519::verify_strong(&sig.signature, message, &sig.signer) { - Some(sig.signer.0) - } else { - None - } - }).map_err(UncheckedJustification) -} - -/// Check a full justification for a header hash. -/// Provide all valid authorities. -/// -/// On failure, returns the justification back. -pub fn check_justification(authorities: &[AuthorityId], parent: B::Hash, just: UncheckedJustification) - -> Result, UncheckedJustification> -{ - let message = Encode::encode(&PrimitiveMessage:: { - parent, - action: PrimitiveAction::Commit(just.0.round_number as u32, just.0.digest.clone()), - }); - - check_justification_signed_message(authorities, &message[..], just) -} - -/// Check a prepare justification for a header hash. -/// Provide all valid authorities. -/// -/// On failure, returns the justification back. -pub fn check_prepare_justification(authorities: &[AuthorityId], parent: B::Hash, just: UncheckedJustification) - -> Result, UncheckedJustification> -{ - let message = Encode::encode(&PrimitiveMessage:: { - parent, - action: PrimitiveAction::Prepare(just.0.round_number as u32, just.0.digest.clone()), - }); - - check_justification_signed_message(authorities, &message[..], just) -} - -/// Check proposal message signatures and authority. -/// Provide all valid authorities. -pub fn check_proposal( - authorities: &[AuthorityId], - parent_hash: &B::Hash, - propose: &::rhododendron::LocalizedProposal) - -> Result<(), Error> -{ - if !authorities.contains(&propose.sender) { - return Err(ErrorKind::InvalidAuthority(propose.sender.into()).into()); - } - - let action_header = PrimitiveAction::ProposeHeader(propose.round_number as u32, propose.digest.clone()); - let action_propose = PrimitiveAction::Propose(propose.round_number as u32, propose.proposal.clone()); - check_action::(action_header, parent_hash, &propose.digest_signature)?; - check_action::(action_propose, parent_hash, &propose.full_signature) -} - -/// Check vote message signatures and authority. -/// Provide all valid authorities. -pub fn check_vote( - authorities: &[AuthorityId], - parent_hash: &B::Hash, - vote: &::rhododendron::LocalizedVote) - -> Result<(), Error> -{ - if !authorities.contains(&vote.sender) { - return Err(ErrorKind::InvalidAuthority(vote.sender.into()).into()); - } - - let action = match vote.vote { - ::rhododendron::Vote::Prepare(r, ref h) => PrimitiveAction::Prepare(r as u32, h.clone()), - ::rhododendron::Vote::Commit(r, ref h) => PrimitiveAction::Commit(r as u32, h.clone()), - ::rhododendron::Vote::AdvanceRound(r) => PrimitiveAction::AdvanceRound(r as u32), - }; - check_action::(action, parent_hash, &vote.signature) -} - -fn check_action(action: PrimitiveAction, parent_hash: &B::Hash, sig: &LocalizedSignature) -> Result<(), Error> { - let primitive = PrimitiveMessage { - parent: parent_hash.clone(), - action, - }; - - let message = Encode::encode(&primitive); - if ed25519::verify_strong(&sig.signature, &message, &sig.signer) { - Ok(()) - } else { - Err(ErrorKind::InvalidSignature(sig.signature.into(), sig.signer.clone().into()).into()) - } -} - -/// Sign a BFT message with the given key. -pub fn sign_message(message: Message, key: &ed25519::Pair, parent_hash: B::Hash) -> LocalizedMessage { - let signer = key.public(); - - let sign_action = |action: PrimitiveAction| { - let primitive = PrimitiveMessage { - parent: parent_hash.clone(), - action, - }; - - let to_sign = Encode::encode(&primitive); - LocalizedSignature { - signer: signer.clone(), - signature: key.sign(&to_sign), - } - }; - - match message { - ::rhododendron::Message::Propose(r, proposal) => { - let header_hash = proposal.hash(); - let action_header = PrimitiveAction::ProposeHeader(r as u32, header_hash.clone()); - let action_propose = PrimitiveAction::Propose(r as u32, proposal.clone()); - - ::rhododendron::LocalizedMessage::Propose(::rhododendron::LocalizedProposal { - round_number: r, - proposal, - digest: header_hash, - sender: signer.clone().into(), - digest_signature: sign_action(action_header), - full_signature: sign_action(action_propose), - }) - } - ::rhododendron::Message::Vote(vote) => { - let action = match vote { - ::rhododendron::Vote::Prepare(r, ref h) => PrimitiveAction::Prepare(r as u32, h.clone()), - ::rhododendron::Vote::Commit(r, ref h) => PrimitiveAction::Commit(r as u32, h.clone()), - ::rhododendron::Vote::AdvanceRound(r) => PrimitiveAction::AdvanceRound(r as u32), - }; - - ::rhododendron::LocalizedMessage::Vote(::rhododendron::LocalizedVote { - vote: vote, - sender: signer.clone().into(), - signature: sign_action(action), - }) - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use std::collections::HashSet; - use runtime_primitives::testing::{Block as GenericTestBlock, Header as TestHeader}; - use primitives::H256; - use self::keyring::Keyring; - - extern crate substrate_keyring as keyring; - - type TestBlock = GenericTestBlock<()>; - - struct FakeClient { - authorities: Vec, - imported_heights: Mutex> - } - - impl BlockImport for FakeClient { - fn import_block(&self, block: TestBlock, _justification: Justification, _authorities: &[AuthorityId]) -> bool { - assert!(self.imported_heights.lock().insert(block.header.number)); - true - } - } - - impl Authorities for FakeClient { - fn authorities(&self, _at: &BlockId) -> Result, Error> { - Ok(self.authorities.clone()) - } - } - - // "black hole" output sink. - struct Comms(::std::marker::PhantomData); - - impl Sink for Comms { - type SinkItem = Communication; - type SinkError = E; - - fn start_send(&mut self, _item: Communication) -> ::futures::StartSend, E> { - Ok(::futures::AsyncSink::Ready) - } - - fn poll_complete(&mut self) -> ::futures::Poll<(), E> { - Ok(Async::Ready(())) - } - } - - impl Stream for Comms { - type Item = Communication; - type Error = E; - - fn poll(&mut self) -> ::futures::Poll, Self::Error> { - Ok(::futures::Async::NotReady) - } - } - - struct DummyFactory; - struct DummyProposer(u64); - - impl Environment for DummyFactory { - type Proposer = DummyProposer; - type Input = Comms; - type Output = Comms; - type Error = Error; - - fn init(&self, parent_header: &TestHeader, _authorities: &[AuthorityId], _sign_with: Arc) - -> Result<(DummyProposer, Self::Input, Self::Output), Error> - { - Ok((DummyProposer(parent_header.number + 1), Comms(::std::marker::PhantomData), Comms(::std::marker::PhantomData))) - } - } - - impl Proposer for DummyProposer { - type Error = Error; - type Create = Result; - type Evaluate = Result; - - fn propose(&self) -> Result { - - Ok(TestBlock { - header: from_block_number(self.0), - extrinsics: Default::default() - }) - } - - fn evaluate(&self, proposal: &TestBlock) -> Result { - Ok(proposal.header.number == self.0) - } - - fn import_misbehavior(&self, _misbehavior: Vec<(AuthorityId, Misbehavior)>) {} - - fn round_proposer(&self, round_number: usize, authorities: &[AuthorityId]) -> AuthorityId { - authorities[round_number % authorities.len()].clone() - } - } - - fn make_service(client: FakeClient) - -> BftService - { - BftService { - client: Arc::new(client), - live_agreement: Mutex::new(None), - round_cache: Arc::new(Mutex::new(RoundCache { - hash: None, - start_round: 0, - })), - round_timeout_multiplier: 10, - key: Arc::new(Keyring::One.into()), - factory: DummyFactory - } - } - - fn sign_vote(vote: ::rhododendron::Vote, key: &ed25519::Pair, parent_hash: H256) -> LocalizedSignature { - match sign_message::(vote.into(), key, parent_hash) { - ::rhododendron::LocalizedMessage::Vote(vote) => vote.signature, - _ => panic!("signing vote leads to signed vote"), - } - } - - fn from_block_number(num: u64) -> TestHeader { - TestHeader::new( - num, - Default::default(), - Default::default(), - Default::default(), - Default::default(), - ) - } - - #[test] - fn future_gets_preempted() { - let client = FakeClient { - authorities: vec![ - Keyring::One.to_raw_public().into(), - Keyring::Two.to_raw_public().into(), - Keyring::Alice.to_raw_public().into(), - Keyring::Eve.to_raw_public().into(), - ], - imported_heights: Mutex::new(HashSet::new()), - }; - - let service = make_service(client); - - let first = from_block_number(2); - let first_hash = first.hash(); - - let mut second = from_block_number(3); - second.parent_hash = first_hash; - let _second_hash = second.hash(); - - let mut first_bft = service.build_upon(&first).unwrap().unwrap(); - assert!(service.live_agreement.lock().as_ref().unwrap().0 == first); - - let _second_bft = service.build_upon(&second).unwrap(); - assert!(service.live_agreement.lock().as_ref().unwrap().0 != first); - assert!(service.live_agreement.lock().as_ref().unwrap().0 == second); - - // first_bft has been cancelled. need to swap out so we can check it. - let (_tx, mut rx) = oneshot::channel(); - ::std::mem::swap(&mut rx, &mut first_bft.cancel); - - assert!(rx.wait().is_ok()); - } - - #[test] - fn max_faulty() { - assert_eq!(max_faulty_of(3), 0); - assert_eq!(max_faulty_of(4), 1); - assert_eq!(max_faulty_of(100), 33); - assert_eq!(max_faulty_of(0), 0); - assert_eq!(max_faulty_of(11), 3); - assert_eq!(max_faulty_of(99), 32); - } - - #[test] - fn justification_check_works() { - let parent_hash = Default::default(); - let hash = [0xff; 32].into(); - - let authorities = vec![ - Keyring::One.to_raw_public().into(), - Keyring::Two.to_raw_public().into(), - Keyring::Alice.to_raw_public().into(), - Keyring::Eve.to_raw_public().into(), - ]; - - let authorities_keys = vec![ - Keyring::One.into(), - Keyring::Two.into(), - Keyring::Alice.into(), - Keyring::Eve.into(), - ]; - - let unchecked = UncheckedJustification(rhododendron::UncheckedJustification { - digest: hash, - round_number: 1, - signatures: authorities_keys.iter().take(3).map(|key| { - sign_vote(rhododendron::Vote::Commit(1, hash).into(), key, parent_hash) - }).collect(), - }); - - assert!(check_justification::(&authorities, parent_hash, unchecked).is_ok()); - - let unchecked = UncheckedJustification(rhododendron::UncheckedJustification { - digest: hash, - round_number: 0, // wrong round number (vs. the signatures) - signatures: authorities_keys.iter().take(3).map(|key| { - sign_vote(rhododendron::Vote::Commit(1, hash).into(), key, parent_hash) - }).collect(), - }); - - assert!(check_justification::(&authorities, parent_hash, unchecked).is_err()); - - // not enough signatures. - let unchecked = UncheckedJustification(rhododendron::UncheckedJustification { - digest: hash, - round_number: 1, - signatures: authorities_keys.iter().take(2).map(|key| { - sign_vote(rhododendron::Vote::Commit(1, hash).into(), key, parent_hash) - }).collect(), - }); - - assert!(check_justification::(&authorities, parent_hash, unchecked).is_err()); - - // wrong hash. - let unchecked = UncheckedJustification(rhododendron::UncheckedJustification { - digest: [0xfe; 32].into(), - round_number: 1, - signatures: authorities_keys.iter().take(3).map(|key| { - sign_vote(rhododendron::Vote::Commit(1, hash).into(), key, parent_hash) - }).collect(), - }); - - assert!(check_justification::(&authorities, parent_hash, unchecked).is_err()); - } - - #[test] - fn propose_check_works() { - let parent_hash = Default::default(); - - let authorities = vec![ - Keyring::Alice.to_raw_public().into(), - Keyring::Eve.to_raw_public().into(), - ]; - - let block = TestBlock { - header: from_block_number(1), - extrinsics: Default::default() - }; - - let proposal = sign_message(::rhododendron::Message::Propose(1, block.clone()), &Keyring::Alice.pair(), parent_hash);; - if let ::rhododendron::LocalizedMessage::Propose(proposal) = proposal { - assert!(check_proposal(&authorities, &parent_hash, &proposal).is_ok()); - let mut invalid_round = proposal.clone(); - invalid_round.round_number = 0; - assert!(check_proposal(&authorities, &parent_hash, &invalid_round).is_err()); - let mut invalid_digest = proposal.clone(); - invalid_digest.digest = [0xfe; 32].into(); - assert!(check_proposal(&authorities, &parent_hash, &invalid_digest).is_err()); - } else { - assert!(false); - } - - // Not an authority - let proposal = sign_message::(::rhododendron::Message::Propose(1, block), &Keyring::Bob.pair(), parent_hash);; - if let ::rhododendron::LocalizedMessage::Propose(proposal) = proposal { - assert!(check_proposal(&authorities, &parent_hash, &proposal).is_err()); - } else { - assert!(false); - } - } - - #[test] - fn vote_check_works() { - let parent_hash: H256 = Default::default(); - let hash: H256 = [0xff; 32].into(); - - let authorities = vec![ - Keyring::Alice.to_raw_public().into(), - Keyring::Eve.to_raw_public().into(), - ]; - - let vote = sign_message::(::rhododendron::Message::Vote(::rhododendron::Vote::Prepare(1, hash)), &Keyring::Alice.pair(), parent_hash);; - if let ::rhododendron::LocalizedMessage::Vote(vote) = vote { - assert!(check_vote::(&authorities, &parent_hash, &vote).is_ok()); - let mut invalid_sender = vote.clone(); - invalid_sender.signature.signer = Keyring::Eve.into(); - assert!(check_vote::(&authorities, &parent_hash, &invalid_sender).is_err()); - } else { - assert!(false); - } - - // Not an authority - let vote = sign_message::(::rhododendron::Message::Vote(::rhododendron::Vote::Prepare(1, hash)), &Keyring::Bob.pair(), parent_hash);; - if let ::rhododendron::LocalizedMessage::Vote(vote) = vote { - assert!(check_vote::(&authorities, &parent_hash, &vote).is_err()); - } else { - assert!(false); - } - } - - #[test] - fn drop_bft_future_does_not_deadlock() { - let client = FakeClient { - authorities: vec![ - Keyring::One.to_raw_public().into(), - Keyring::Two.to_raw_public().into(), - Keyring::Alice.to_raw_public().into(), - Keyring::Eve.to_raw_public().into(), - ], - imported_heights: Mutex::new(HashSet::new()), - }; - - let service = make_service(client); - - let first = from_block_number(2); - let first_hash = first.hash(); - - let mut second = from_block_number(3); - second.parent_hash = first_hash; - - let _ = service.build_upon(&first).unwrap(); - assert!(service.live_agreement.lock().as_ref().unwrap().0 == first); - service.live_agreement.lock().take(); - } - - #[test] - fn bft_can_build_though_skipped() { - let client = FakeClient { - authorities: vec![ - Keyring::One.to_raw_public().into(), - Keyring::Two.to_raw_public().into(), - Keyring::Alice.to_raw_public().into(), - Keyring::Eve.to_raw_public().into(), - ], - imported_heights: Mutex::new(HashSet::new()), - }; - - let service = make_service(client); - - let first = from_block_number(2); - let first_hash = first.hash(); - - let mut second = from_block_number(3); - second.parent_hash = first_hash; - - let mut third = from_block_number(4); - third.parent_hash = second.hash(); - - let _ = service.build_upon(&first).unwrap(); - assert!(service.live_agreement.lock().as_ref().unwrap().0 == first); - // BFT has not seen second, but will move forward on third - service.build_upon(&third).unwrap(); - assert!(service.live_agreement.lock().as_ref().unwrap().0 == third); - - // but we are not walking backwards - service.build_upon(&second).unwrap(); - assert!(service.live_agreement.lock().as_ref().unwrap().0 == third); - } -} diff --git a/substrate/cli/Cargo.toml b/substrate/cli/Cargo.toml deleted file mode 100644 index 7ae824c3dc104..0000000000000 --- a/substrate/cli/Cargo.toml +++ /dev/null @@ -1,35 +0,0 @@ -[package] -name = "substrate-cli" -version = "0.3.0" -authors = ["Parity Technologies "] -description = "Substrate CLI interface." -build = "build.rs" - -[dependencies] -clap = { version = "~2.32", features = ["yaml"] } -backtrace = "0.3" -env_logger = "0.4" -error-chain = "0.12" -log = "0.3" -atty = "0.2" -regex = "1" -time = "0.1" -slog = "^2" -ansi_term = "0.10" -lazy_static = "1.0" -app_dirs = "1.2" -tokio = "0.1.7" -futures = "0.1.17" -fdlimit = "0.1" -exit-future = "0.1" -sysinfo = "0.5.7" -substrate-client = { path = "../../substrate/client" } -substrate-network = { path = "../../substrate/network" } -substrate-network-libp2p = { path = "../../substrate/network-libp2p" } -substrate-runtime-primitives = { path = "../../substrate/runtime/primitives" } -substrate-service = { path = "../../substrate/service" } -substrate-telemetry = { path = "../../substrate/telemetry" } -names = "0.11.0" - -[build-dependencies] -clap = "~2.32" diff --git a/substrate/cli/README.adoc b/substrate/cli/README.adoc deleted file mode 100644 index 2b9b74362d322..0000000000000 --- a/substrate/cli/README.adoc +++ /dev/null @@ -1,13 +0,0 @@ - -= Substrate CLI - -.Summary -[source, toml] ----- -include::Cargo.toml[lines=2..5] ----- - -.Description -Polkadot CLI library - -include::doc/shell-completion.adoc[] diff --git a/substrate/cli/build.rs b/substrate/cli/build.rs deleted file mode 100644 index 645e98d5e8a9c..0000000000000 --- a/substrate/cli/build.rs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2017 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 . - -#[macro_use] -extern crate clap; - -use std::fs; -use std::env; -use clap::Shell; -use std::path::Path; - -fn main() { - build_shell_completion(); -} - -/// Build shell completion scripts for all known shells -/// Full list in https://github.com/kbknapp/clap-rs/blob/e9d0562a1dc5dfe731ed7c767e6cee0af08f0cf9/src/app/parser.rs#L123 -fn build_shell_completion() { - let shells = [Shell::Bash, Shell::Fish, Shell::Zsh, Shell::Elvish, Shell::PowerShell]; - for shell in shells.iter() { - build_completion(shell); - } -} - -/// Build the shell auto-completion for a given Shell -fn build_completion(shell: &Shell) { - let yml = load_yaml!("src/cli.yml"); - - let outdir = match env::var_os("OUT_DIR") { - None => return, - Some(dir) => dir, - }; - let path = Path::new(&outdir) - .parent().unwrap() - .parent().unwrap() - .parent().unwrap() - .join("completion-scripts"); - - fs::create_dir(&path).ok(); - - let mut app = clap::App::from_yaml(&yml); - app.gen_completions( - "polkadot", - *shell, - &path); -} diff --git a/substrate/cli/doc/shell-completion.adoc b/substrate/cli/doc/shell-completion.adoc deleted file mode 100644 index 8afbd37adb9f4..0000000000000 --- a/substrate/cli/doc/shell-completion.adoc +++ /dev/null @@ -1,41 +0,0 @@ - -== Shell completion - -The Substrate cli command supports shell auto-completion. For this to work, you will need to run the completion script matching you build and system. - -Assuming you built a release version using `cargo build --release` and use `bash` run the following: - -[source, shell] -source target/release/completion-scripts/substrate.bash - -You can find completion scripts for: -- bash -- fish -- zsh -- elvish -- powershell - -To make this change persistent, you can proceed as follow: - -.First install - -[source, shell] ----- -COMPL_DIR=$HOME/.completion -mkdir -p $COMPL_DIR -cp -f target/release/completion-scripts/substrate.bash $COMPL_DIR/ -echo "source $COMPL_DIR/substrate.bash" >> $HOME/.bash_profile -source $HOME/.bash_profile ----- - -.Update - -When you build a new version of Substrate, the following will ensure you auto-completion script matches the current binary: - -[source, shell] ----- -COMPL_DIR=$HOME/.completion -mkdir -p $COMPL_DIR -cp -f target/release/completion-scripts/substrate.bash $COMPL_DIR/ -source $HOME/.bash_profile ----- diff --git a/substrate/cli/src/cli.yml b/substrate/cli/src/cli.yml deleted file mode 100644 index 96c80e111c536..0000000000000 --- a/substrate/cli/src/cli.yml +++ /dev/null @@ -1,229 +0,0 @@ -name: {name} -author: {author} -about: {description} -args: - - log: - short: l - long: log - value_name: LOG_PATTERN - help: Sets a custom logging filter - takes_value: true - - base-path: - long: base-path - short: d - value_name: PATH - help: Specify custom base path - takes_value: true - - keystore-path: - long: keystore-path - value_name: PATH - help: Specify custom keystore path - takes_value: true - - key: - long: key - value_name: STRING - help: Specify additional key seed - takes_value: true - - node-key: - long: node-key - value_name: KEY - help: Specify node secret key (64-character hex string) - takes_value: true - - validator: - long: validator - help: Enable validator mode - takes_value: false - - light: - long: light - help: Run in light client mode - takes_value: false - - dev: - long: dev - help: Run in development mode; implies --chain=dev --validator --key Alice - takes_value: false - - listen-addr: - long: listen-addr - value_name: LISTEN_ADDR - help: Listen on this multiaddress - takes_value: true - multiple: true - - port: - long: port - value_name: PORT - help: Specify p2p protocol TCP port. Only used if --listen-addr is not specified. - takes_value: true - - rpc-external: - long: rpc-external - help: Listen to all RPC interfaces (default is local) - takes_value: false - - ws-external: - long: ws-external - help: Listen to all Websocket interfaces (default is local) - takes_value: false - - rpc-port: - long: rpc-port - value_name: PORT - help: Specify HTTP RPC server TCP port - takes_value: true - - ws-port: - long: ws-port - value_name: PORT - help: Specify WebSockets RPC server TCP port - takes_value: true - - bootnodes: - long: bootnodes - value_name: URL - help: Specify a list of bootnodes - takes_value: true - multiple: true - - reserved-nodes: - long: reserved-nodes - value_name: URL - help: Specify a list of reserved node addresses - takes_value: true - multiple: true - - min-peers: - long: min-peers - value_name: MIN_PEERS - help: Specify the minimum number of peers - takes_value: true - - max-peers: - long: max-peers - value_name: MAX_PEERS - help: Specify the maximum number of peers - takes_value: true - - chain: - long: chain - value_name: CHAIN_SPEC - help: Specify the chain specification (one of krummelanke, dev, local or staging) - takes_value: true - - pruning: - long: pruning - value_name: PRUNING_MODE - help: Specify the pruning mode, a number of blocks to keep or "archive". Default is 256. - takes_value: true - - name: - long: name - value_name: NAME - help: The human-readable name for this node, as reported to the telemetry server, if enabled - takes_value: true - - telemetry: - short: t - long: telemetry - help: Should connect to the Polkadot telemetry server (telemetry is off by default on local chains) - takes_value: false - - no-telemetry: - long: no-telemetry - help: Should not connect to the Polkadot telemetry server (telemetry is on by default on global chains) - takes_value: false - - telemetry-url: - long: telemetry-url - value_name: TELEMETRY_URL - help: The URL of the telemetry server. Implies --telemetry - takes_value: true - - execution: - long: execution - value_name: STRATEGY - help: The means of execution used when calling into the runtime. Can be either wasm, native or both. -subcommands: - - build-spec: - about: Build a spec.json file, outputing to stdout - args: - - raw: - long: raw - help: Force raw genesis storage output. - takes_value: false - - chain: - long: chain - value_name: CHAIN_SPEC - help: Specify the chain specification (one of krummelanke, dev, local or staging) - takes_value: true - - export-blocks: - about: Export blocks to a file - args: - - OUTPUT: - index: 1 - help: Output file name or stdout if unspecified. - required: false - - chain: - long: chain - value_name: CHAIN_SPEC - help: Specify the chain specification. - takes_value: true - - base-path: - long: base-path - short: d - value_name: PATH - help: Specify custom base path. - takes_value: true - - from: - long: from - value_name: BLOCK - help: Specify starting block number. 1 by default. - takes_value: true - - to: - long: to - value_name: BLOCK - help: Specify last block number. Best block by default. - takes_value: true - - json: - long: json - help: Use JSON output rather than binary. - takes_value: false - - import-blocks: - about: Import blocks from file. - args: - - INPUT: - index: 1 - help: Input file or stdin if unspecified. - required: false - - chain: - long: chain - value_name: CHAIN_SPEC - help: Specify the chain specification. - takes_value: true - - base-path: - long: base-path - short: d - value_name: PATH - help: Specify custom base path. - takes_value: true - - execution: - long: execution - value_name: STRATEGY - help: The means of execution used when calling into the runtime. Can be either wasm, native or both. - - max-heap-pages: - long: max-heap-pages - value_name: COUNT - help: The maximum number of 64KB pages to ever allocate for Wasm execution. Don't alter this unless you know what you're doing. - - revert: - about: Revert chain to the previous state - args: - - NUM: - index: 1 - help: Number of blocks to revert. Default is 256. - - chain: - long: chain - value_name: CHAIN_SPEC - help: Specify the chain specification. - takes_value: true - - base-path: - long: base-path - short: d - value_name: PATH - help: Specify custom base path. - takes_value: true - - purge-chain: - about: Remove the whole chain data. - args: - - chain: - long: chain - value_name: CHAIN_SPEC - help: Specify the chain specification. - takes_value: true - - base-path: - long: base-path - short: d - value_name: PATH - help: Specify custom base path. - takes_value: true diff --git a/substrate/cli/src/error.rs b/substrate/cli/src/error.rs deleted file mode 100644 index ec70a5b70b7c8..0000000000000 --- a/substrate/cli/src/error.rs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2017 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 . - -//! Initialization errors. - -use client; - -error_chain! { - foreign_links { - Io(::std::io::Error) #[doc="IO error"]; - Cli(::clap::Error) #[doc="CLI error"]; - Service(::service::Error) #[doc="Substrate service error"]; - } - links { - Client(client::error::Error, client::error::ErrorKind) #[doc="Client error"]; - } - errors { - /// Input error. - Input(m: String) { - description("Invalid input"), - display("{}", m), - } - } -} diff --git a/substrate/cli/src/informant.rs b/substrate/cli/src/informant.rs deleted file mode 100644 index eacc95c50dbf7..0000000000000 --- a/substrate/cli/src/informant.rs +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2017 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 . - -//! Console informant. Prints sync progress and block events. Runs on the calling thread. - -use ansi_term::Colour; -use std::time::{Duration, Instant}; -use futures::{Future, Stream}; -use service::{Service, Components}; -use tokio::runtime::TaskExecutor; -use tokio::timer::Interval; -use sysinfo::{get_current_pid, ProcessExt, System, SystemExt}; -use network::{SyncState, SyncProvider}; -use client::BlockchainEvents; -use runtime_primitives::traits::{Header, As}; - -const TIMER_INTERVAL_MS: u64 = 5000; - -/// Spawn informant on the event loop -pub fn start(service: &Service, exit: ::exit_future::Exit, handle: TaskExecutor) - where - C: Components, -{ - let interval = Interval::new(Instant::now(), Duration::from_millis(TIMER_INTERVAL_MS)); - - let network = service.network(); - let client = service.client(); - let txpool = service.extrinsic_pool(); - let mut last_number = None; - - let mut sys = System::new(); - let self_pid = get_current_pid(); - - let display_notifications = interval.map_err(|e| debug!("Timer error: {:?}", e)).for_each(move |_| { - let sync_status = network.status(); - - if let Ok(best_block) = client.best_block_header() { - let hash = best_block.hash(); - let num_peers = sync_status.num_peers; - let best_number: u64 = best_block.number().as_(); - let speed = move || speed(best_number, last_number); - let (status, target) = match (sync_status.sync.state, sync_status.sync.best_seen_block) { - (SyncState::Idle, _) => ("Idle".into(), "".into()), - (SyncState::Downloading, None) => (format!("Syncing{}", speed()), "".into()), - (SyncState::Downloading, Some(n)) => (format!("Syncing{}", speed()), format!(", target=#{}", n)), - }; - last_number = Some(best_number); - let txpool_status = txpool.light_status(); - info!( - target: "substrate", - "{}{} ({} peers), best: #{} ({})", - Colour::White.bold().paint(&status), - target, - Colour::White.bold().paint(format!("{}", sync_status.num_peers)), - Colour::White.paint(format!("{}", best_number)), - hash - ); - - // 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()) - } else { (0.0, 0) }; - - telemetry!( - "system.interval"; - "status" => format!("{}{}", status, target), - "peers" => num_peers, - "height" => best_number, - "best" => ?hash, - "txcount" => txpool_status.transaction_count, - "cpu" => cpu_usage, - "memory" => memory - ); - } else { - warn!("Error getting best block information"); - } - - Ok(()) - }); - - let client = service.client(); - let display_block_import = client.import_notification_stream().for_each(|n| { - info!(target: "substrate", "Imported #{} ({})", n.header.number(), n.hash); - Ok(()) - }); - - let txpool = service.extrinsic_pool(); - let display_txpool_import = txpool.import_notification_stream().for_each(move |_| { - let status = txpool.light_status(); - telemetry!("txpool.import"; "mem_usage" => status.mem_usage, "count" => status.transaction_count, "sender" => status.senders); - Ok(()) - }); - - let informant_work = display_notifications.join3(display_block_import, display_txpool_import); - handle.spawn(exit.until(informant_work).map(|_| ())); -} - -fn speed(best_number: u64, last_number: Option) -> String { - let speed = match last_number { - Some(num) => (best_number.saturating_sub(num) * 10_000 / TIMER_INTERVAL_MS) as f64, - None => 0.0 - }; - - if speed < 1.0 { - "".into() - } else { - format!(" {:4.1} bps", speed / 10.0) - } -} - diff --git a/substrate/cli/src/lib.rs b/substrate/cli/src/lib.rs deleted file mode 100644 index 94d8849b05b35..0000000000000 --- a/substrate/cli/src/lib.rs +++ /dev/null @@ -1,571 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -// tag::description[] -//! Substrate CLI library. -// end::description[] - -#![warn(missing_docs)] -#![warn(unused_extern_crates)] - -extern crate app_dirs; -extern crate env_logger; -extern crate atty; -extern crate ansi_term; -extern crate regex; -extern crate time; -extern crate fdlimit; -extern crate futures; -extern crate tokio; -extern crate names; -extern crate backtrace; -extern crate sysinfo; - -extern crate substrate_client as client; -extern crate substrate_network as network; -extern crate substrate_network_libp2p as network_libp2p; -extern crate substrate_runtime_primitives as runtime_primitives; -extern crate substrate_service as service; -#[macro_use] -extern crate slog; // needed until we can reexport `slog_info` from `substrate_telemetry` -#[macro_use] -extern crate substrate_telemetry; -extern crate exit_future; - -#[macro_use] -extern crate lazy_static; -extern crate clap; -#[macro_use] -extern crate error_chain; -#[macro_use] -extern crate log; - -pub mod error; -pub mod informant; -mod panic_hook; - -use network_libp2p::AddrComponent; -use runtime_primitives::traits::As; -use service::{ - ServiceFactory, FactoryFullConfiguration, RuntimeGenesis, - FactoryGenesis, PruningMode, ChainSpec, -}; -use network::NonReservedPeerMode; - -use std::io::{Write, Read, stdin, stdout}; -use std::iter; -use std::fs; -use std::fs::File; -use std::net::{Ipv4Addr, SocketAddr}; -use std::path::{Path, PathBuf}; -use names::{Generator, Name}; -use regex::Regex; - -use futures::Future; - -/// Executable version. Used to pass version information from the root crate. -pub struct VersionInfo { - /// Implementation version. - pub version: &'static str, - /// SCM Commit hash. - pub commit: &'static str, - /// Executable file name. - pub executable_name: &'static str, - /// Executable file description. - pub description: &'static str, - /// Executable file author. - pub author: &'static str, -} - -/// CLI Action -pub enum Action { - /// Substrate handled the command. No need to do anything. - ExecutedInternally, - /// Service mode requested. Caller should start the service. - RunService((FactoryFullConfiguration, E)), -} - -/// Something that can be converted into an exit signal. -pub trait IntoExit { - /// Exit signal type. - type Exit: Future + Send + 'static; - /// Convert into exit signal. - fn into_exit(self) -> Self::Exit; -} - -fn load_spec(matches: &clap::ArgMatches, factory: F) -> Result, String> - where G: RuntimeGenesis, F: FnOnce(&str) -> Result>, String>, -{ - let chain_key = matches.value_of("chain").unwrap_or_else(|| if matches.is_present("dev") { "dev" } else { "" }); - let spec = match factory(chain_key)? { - Some(spec) => spec, - None => ChainSpec::from_json_file(PathBuf::from(chain_key))? - }; - Ok(spec) -} - -fn base_path(matches: &clap::ArgMatches) -> PathBuf { - matches.value_of("base-path") - .map(|x| Path::new(x).to_owned()) - .unwrap_or_else(default_base_path) -} - -/// Check whether a node name is considered as valid -fn is_node_name_valid(_name: &str) -> Result<(), &str> { - const MAX_NODE_NAME_LENGTH: usize = 32; - let name = _name.to_string(); - if name.chars().count() >= MAX_NODE_NAME_LENGTH { - return Err("Node name too long"); - } - - let invalid_chars = r"[\\.@]"; - let re = Regex::new(invalid_chars).unwrap(); - if re.is_match(&name) { - return Err("Node name should not contain invalid chars such as '.' and '@'"); - } - - let invalid_patterns = r"(https?:\\/+)?(www)+"; - let re = Regex::new(invalid_patterns).unwrap(); - if re.is_match(&name) { - return Err("Node name should not contain urls"); - } - - Ok(()) -} - -/// Parse command line arguments and execute commands or return service configuration. -/// -/// IANA unassigned port ranges that we could use: -/// 6717-6766 Unassigned -/// 8504-8553 Unassigned -/// 9556-9591 Unassigned -/// 9803-9874 Unassigned -/// 9926-9949 Unassigned -pub fn prepare_execution( - args: I, - exit: E, - version: VersionInfo, - spec_factory: S, - impl_name: &'static str, -) -> error::Result> -where - I: IntoIterator, - T: Into + Clone, - E: IntoExit, - F: ServiceFactory, - S: FnOnce(&str) -> Result>>, String>, -{ - panic_hook::set(); - - let full_version = service::config::full_version_from_strs( - version.version, - version.commit - ); - let yaml = format!(include_str!("./cli.yml"), - name = version.executable_name, - description = version.description, - author = version.author, - ); - let yaml = &clap::YamlLoader::load_from_str(&yaml).expect("Invalid yml file")[0]; - let matches = match clap::App::from_yaml(yaml) - .version(&(full_version + "\n")[..]) - .get_matches_from_safe(args) { - Ok(m) => m, - Err(e) => e.exit(), - }; - - // TODO [ToDr] Split parameters parsing from actual execution. - let log_pattern = matches.value_of("log").unwrap_or(""); - init_logger(log_pattern); - fdlimit::raise_fd_limit(); - - if let Some(matches) = matches.subcommand_matches("build-spec") { - let spec = load_spec(&matches, spec_factory)?; - build_spec::(matches, spec)?; - return Ok(Action::ExecutedInternally); - } - - if let Some(matches) = matches.subcommand_matches("export-blocks") { - let spec = load_spec(&matches, spec_factory)?; - export_blocks::(matches, spec, exit.into_exit())?; - return Ok(Action::ExecutedInternally); - } - - if let Some(matches) = matches.subcommand_matches("import-blocks") { - let spec = load_spec(&matches, spec_factory)?; - import_blocks::(matches, spec, exit.into_exit())?; - return Ok(Action::ExecutedInternally); - } - - if let Some(matches) = matches.subcommand_matches("revert") { - let spec = load_spec(&matches, spec_factory)?; - revert_chain::(matches, spec)?; - return Ok(Action::ExecutedInternally); - } - - if let Some(matches) = matches.subcommand_matches("purge-chain") { - let spec = load_spec(&matches, spec_factory)?; - purge_chain::(matches, spec)?; - return Ok(Action::ExecutedInternally); - } - - let spec = load_spec(&matches, spec_factory)?; - let mut config = service::Configuration::default_with_spec(spec); - - config.impl_name = impl_name; - config.impl_commit = version.commit; - config.impl_version = version.version; - - config.name = match matches.value_of("name") { - None => Generator::with_naming(Name::Numbered).next().unwrap(), - Some(name) => name.into(), - }; - match is_node_name_valid(&config.name) { - Ok(_) => (), - Err(msg) => return Err(error::ErrorKind::Input( - format!("Invalid node name '{}'. Reason: {}. If unsure, use none.", config.name, msg)).into()) - } - - let base_path = base_path(&matches); - - config.keystore_path = matches.value_of("keystore") - .map(|x| Path::new(x).to_owned()) - .unwrap_or_else(|| keystore_path(&base_path, config.chain_spec.id())) - .to_string_lossy() - .into(); - - config.database_path = db_path(&base_path, config.chain_spec.id()).to_string_lossy().into(); - - config.pruning = match matches.value_of("pruning") { - Some("archive") => PruningMode::ArchiveAll, - None => PruningMode::default(), - Some(s) => PruningMode::keep_blocks(s.parse() - .map_err(|_| error::ErrorKind::Input("Invalid pruning mode specified".to_owned()))?), - }; - - let role = - if matches.is_present("light") { - config.execution_strategy = service::ExecutionStrategy::NativeWhenPossible; - service::Roles::LIGHT - } else if matches.is_present("validator") || matches.is_present("dev") { - config.execution_strategy = service::ExecutionStrategy::Both; - service::Roles::AUTHORITY - } else { - config.execution_strategy = service::ExecutionStrategy::NativeWhenPossible; - service::Roles::FULL - }; - - if let Some(s) = matches.value_of("execution") { - config.execution_strategy = match s { - "both" => service::ExecutionStrategy::Both, - "native" => service::ExecutionStrategy::NativeWhenPossible, - "wasm" => service::ExecutionStrategy::AlwaysWasm, - _ => return Err(error::ErrorKind::Input("Invalid execution mode specified".to_owned()).into()), - }; - } - - config.roles = role; - { - config.network.boot_nodes.extend(matches - .values_of("bootnodes") - .map_or(Default::default(), |v| v.map(|n| n.to_owned()).collect::>())); - config.network.config_path = Some(network_path(&base_path, config.chain_spec.id()).to_string_lossy().into()); - config.network.net_config_path = config.network.config_path.clone(); - config.network.reserved_nodes.extend(matches - .values_of("reserved-nodes") - .map_or(Default::default(), |v| v.map(|n| n.to_owned()).collect::>())); - if !config.network.reserved_nodes.is_empty() { - config.network.non_reserved_mode = NonReservedPeerMode::Deny; - } - - config.network.listen_addresses = Vec::new(); - for addr in matches.values_of("listen-addr").unwrap_or_default() { - let addr = addr.parse().map_err(|_| "Invalid listen multiaddress")?; - config.network.listen_addresses.push(addr); - } - if config.network.listen_addresses.is_empty() { - let port = match matches.value_of("port") { - Some(port) => port.parse().map_err(|_| "Invalid p2p port value specified.")?, - None => 30333, - }; - config.network.listen_addresses = vec![ - iter::once(AddrComponent::IP4(Ipv4Addr::new(0, 0, 0, 0))) - .chain(iter::once(AddrComponent::TCP(port))) - .collect() - ]; - } - - config.network.public_addresses = Vec::new(); - - config.network.client_version = config.client_id(); - config.network.use_secret = match matches.value_of("node-key").map(|s| s.parse()) { - Some(Ok(secret)) => Some(secret), - Some(Err(err)) => return Err(format!("Error parsing node key: {}", err).into()), - None => None, - }; - - let min_peers = match matches.value_of("min-peers") { - Some(min_peers) => min_peers.parse().map_err(|_| "Invalid min-peers value specified.")?, - None => 25, - }; - let max_peers = match matches.value_of("max-peers") { - Some(max_peers) => max_peers.parse().map_err(|_| "Invalid max-peers value specified.")?, - None => 50, - }; - if min_peers > max_peers { - return Err(error::ErrorKind::Input("Min-peers mustn't be larger than max-peers.".to_owned()).into()); - } - config.network.min_peers = min_peers; - config.network.max_peers = max_peers; - } - - config.keys = matches.values_of("key").unwrap_or_default().map(str::to_owned).collect(); - if matches.is_present("dev") { - config.keys.push("Alice".into()); - } - - let rpc_interface: &str = if matches.is_present("rpc-external") { "0.0.0.0" } else { "127.0.0.1" }; - let ws_interface: &str = if matches.is_present("ws-external") { "0.0.0.0" } else { "127.0.0.1" }; - - config.rpc_http = Some(parse_address(&format!("{}:{}", rpc_interface, 9933), "rpc-port", &matches)?); - config.rpc_ws = Some(parse_address(&format!("{}:{}", ws_interface, 9944), "ws-port", &matches)?); - - // Override telemetry - if matches.is_present("no-telemetry") { - config.telemetry_url = None; - } else if let Some(url) = matches.value_of("telemetry-url") { - config.telemetry_url = Some(url.to_owned()); - } - - Ok(Action::RunService((config, exit))) -} - -fn build_spec(matches: &clap::ArgMatches, spec: ChainSpec>) -> error::Result<()> - where F: ServiceFactory, -{ - info!("Building chain spec"); - let raw = matches.is_present("raw"); - let json = service::chain_ops::build_spec::>(spec, raw)?; - print!("{}", json); - Ok(()) -} - -fn export_blocks(matches: &clap::ArgMatches, spec: ChainSpec>, exit: E) -> error::Result<()> - where F: ServiceFactory, E: Future + Send + 'static, -{ - let base_path = base_path(matches); - let mut config = service::Configuration::default_with_spec(spec); - config.database_path = db_path(&base_path, config.chain_spec.id()).to_string_lossy().into(); - info!("DB path: {}", config.database_path); - let from: u64 = match matches.value_of("from") { - Some(v) => v.parse().map_err(|_| "Invalid --from argument")?, - None => 1, - }; - - let to: Option = match matches.value_of("to") { - Some(v) => Some(v.parse().map_err(|_| "Invalid --to argument")?), - None => None, - }; - let json = matches.is_present("json"); - - let file: Box = match matches.value_of("OUTPUT") { - Some(filename) => Box::new(File::create(filename)?), - None => Box::new(stdout()), - }; - - Ok(service::chain_ops::export_blocks::(config, exit, file, As::sa(from), to.map(As::sa), json)?) -} - -fn import_blocks(matches: &clap::ArgMatches, spec: ChainSpec>, exit: E) -> error::Result<()> - where F: ServiceFactory, E: Future + Send + 'static, -{ - let base_path = base_path(matches); - let mut config = service::Configuration::default_with_spec(spec); - config.database_path = db_path(&base_path, config.chain_spec.id()).to_string_lossy().into(); - - if let Some(s) = matches.value_of("execution") { - config.execution_strategy = match s { - "both" => service::ExecutionStrategy::Both, - "native" => service::ExecutionStrategy::NativeWhenPossible, - "wasm" => service::ExecutionStrategy::AlwaysWasm, - _ => return Err(error::ErrorKind::Input("Invalid execution mode specified".to_owned()).into()), - }; - } - - let file: Box = match matches.value_of("INPUT") { - Some(filename) => Box::new(File::open(filename)?), - None => Box::new(stdin()), - }; - - Ok(service::chain_ops::import_blocks::(config, exit, file)?) -} - -fn revert_chain(matches: &clap::ArgMatches, spec: ChainSpec>) -> error::Result<()> - where F: ServiceFactory, -{ - let base_path = base_path(matches); - let mut config = service::Configuration::default_with_spec(spec); - config.database_path = db_path(&base_path, config.chain_spec.id()).to_string_lossy().into(); - - let blocks = match matches.value_of("NUM") { - Some(v) => v.parse().map_err(|_| "Invalid block count specified")?, - None => 256, - }; - - Ok(service::chain_ops::revert_chain::(config, As::sa(blocks))?) -} - -fn purge_chain(matches: &clap::ArgMatches, spec: ChainSpec>) -> error::Result<()> - where F: ServiceFactory, -{ - let base_path = base_path(matches); - let database_path = db_path(&base_path, spec.id()); - - print!("Are you sure to remove {:?}? (y/n)", &database_path); - stdout().flush().expect("failed to flush stdout"); - - let mut input = String::new(); - stdin().read_line(&mut input)?; - let input = input.trim(); - - match input.chars().nth(0) { - Some('y') | Some('Y') => { - fs::remove_dir_all(&database_path)?; - println!("{:?} removed.", &database_path); - }, - _ => println!("Aborted"), - } - - Ok(()) -} - -fn parse_address(default: &str, port_param: &str, matches: &clap::ArgMatches) -> Result { - let mut address: SocketAddr = default.parse().ok().ok_or_else(|| format!("Invalid address specified for --{}.", port_param))?; - if let Some(port) = matches.value_of(port_param) { - let port: u16 = port.parse().ok().ok_or_else(|| format!("Invalid port for --{} specified.", port_param))?; - address.set_port(port); - } - - Ok(address) -} - -fn keystore_path(base_path: &Path, chain_id: &str) -> PathBuf { - let mut path = base_path.to_owned(); - path.push("chains"); - path.push(chain_id); - path.push("keystore"); - path -} - -fn db_path(base_path: &Path, chain_id: &str) -> PathBuf { - let mut path = base_path.to_owned(); - path.push("chains"); - path.push(chain_id); - path.push("db"); - path -} - -fn network_path(base_path: &Path, chain_id: &str) -> PathBuf { - let mut path = base_path.to_owned(); - path.push("chains"); - path.push(chain_id); - path.push("network"); - path -} - -fn default_base_path() -> PathBuf { - use app_dirs::{AppInfo, AppDataType}; - - let app_info = AppInfo { - name: "Polkadot", - author: "Parity Technologies", - }; - - app_dirs::get_app_root( - AppDataType::UserData, - &app_info, - ).expect("app directories exist on all supported platforms; qed") -} - -fn init_logger(pattern: &str) { - use ansi_term::Colour; - - let mut builder = env_logger::LogBuilder::new(); - // Disable info logging by default for some modules: - builder.filter(Some("ws"), log::LogLevelFilter::Warn); - builder.filter(Some("hyper"), log::LogLevelFilter::Warn); - // Enable info for others. - builder.filter(None, log::LogLevelFilter::Info); - - if let Ok(lvl) = std::env::var("RUST_LOG") { - builder.parse(&lvl); - } - - builder.parse(pattern); - let isatty = atty::is(atty::Stream::Stderr); - let enable_color = isatty; - - let format = move |record: &log::LogRecord| { - let timestamp = time::strftime("%Y-%m-%d %H:%M:%S", &time::now()).expect("Error formatting log timestamp"); - - let mut output = if log::max_log_level() <= log::LogLevelFilter::Info { - format!("{} {}", Colour::Black.bold().paint(timestamp), record.args()) - } else { - let name = ::std::thread::current().name().map_or_else(Default::default, |x| format!("{}", Colour::Blue.bold().paint(x))); - format!("{} {} {} {} {}", Colour::Black.bold().paint(timestamp), name, record.level(), record.target(), record.args()) - }; - - if !enable_color { - output = kill_color(output.as_ref()); - } - - if !isatty && record.level() <= log::LogLevel::Info && atty::is(atty::Stream::Stdout) { - // duplicate INFO/WARN output to console - println!("{}", output); - } - output - }; - builder.format(format); - - builder.init().expect("Logger initialized only once."); -} - -fn kill_color(s: &str) -> String { - lazy_static! { - static ref RE: Regex = Regex::new("\x1b\\[[^m]+m").expect("Error initializing color regex"); - } - RE.replace_all(s, "").to_string() -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn tests_node_name_good() { - assert!(is_node_name_valid("short name").is_ok()); - } - - #[test] - fn tests_node_name_bad() { - assert!(is_node_name_valid("long names are not very cool for the ui").is_err()); - assert!(is_node_name_valid("Dots.not.Ok").is_err()); - assert!(is_node_name_valid("http://visit.me").is_err()); - assert!(is_node_name_valid("https://visit.me").is_err()); - assert!(is_node_name_valid("www.visit.me").is_err()); - assert!(is_node_name_valid("email@domain").is_err()); - } -} diff --git a/substrate/cli/src/panic_hook.rs b/substrate/cli/src/panic_hook.rs deleted file mode 100644 index c7cea5c8537b0..0000000000000 --- a/substrate/cli/src/panic_hook.rs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2017 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 . - -//! Custom panic hook with bug report link - -use backtrace::Backtrace; -use std::io::{self, Write}; -use std::panic::{self, PanicInfo}; -use std::thread; - -/// Set the panic hook -pub fn set() { - panic::set_hook(Box::new(panic_hook)); -} - -static ABOUT_PANIC: &str = " -This is a bug. Please report it at: - - https://github.com/paritytech/polkadot/issues/new -"; - -fn panic_hook(info: &PanicInfo) { - let location = info.location(); - let file = location.as_ref().map(|l| l.file()).unwrap_or(""); - let line = location.as_ref().map(|l| l.line()).unwrap_or(0); - - let msg = match info.payload().downcast_ref::<&'static str>() { - Some(s) => *s, - None => match info.payload().downcast_ref::() { - Some(s) => &s[..], - None => "Box", - } - }; - - let thread = thread::current(); - let name = thread.name().unwrap_or(""); - - let backtrace = Backtrace::new(); - - let mut stderr = io::stderr(); - - let _ = writeln!(stderr, ""); - let _ = writeln!(stderr, "===================="); - let _ = writeln!(stderr, ""); - let _ = writeln!(stderr, "{:?}", backtrace); - let _ = writeln!(stderr, ""); - let _ = writeln!( - stderr, - "Thread '{}' panicked at '{}', {}:{}", - name, msg, file, line - ); - - let _ = writeln!(stderr, "{}", ABOUT_PANIC); - ::std::process::exit(1); -} - diff --git a/substrate/client/Cargo.toml b/substrate/client/Cargo.toml deleted file mode 100644 index 5512b0d65832a..0000000000000 --- a/substrate/client/Cargo.toml +++ /dev/null @@ -1,32 +0,0 @@ -[package] -name = "substrate-client" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -error-chain = "0.12" -fnv = "1.0" -log = "0.3" -parking_lot = "0.4" -triehash = "0.2" -hex-literal = "0.1" -futures = "0.1.17" -ed25519 = { path = "../ed25519" } -slog = "^2" -heapsize = "0.4" -substrate-bft = { path = "../bft" } -substrate-codec = { path = "../codec" } -substrate-executor = { path = "../executor" } -substrate-primitives = { path = "../primitives" } -substrate-runtime-io = { path = "../runtime-io" } -substrate-runtime-support = { path = "../runtime-support" } -substrate-runtime-primitives = { path = "../runtime/primitives" } -substrate-state-machine = { path = "../state-machine" } -substrate-keyring = { path = "../../substrate/keyring" } -substrate-telemetry = { path = "../telemetry" } -hashdb = "0.2.1" -patricia-trie = "0.2.1" -rlp = "0.2.4" - -[dev-dependencies] -substrate-test-client = { path = "../test-client" } diff --git a/substrate/client/README.adoc b/substrate/client/README.adoc deleted file mode 100644 index d644b1d039b11..0000000000000 --- a/substrate/client/README.adoc +++ /dev/null @@ -1,13 +0,0 @@ - -= Client - -.Summary -[source, toml] ----- -include::Cargo.toml[lines=2..5] ----- - -.Description ----- -include::src/lib.rs[tag=description] ----- diff --git a/substrate/client/db/Cargo.toml b/substrate/client/db/Cargo.toml deleted file mode 100644 index c88e338970e39..0000000000000 --- a/substrate/client/db/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "substrate-client-db" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -parking_lot = "0.4" -log = "0.3" -kvdb = "0.1" -kvdb-rocksdb = "0.1.3" -hashdb = "0.2.1" -memorydb = "0.2.1" -substrate-primitives = { path = "../../../substrate/primitives" } -substrate-runtime-primitives = { path = "../../../substrate/runtime/primitives" } -substrate-client = { path = "../../../substrate/client" } -substrate-state-machine = { path = "../../../substrate/state-machine" } -substrate-runtime-support = { path = "../../../substrate/runtime-support" } -substrate-codec = { path = "../../../substrate/codec" } -substrate-codec-derive = { path = "../../../substrate/codec/derive" } -substrate-executor = { path = "../../../substrate/executor" } -substrate-state-db = { path = "../../../substrate/state-db" } - -[dev-dependencies] -kvdb-memorydb = "0.1" diff --git a/substrate/client/db/src/cache.rs b/substrate/client/db/src/cache.rs deleted file mode 100644 index 194e307e069ae..0000000000000 --- a/substrate/client/db/src/cache.rs +++ /dev/null @@ -1,432 +0,0 @@ -// Copyright 2017 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 . - -//! DB-backed cache of blockchain data. - -use std::sync::Arc; -use parking_lot::RwLock; - -use kvdb::{KeyValueDB, DBTransaction}; - -use client::blockchain::Cache as BlockchainCache; -use client::error::Result as ClientResult; -use codec::{Codec, Encode, Decode}; -use primitives::AuthorityId; -use runtime_primitives::generic::BlockId; -use runtime_primitives::traits::{Block as BlockT, As, NumberFor}; -use utils::{COLUMN_META, BlockKey, db_err, meta_keys, read_id, db_key_to_number, number_to_db_key}; - -/// Database-backed cache of blockchain data. -pub struct DbCache { - db: Arc, - block_index_column: Option, - authorities_at: DbCacheList>, -} - -impl DbCache - where - Block: BlockT, - NumberFor: As, -{ - /// Create new cache. - pub fn new( - db: Arc, - block_index_column: Option, - authorities_column: Option - ) -> ClientResult { - Ok(DbCache { - db: db.clone(), - block_index_column, - authorities_at: DbCacheList::new(db, meta_keys::BEST_AUTHORITIES, authorities_column)?, - }) - } - - /// Get authorities_cache. - pub fn authorities_at_cache(&self) -> &DbCacheList> { - &self.authorities_at - } -} - -impl BlockchainCache for DbCache - where - Block: BlockT, - NumberFor: As, -{ - fn authorities_at(&self, at: BlockId) -> Option> { - let authorities_at = read_id(&*self.db, self.block_index_column, at).and_then(|at| match at { - Some(at) => self.authorities_at.value_at_key(at), - None => Ok(None), - }); - - match authorities_at { - Ok(authorities) => authorities, - Err(error) => { - warn!("Trying to read authorities from db cache has failed with: {}", error); - None - }, - } - } -} - -/// Database-backed blockchain cache which holds its entries as a list. -/// The meta column holds the pointer to the best known cache entry and -/// every entry points to the previous entry. -/// New entry appears when the set of authorities changes in block, so the -/// best entry here means the entry that is valid for the best block (and -/// probably for its ascendants). -pub struct DbCacheList { - db: Arc, - meta_key: &'static [u8], - column: Option, - /// Best entry at the moment. None means that cache has no entries at all. - best_entry: RwLock, T>>>, -} - -/// Single cache entry. -#[derive(Clone)] -#[cfg_attr(test, derive(Debug, PartialEq))] -pub struct Entry { - /// first block, when this value became actual - valid_from: N, - /// None means that we do not know the value starting from `valid_from` block - value: Option, -} - -/// Internal representation of the single cache entry. The entry points to the -/// previous entry in the cache, allowing us to traverse back in time in list-style. -#[derive(Encode, Decode)] -#[cfg_attr(test, derive(Debug, PartialEq))] -struct StorageEntry { - /// None if valid from the beginning - prev_valid_from: Option, - /// None means that we do not know the value starting from `valid_from` block - value: Option, -} - -impl DbCacheList - where - Block: BlockT, - NumberFor: As, - T: Clone + PartialEq + Codec, -{ - /// Creates new cache list. - fn new(db: Arc, meta_key: &'static [u8], column: Option) -> ClientResult { - let best_entry = RwLock::new(db.get(COLUMN_META, meta_key) - .map_err(db_err) - .and_then(|block| match block { - Some(block) => { - let valid_from = db_key_to_number(&block)?; - read_storage_entry::(&*db, column, valid_from) - .map(|entry| Some(Entry { - valid_from, - value: entry - .expect("meta entry references the entry at the block; storage entry at block exists when referenced; qed") - .value, - })) - }, - None => Ok(None), - })?); - - Ok(DbCacheList { - db, - column, - meta_key, - best_entry, - }) - } - - /// Gets the best known entry. - pub fn best_entry(&self) -> Option, T>> { - self.best_entry.read().clone() - } - - /// Commits the new best pending value to the database. Returns Some if best entry must - /// be updated after transaction is committed. - pub fn commit_best_entry( - &self, - transaction: &mut DBTransaction, - valid_from: NumberFor, - pending_value: Option - ) -> Option, T>> { - let best_entry = self.best_entry(); - let update_best_entry = match ( - best_entry.as_ref().and_then(|a| a.value.as_ref()), - pending_value.as_ref() - ) { - (Some(best_value), Some(pending_value)) => best_value != pending_value, - (None, Some(_)) | (Some(_), None) => true, - (None, None) => false, - }; - if !update_best_entry { - return None; - } - - let valid_from_key = number_to_db_key(valid_from); - transaction.put(COLUMN_META, self.meta_key, &valid_from_key); - transaction.put(self.column, &valid_from_key, &StorageEntry { - prev_valid_from: best_entry.map(|b| b.valid_from), - value: pending_value.clone(), - }.encode()); - - Some(Entry { - valid_from, - value: pending_value, - }) - } - - /// Updates the best in-memory cache entry. Must be called after transaction with changes - /// from commit_best_entry has been committed. - pub fn update_best_entry(&self, best_entry: Option, T>>) { - *self.best_entry.write() = best_entry; - } - - /// Prune all entries from the beginning up to the block (including entry at the number). Returns - /// the number of pruned entries. Pruning never deletes the latest entry in the cache. - pub fn prune_entries( - &self, - transaction: &mut DBTransaction, - last_to_prune: NumberFor - ) -> ClientResult { - // find the last entry we want to keep - let mut last_entry_to_keep = match self.best_entry() { - Some(best_entry) => best_entry.valid_from, - None => return Ok(0), - }; - let mut first_entry_to_remove = last_entry_to_keep; - while first_entry_to_remove > last_to_prune { - last_entry_to_keep = first_entry_to_remove; - - let entry = read_storage_entry::(&*self.db, self.column, first_entry_to_remove)? - .expect("entry referenced from the next entry; entry exists when referenced; qed"); - // if we have reached the first list entry - // AND all list entries are for blocks that are later than last_to_prune - // => nothing to prune - first_entry_to_remove = match entry.prev_valid_from { - Some(prev_valid_from) => prev_valid_from, - None => return Ok(0), - } - } - - // remove all entries, starting from entry_to_remove - let mut pruned = 0; - let mut entry_to_remove = Some(first_entry_to_remove); - while let Some(current_entry) = entry_to_remove { - let entry = read_storage_entry::(&*self.db, self.column, current_entry)? - .expect("referenced entry exists; entry_to_remove is a reference to the entry; qed"); - - if current_entry != last_entry_to_keep { - transaction.delete(self.column, &number_to_db_key(current_entry)); - pruned += 1; - } - entry_to_remove = entry.prev_valid_from; - } - - let mut entry = read_storage_entry::(&*self.db, self.column, last_entry_to_keep)? - .expect("last_entry_to_keep >= first_entry_to_remove; that means that we're leaving this entry in the db; qed"); - entry.prev_valid_from = None; - transaction.put(self.column, &number_to_db_key(last_entry_to_keep), &entry.encode()); - - Ok(pruned) - } - - /// Reads the cached value, actual at given block. Returns None if the value was not cached - /// or if it has been pruned. - fn value_at_key(&self, key: BlockKey) -> ClientResult> { - let at = db_key_to_number::>(&key)?; - let best_valid_from = match self.best_entry() { - // there are entries in cache - Some(best_entry) => { - // we're looking for the best value - if at >= best_entry.valid_from { - return Ok(best_entry.value); - } - - // we're looking for the value of older blocks - best_entry.valid_from - }, - // there are no entries in the cache - None => return Ok(None), - }; - - let mut entry = read_storage_entry::(&*self.db, self.column, best_valid_from)? - .expect("self.best_entry().is_some() if there's entry for best_valid_from; qed"); - loop { - let prev_valid_from = match entry.prev_valid_from { - Some(prev_valid_from) => prev_valid_from, - None => return Ok(None), - }; - - let prev_entry = read_storage_entry::(&*self.db, self.column, prev_valid_from)? - .expect("entry referenced from the next entry; entry exists when referenced; qed"); - if at >= prev_valid_from { - return Ok(prev_entry.value); - } - - entry = prev_entry; - } - } -} - -/// Reads the entry at the block with given number. -fn read_storage_entry( - db: &KeyValueDB, - column: Option, - number: NumberFor -) -> ClientResult, T>>> - where - Block: BlockT, - NumberFor: As, - T: Codec, -{ - db.get(column, &number_to_db_key(number)) - .and_then(|entry| match entry { - Some(entry) => Ok(StorageEntry::, T>::decode(&mut &entry[..])), - None => Ok(None), - }) - .map_err(db_err) -} - -#[cfg(test)] -mod tests { - use runtime_primitives::testing::Block as RawBlock; - use light::{AUTHORITIES_ENTRIES_TO_KEEP, columns, LightStorage}; - use light::tests::insert_block; - use super::*; - - type Block = RawBlock; - - #[test] - fn authorities_storage_entry_serialized() { - let test_cases: Vec>> = vec![ - StorageEntry { prev_valid_from: Some(42), value: Some(vec![[1u8; 32].into()]) }, - StorageEntry { prev_valid_from: None, value: Some(vec![[1u8; 32].into(), [2u8; 32].into()]) }, - StorageEntry { prev_valid_from: None, value: None }, - ]; - - for expected in test_cases { - let serialized = expected.encode(); - let deserialized = StorageEntry::decode(&mut &serialized[..]).unwrap(); - assert_eq!(expected, deserialized); - } - } - - #[test] - fn best_authorities_are_updated() { - let db = LightStorage::new_test(); - let authorities_at: Vec<(usize, Option>>)> = vec![ - (0, None), - (0, None), - (1, Some(Entry { valid_from: 1, value: Some(vec![[2u8; 32].into()]) })), - (1, Some(Entry { valid_from: 1, value: Some(vec![[2u8; 32].into()]) })), - (2, Some(Entry { valid_from: 3, value: Some(vec![[4u8; 32].into()]) })), - (2, Some(Entry { valid_from: 3, value: Some(vec![[4u8; 32].into()]) })), - (3, Some(Entry { valid_from: 5, value: None })), - (3, Some(Entry { valid_from: 5, value: None })), - ]; - - // before any block, there are no entries in cache - assert!(db.cache().authorities_at_cache().best_entry().is_none()); - assert_eq!(db.db().iter(columns::AUTHORITIES).count(), 0); - - // insert blocks and check that best_authorities() returns correct result - let mut prev_hash = Default::default(); - for number in 0..authorities_at.len() { - let authorities_at_number = authorities_at[number].1.clone().and_then(|e| e.value); - prev_hash = insert_block(&db, &prev_hash, number as u64, authorities_at_number); - assert_eq!(db.cache().authorities_at_cache().best_entry(), authorities_at[number].1); - assert_eq!(db.db().iter(columns::AUTHORITIES).count(), authorities_at[number].0); - } - - // check that authorities_at() returns correct results for all retrospective blocks - for number in 1..authorities_at.len() + 1 { - assert_eq!(db.cache().authorities_at(BlockId::Number(number as u64)), - authorities_at.get(number + 1) - .or_else(|| authorities_at.last()) - .unwrap().1.clone().and_then(|e| e.value)); - } - - // now check that cache entries are pruned when new blocks are inserted - let mut current_entries_count = authorities_at.last().unwrap().0; - let pruning_starts_at = AUTHORITIES_ENTRIES_TO_KEEP as usize; - for number in authorities_at.len()..authorities_at.len() + pruning_starts_at { - prev_hash = insert_block(&db, &prev_hash, number as u64, None); - if number > pruning_starts_at { - let prev_entries_count = authorities_at[number - pruning_starts_at].0; - let entries_count = authorities_at.get(number - pruning_starts_at + 1).map(|e| e.0) - .unwrap_or_else(|| authorities_at.last().unwrap().0); - current_entries_count -= entries_count - prev_entries_count; - } - - // there's always at least 1 entry in the cache (after first insertion) - assert_eq!(db.db().iter(columns::AUTHORITIES).count(), ::std::cmp::max(current_entries_count, 1)); - } - } - - #[test] - fn best_authorities_are_pruned() { - let db = LightStorage::::new_test(); - let mut transaction = DBTransaction::new(); - - // insert first entry at block#100 - db.cache().authorities_at_cache().update_best_entry( - db.cache().authorities_at_cache().commit_best_entry(&mut transaction, 100, Some(vec![[1u8; 32].into()]))); - db.db().write(transaction).unwrap(); - - // no entries are pruned, since there's only one entry in the cache - let mut transaction = DBTransaction::new(); - assert_eq!(db.cache().authorities_at_cache().prune_entries(&mut transaction, 50).unwrap(), 0); - assert_eq!(db.cache().authorities_at_cache().prune_entries(&mut transaction, 100).unwrap(), 0); - assert_eq!(db.cache().authorities_at_cache().prune_entries(&mut transaction, 150).unwrap(), 0); - - // insert second entry at block#200 - let mut transaction = DBTransaction::new(); - db.cache().authorities_at_cache().update_best_entry( - db.cache().authorities_at_cache().commit_best_entry(&mut transaction, 200, Some(vec![[2u8; 32].into()]))); - db.db().write(transaction).unwrap(); - - let mut transaction = DBTransaction::new(); - assert_eq!(db.cache().authorities_at_cache().prune_entries(&mut transaction, 50).unwrap(), 0); - assert_eq!(db.cache().authorities_at_cache().prune_entries(&mut transaction, 100).unwrap(), 1); - assert_eq!(db.cache().authorities_at_cache().prune_entries(&mut transaction, 150).unwrap(), 1); - // still only 1 entry is removed since pruning never deletes the last entry - assert_eq!(db.cache().authorities_at_cache().prune_entries(&mut transaction, 200).unwrap(), 1); - assert_eq!(db.cache().authorities_at_cache().prune_entries(&mut transaction, 250).unwrap(), 1); - - // physically remove entry for block#100 from db - let mut transaction = DBTransaction::new(); - assert_eq!(db.cache().authorities_at_cache().prune_entries(&mut transaction, 150).unwrap(), 1); - db.db().write(transaction).unwrap(); - - assert_eq!(db.cache().authorities_at_cache().best_entry().unwrap().value, Some(vec![[2u8; 32].into()])); - assert_eq!(db.cache().authorities_at(BlockId::Number(50)), None); - assert_eq!(db.cache().authorities_at(BlockId::Number(100)), None); - assert_eq!(db.cache().authorities_at(BlockId::Number(150)), None); - assert_eq!(db.cache().authorities_at(BlockId::Number(200)), Some(vec![[2u8; 32].into()])); - assert_eq!(db.cache().authorities_at(BlockId::Number(250)), Some(vec![[2u8; 32].into()])); - - // try to delete last entry => failure (no entries are removed) - let mut transaction = DBTransaction::new(); - assert_eq!(db.cache().authorities_at_cache().prune_entries(&mut transaction, 300).unwrap(), 0); - db.db().write(transaction).unwrap(); - - assert_eq!(db.cache().authorities_at_cache().best_entry().unwrap().value, Some(vec![[2u8; 32].into()])); - assert_eq!(db.cache().authorities_at(BlockId::Number(50)), None); - assert_eq!(db.cache().authorities_at(BlockId::Number(100)), None); - assert_eq!(db.cache().authorities_at(BlockId::Number(150)), None); - assert_eq!(db.cache().authorities_at(BlockId::Number(200)), Some(vec![[2u8; 32].into()])); - assert_eq!(db.cache().authorities_at(BlockId::Number(250)), Some(vec![[2u8; 32].into()])); - } -} diff --git a/substrate/client/db/src/lib.rs b/substrate/client/db/src/lib.rs deleted file mode 100644 index acbd935ce8c32..0000000000000 --- a/substrate/client/db/src/lib.rs +++ /dev/null @@ -1,715 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -// tag::description[] -//! Client backend that uses RocksDB database as storage. -// end::description[] - -extern crate substrate_client as client; -extern crate kvdb_rocksdb; -extern crate kvdb; -extern crate hashdb; -extern crate memorydb; -extern crate parking_lot; -extern crate substrate_state_machine as state_machine; -extern crate substrate_primitives as primitives; -extern crate substrate_runtime_support as runtime_support; -extern crate substrate_runtime_primitives as runtime_primitives; -extern crate substrate_codec as codec; -extern crate substrate_executor as executor; -extern crate substrate_state_db as state_db; - -#[macro_use] -extern crate log; - -#[macro_use] -extern crate substrate_codec_derive; - -#[cfg(test)] -extern crate kvdb_memorydb; - -pub mod light; - -mod cache; -mod utils; - -use std::sync::Arc; -use std::path::PathBuf; -use std::io; - -use codec::{Decode, Encode}; -use hashdb::Hasher; -use kvdb::{KeyValueDB, DBTransaction}; -use memorydb::MemoryDB; -use parking_lot::RwLock; -use primitives::{H256, AuthorityId, Blake2Hasher, RlpCodec}; -use runtime_primitives::generic::BlockId; -use runtime_primitives::bft::Justification; -use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, As, Hash, HashFor, NumberFor, Zero}; -use runtime_primitives::BuildStorage; -use state_machine::backend::Backend as StateBackend; -use executor::RuntimeInfo; -use state_machine::{CodeExecutor, DBValue, ExecutionStrategy}; -use utils::{Meta, db_err, meta_keys, number_to_db_key, db_key_to_number, open_database, - read_db, read_id, read_meta}; -use state_db::StateDb; -pub use state_db::PruningMode; - -const FINALIZATION_WINDOW: u64 = 32; - -/// DB-backed patricia trie state, transaction type is an overlay of changes to commit. -pub type DbState = state_machine::TrieBackend; - -/// Database settings. -pub struct DatabaseSettings { - /// Cache size in bytes. If `None` default is used. - pub cache_size: Option, - /// Path to the database. - pub path: PathBuf, - /// Pruning mode. - pub pruning: PruningMode, -} - -/// Create an instance of db-backed client. -pub fn new_client( - settings: DatabaseSettings, - executor: E, - genesis_storage: S, - execution_strategy: ExecutionStrategy, -) -> Result, client::LocalCallExecutor, E>, Block>, client::error::Error> - where - Block: BlockT, - E: CodeExecutor + RuntimeInfo, - S: BuildStorage, -{ - let backend = Arc::new(Backend::new(settings, FINALIZATION_WINDOW)?); - let executor = client::LocalCallExecutor::new(backend.clone(), executor); - Ok(client::Client::new(backend, executor, genesis_storage, execution_strategy)?) -} - -mod columns { - pub const META: Option = Some(0); - pub const STATE: Option = Some(1); - pub const STATE_META: Option = Some(2); - pub const BLOCK_INDEX: Option = Some(3); - pub const HEADER: Option = Some(4); - pub const BODY: Option = Some(5); - pub const JUSTIFICATION: Option = Some(6); -} - -struct PendingBlock { - header: Block::Header, - justification: Option>, - body: Option>, - is_best: bool, -} - -// wrapper that implements trait required for state_db -struct StateMetaDb<'a>(&'a KeyValueDB); - -impl<'a> state_db::MetaDb for StateMetaDb<'a> { - type Error = io::Error; - - fn get_meta(&self, key: &[u8]) -> Result>, Self::Error> { - self.0.get(columns::STATE_META, key).map(|r| r.map(|v| v.to_vec())) - } -} - -/// Block database -pub struct BlockchainDb { - db: Arc, - meta: RwLock::Number, Block::Hash>>, -} - -impl BlockchainDb { - fn new(db: Arc) -> Result { - let meta = read_meta::(&*db, columns::HEADER)?; - Ok(BlockchainDb { - db, - meta: RwLock::new(meta) - }) - } - - fn update_meta(&self, hash: Block::Hash, number: ::Number, is_best: bool) { - if is_best { - let mut meta = self.meta.write(); - if number == Zero::zero() { - meta.genesis_hash = hash; - } - meta.best_number = number; - meta.best_hash = hash; - } - } -} - -impl client::blockchain::HeaderBackend for BlockchainDb { - fn header(&self, id: BlockId) -> Result, client::error::Error> { - match read_db(&*self.db, columns::BLOCK_INDEX, columns::HEADER, id)? { - Some(header) => match Block::Header::decode(&mut &header[..]) { - Some(header) => Ok(Some(header)), - None => return Err(client::error::ErrorKind::Backend("Error decoding header".into()).into()), - } - None => Ok(None), - } - } - - fn info(&self) -> Result, client::error::Error> { - let meta = self.meta.read(); - Ok(client::blockchain::Info { - best_hash: meta.best_hash, - best_number: meta.best_number, - genesis_hash: meta.genesis_hash, - }) - } - - fn status(&self, id: BlockId) -> Result { - let exists = match id { - BlockId::Hash(_) => read_id(&*self.db, columns::BLOCK_INDEX, id)?.is_some(), - BlockId::Number(n) => n <= self.meta.read().best_number, - }; - match exists { - true => Ok(client::blockchain::BlockStatus::InChain), - false => Ok(client::blockchain::BlockStatus::Unknown), - } - } - - fn number(&self, hash: Block::Hash) -> Result::Number>, client::error::Error> { - read_id::(&*self.db, columns::BLOCK_INDEX, BlockId::Hash(hash)) - .and_then(|key| match key { - Some(key) => Ok(Some(db_key_to_number(&key)?)), - None => Ok(None), - }) - } - - fn hash(&self, number: ::Number) -> Result, client::error::Error> { - read_db::(&*self.db, columns::BLOCK_INDEX, columns::HEADER, BlockId::Number(number)).map(|x| - x.map(|raw| HashFor::::hash(&raw[..])).map(Into::into) - ) - } -} - -impl client::blockchain::Backend for BlockchainDb { - fn body(&self, id: BlockId) -> Result>, client::error::Error> { - match read_db(&*self.db, columns::BLOCK_INDEX, columns::BODY, id)? { - Some(body) => match Decode::decode(&mut &body[..]) { - Some(body) => Ok(Some(body)), - None => return Err(client::error::ErrorKind::Backend("Error decoding body".into()).into()), - } - None => Ok(None), - } - } - - fn justification(&self, id: BlockId) -> Result>, client::error::Error> { - match read_db(&*self.db, columns::BLOCK_INDEX, columns::JUSTIFICATION, id)? { - Some(justification) => match Decode::decode(&mut &justification[..]) { - Some(justification) => Ok(Some(justification)), - None => return Err(client::error::ErrorKind::Backend("Error decoding justification".into()).into()), - } - None => Ok(None), - } - } - - fn cache(&self) -> Option<&client::blockchain::Cache> { - None - } -} - -/// Database transaction -pub struct BlockImportOperation { - old_state: DbState, - updates: MemoryDB, - pending_block: Option>, -} - -impl client::backend::BlockImportOperation -for BlockImportOperation -where Block: BlockT, -{ - type State = DbState; - - fn state(&self) -> Result, client::error::Error> { - Ok(Some(&self.old_state)) - } - - fn set_block_data(&mut self, header: Block::Header, body: Option>, justification: Option>, is_best: bool) -> Result<(), client::error::Error> { - assert!(self.pending_block.is_none(), "Only one block per operation is allowed"); - self.pending_block = Some(PendingBlock { - header, - body, - justification, - is_best, - }); - Ok(()) - } - - fn update_authorities(&mut self, _authorities: Vec) { - // currently authorities are not cached on full nodes - } - - fn update_storage(&mut self, update: MemoryDB) -> Result<(), client::error::Error> { - self.updates = update; - Ok(()) - } - - fn reset_storage, Vec)>>(&mut self, iter: I) -> Result<(), client::error::Error> { - // TODO: wipe out existing trie. - let (_, update) = self.old_state.storage_root(iter.into_iter().map(|(k, v)| (k, Some(v)))); - self.updates = update; - Ok(()) - } -} - -struct StorageDb { - pub db: Arc, - pub state_db: StateDb, -} - -impl state_machine::Storage for StorageDb { - fn get(&self, key: &H256) -> Result, String> { - self.state_db.get(&key.0.into(), self).map(|r| r.map(|v| DBValue::from_slice(&v))) - .map_err(|e| format!("Database backend error: {:?}", e)) - } -} - -impl state_db::HashDb for StorageDb { - type Error = io::Error; - type Hash = H256; - - fn get(&self, key: &H256) -> Result>, Self::Error> { - self.db.get(columns::STATE, &key[..]).map(|r| r.map(|v| v.to_vec())) - } -} - - -/// Disk backend. Keeps data in a key-value store. In archive mode, trie nodes are kept from all blocks. -/// Otherwise, trie nodes are kept only from the most recent block. -pub struct Backend { - storage: Arc>, - blockchain: BlockchainDb, - finalization_window: u64, -} - -impl Backend { - /// Create a new instance of database backend. - pub fn new(config: DatabaseSettings, finalization_window: u64) -> Result { - let db = open_database(&config, "full")?; - - Backend::from_kvdb(db as Arc<_>, config.pruning, finalization_window) - } - - #[cfg(test)] - fn new_test(keep_blocks: u32) -> Self { - use utils::NUM_COLUMNS; - - let db = Arc::new(::kvdb_memorydb::create(NUM_COLUMNS)); - - Backend::from_kvdb(db as Arc<_>, PruningMode::keep_blocks(keep_blocks), 0).expect("failed to create test-db") - } - - fn from_kvdb(db: Arc, pruning: PruningMode, finalization_window: u64) -> Result { - let blockchain = BlockchainDb::new(db.clone())?; - let map_e = |e: state_db::Error| ::client::error::Error::from(format!("State database error: {:?}", e)); - let state_db: StateDb = StateDb::new(pruning, &StateMetaDb(&*db)).map_err(map_e)?; - let storage_db = StorageDb { - db, - state_db, - }; - - Ok(Backend { - storage: Arc::new(storage_db), - blockchain, - finalization_window, - }) - } -} - -fn apply_state_commit(transaction: &mut DBTransaction, commit: state_db::CommitSet) { - for (key, val) in commit.data.inserted.into_iter() { - transaction.put(columns::STATE, &key[..], &val); - } - for key in commit.data.deleted.into_iter() { - transaction.delete(columns::STATE, &key[..]); - } - for (key, val) in commit.meta.inserted.into_iter() { - transaction.put(columns::STATE_META, &key[..], &val); - } - for key in commit.meta.deleted.into_iter() { - transaction.delete(columns::STATE_META, &key[..]); - } -} - -impl client::backend::Backend for Backend where Block: BlockT { - type BlockImportOperation = BlockImportOperation; - type Blockchain = BlockchainDb; - type State = DbState; - - fn begin_operation(&self, block: BlockId) -> Result { - let state = self.state_at(block)?; - Ok(BlockImportOperation { - pending_block: None, - old_state: state, - updates: MemoryDB::default(), - }) - } - - fn commit_operation(&self, mut operation: Self::BlockImportOperation) -> Result<(), client::error::Error> { - use client::blockchain::HeaderBackend; - let mut transaction = DBTransaction::new(); - if let Some(pending_block) = operation.pending_block { - let hash = pending_block.header.hash(); - let number = pending_block.header.number().clone(); - let key = number_to_db_key(number.clone()); - transaction.put(columns::HEADER, &key, &pending_block.header.encode()); - if let Some(body) = pending_block.body { - transaction.put(columns::BODY, &key, &body.encode()); - } - if let Some(justification) = pending_block.justification { - transaction.put(columns::JUSTIFICATION, &key, &justification.encode()); - } - transaction.put(columns::BLOCK_INDEX, hash.as_ref(), &key); - if pending_block.is_best { - transaction.put(columns::META, meta_keys::BEST_BLOCK, &key); - } - let mut changeset: state_db::ChangeSet = state_db::ChangeSet::default(); - for (key, (val, rc)) in operation.updates.drain() { - if rc > 0 { - changeset.inserted.push((key.0.into(), val.to_vec())); - } else if rc < 0 { - changeset.deleted.push(key.0.into()); - } - } - let number_u64 = number.as_().into(); - let commit = self.storage.state_db.insert_block(&hash, number_u64, &pending_block.header.parent_hash(), changeset); - apply_state_commit(&mut transaction, commit); - - //finalize an older block - if number_u64 > self.finalization_window { - let finalizing_hash = if self.finalization_window == 0 { - Some(hash) - } else { - let finalizing = number_u64 - self.finalization_window; - if finalizing > self.storage.state_db.best_finalized() { - self.blockchain.hash(As::sa(finalizing))? - } else { - None - } - }; - if let Some(finalizing_hash) = finalizing_hash { - trace!(target: "db", "Finalizing block #{} ({:?})", number_u64 - self.finalization_window, finalizing_hash); - let commit = self.storage.state_db.finalize_block(&finalizing_hash); - apply_state_commit(&mut transaction, commit); - } - } - - debug!(target: "db", "DB Commit {:?} ({}), best = {}", hash, number, pending_block.is_best); - self.storage.db.write(transaction).map_err(db_err)?; - self.blockchain.update_meta(hash, number, pending_block.is_best); - } - Ok(()) - } - - fn revert(&self, n: NumberFor) -> Result, client::error::Error> { - use client::blockchain::HeaderBackend; - let mut best = self.blockchain.info()?.best_number; - for c in 0 .. n.as_() { - if best == As::sa(0) { - return Ok(As::sa(c)) - } - let mut transaction = DBTransaction::new(); - match self.storage.state_db.revert_one() { - Some(commit) => { - apply_state_commit(&mut transaction, commit); - let removed = self.blockchain.hash(best)?.ok_or_else( - || client::error::ErrorKind::UnknownBlock( - format!("Error reverting to {}. Block hash not found.", best)))?; - best -= As::sa(1); - let key = number_to_db_key(best.clone()); - let hash = self.blockchain.hash(best)?.ok_or_else( - || client::error::ErrorKind::UnknownBlock( - format!("Error reverting to {}. Block hash not found.", best)))?; - transaction.put(columns::META, meta_keys::BEST_BLOCK, &key); - transaction.delete(columns::BLOCK_INDEX, removed.as_ref()); - self.storage.db.write(transaction).map_err(db_err)?; - self.blockchain.update_meta(hash, best, true); - } - None => return Ok(As::sa(c)) - } - } - Ok(n) - } - - fn blockchain(&self) -> &BlockchainDb { - &self.blockchain - } - - fn state_at(&self, block: BlockId) -> Result { - use client::blockchain::HeaderBackend as BcHeaderBackend; - - // special case for genesis initialization - match block { - BlockId::Hash(h) if h == Default::default() => - return Ok(DbState::with_storage_for_genesis(self.storage.clone())), - _ => {} - } - - match self.blockchain.header(block) { - Ok(Some(ref hdr)) if !self.storage.state_db.is_pruned(hdr.number().as_()) => { - let root = H256::from_slice(hdr.state_root().as_ref()); - Ok(DbState::with_storage(self.storage.clone(), root)) - }, - Err(e) => Err(e), - _ => Err(client::error::ErrorKind::UnknownBlock(format!("{:?}", block)).into()), - } - } -} - -impl client::backend::LocalBackend for Backend -where Block: BlockT {} - -#[cfg(test)] -mod tests { - use hashdb::HashDB; - use super::*; - use client::backend::Backend as BTrait; - use client::backend::BlockImportOperation as Op; - use client::blockchain::HeaderBackend as BlockchainHeaderBackend; - use runtime_primitives::testing::{Header, Block as RawBlock}; - - type Block = RawBlock; - - #[test] - fn block_hash_inserted_correctly() { - let db = Backend::::new_test(1); - for i in 0..10 { - assert!(db.blockchain().hash(i).unwrap().is_none()); - - { - let id = if i == 0 { - BlockId::Hash(Default::default()) - } else { - BlockId::Number(i - 1) - }; - - let mut op = db.begin_operation(id).unwrap(); - let header = Header { - number: i, - parent_hash: if i == 0 { - Default::default() - } else { - db.blockchain.hash(i - 1).unwrap().unwrap() - }, - state_root: Default::default(), - digest: Default::default(), - extrinsics_root: Default::default(), - }; - - op.set_block_data( - header, - Some(vec![]), - None, - true, - ).unwrap(); - db.commit_operation(op).unwrap(); - } - - assert!(db.blockchain().hash(i).unwrap().is_some()) - } - } - - #[test] - fn set_state_data() { - let db = Backend::::new_test(2); - { - let mut op = db.begin_operation(BlockId::Hash(Default::default())).unwrap(); - let mut header = Header { - number: 0, - parent_hash: Default::default(), - state_root: Default::default(), - digest: Default::default(), - extrinsics_root: Default::default(), - }; - - let storage = vec![ - (vec![1, 3, 5], vec![2, 4, 6]), - (vec![1, 2, 3], vec![9, 9, 9]), - ]; - - header.state_root = op.old_state.storage_root(storage - .iter() - .cloned() - .map(|(x, y)| (x, Some(y))) - ).0.into(); - - op.reset_storage(storage.iter().cloned()).unwrap(); - op.set_block_data( - header, - Some(vec![]), - None, - true - ).unwrap(); - - db.commit_operation(op).unwrap(); - - let state = db.state_at(BlockId::Number(0)).unwrap(); - - assert_eq!(state.storage(&[1, 3, 5]).unwrap(), Some(vec![2, 4, 6])); - assert_eq!(state.storage(&[1, 2, 3]).unwrap(), Some(vec![9, 9, 9])); - assert_eq!(state.storage(&[5, 5, 5]).unwrap(), None); - - } - - { - let mut op = db.begin_operation(BlockId::Number(0)).unwrap(); - let mut header = Header { - number: 1, - parent_hash: Default::default(), - state_root: Default::default(), - digest: Default::default(), - extrinsics_root: Default::default(), - }; - - let storage = vec![ - (vec![1, 3, 5], None), - (vec![5, 5, 5], Some(vec![4, 5, 6])), - ]; - - let (root, overlay) = op.old_state.storage_root(storage.iter().cloned()); - op.update_storage(overlay).unwrap(); - header.state_root = root.into(); - - op.set_block_data( - header, - Some(vec![]), - None, - true - ).unwrap(); - - db.commit_operation(op).unwrap(); - - let state = db.state_at(BlockId::Number(1)).unwrap(); - - assert_eq!(state.storage(&[1, 3, 5]).unwrap(), None); - assert_eq!(state.storage(&[1, 2, 3]).unwrap(), Some(vec![9, 9, 9])); - assert_eq!(state.storage(&[5, 5, 5]).unwrap(), Some(vec![4, 5, 6])); - } - } - - #[test] - fn delete_only_when_negative_rc() { - let key; - let backend = Backend::::new_test(0); - - let hash = { - let mut op = backend.begin_operation(BlockId::Hash(Default::default())).unwrap(); - let mut header = Header { - number: 0, - parent_hash: Default::default(), - state_root: Default::default(), - digest: Default::default(), - extrinsics_root: Default::default(), - }; - - let storage: Vec<(_, _)> = vec![]; - - header.state_root = op.old_state.storage_root(storage - .iter() - .cloned() - .map(|(x, y)| (x, Some(y))) - ).0.into(); - let hash = header.hash(); - - op.reset_storage(storage.iter().cloned()).unwrap(); - - key = op.updates.insert(b"hello"); - op.set_block_data( - header, - Some(vec![]), - None, - true - ).unwrap(); - - backend.commit_operation(op).unwrap(); - - assert_eq!(backend.storage.db.get(::columns::STATE, &key.0[..]).unwrap().unwrap(), &b"hello"[..]); - hash - }; - - let hash = { - let mut op = backend.begin_operation(BlockId::Number(0)).unwrap(); - let mut header = Header { - number: 1, - parent_hash: hash, - state_root: Default::default(), - digest: Default::default(), - extrinsics_root: Default::default(), - }; - - let storage: Vec<(_, _)> = vec![]; - - header.state_root = op.old_state.storage_root(storage - .iter() - .cloned() - .map(|(x, y)| (x, Some(y))) - ).0.into(); - let hash = header.hash(); - - op.updates.insert(b"hello"); - op.updates.remove(&key); - op.set_block_data( - header, - Some(vec![]), - None, - true - ).unwrap(); - - backend.commit_operation(op).unwrap(); - - assert_eq!(backend.storage.db.get(::columns::STATE, &key.0[..]).unwrap().unwrap(), &b"hello"[..]); - hash - }; - - { - let mut op = backend.begin_operation(BlockId::Number(1)).unwrap(); - let mut header = Header { - number: 2, - parent_hash: hash, - state_root: Default::default(), - digest: Default::default(), - extrinsics_root: Default::default(), - }; - - let storage: Vec<(_, _)> = vec![]; - - header.state_root = op.old_state.storage_root(storage - .iter() - .cloned() - .map(|(x, y)| (x, Some(y))) - ).0.into(); - - op.updates.remove(&key); - op.set_block_data( - header, - Some(vec![]), - None, - true - ).unwrap(); - - backend.commit_operation(op).unwrap(); - - assert!(backend.storage.db.get(::columns::STATE, &key.0[..]).unwrap().is_none()); - } - } -} diff --git a/substrate/client/db/src/light.rs b/substrate/client/db/src/light.rs deleted file mode 100644 index 9b82aec1f3087..0000000000000 --- a/substrate/client/db/src/light.rs +++ /dev/null @@ -1,392 +0,0 @@ -// Copyright 2017 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 light client blockchain storage. - -use std::sync::Arc; -use parking_lot::RwLock; - -use kvdb::{KeyValueDB, DBTransaction}; - -use client::blockchain::{BlockStatus, Cache as BlockchainCache, - HeaderBackend as BlockchainHeaderBackend, Info as BlockchainInfo}; -use client::cht; -use client::error::{ErrorKind as ClientErrorKind, Result as ClientResult}; -use client::light::blockchain::Storage as LightBlockchainStorage; -use codec::{Decode, Encode}; -use primitives::{AuthorityId, H256, Blake2Hasher}; -use runtime_primitives::generic::BlockId; -use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, Hash, HashFor, - Zero, One, As, NumberFor}; -use cache::DbCache; -use utils::{meta_keys, Meta, db_err, number_to_db_key, db_key_to_number, open_database, - read_db, read_id, read_meta}; -use DatabaseSettings; - -pub(crate) mod columns { - pub const META: Option = ::utils::COLUMN_META; - pub const BLOCK_INDEX: Option = Some(1); - pub const HEADER: Option = Some(2); - pub const AUTHORITIES: Option = Some(3); - pub const CHT: Option = Some(4); -} - -/// Keep authorities for last 'AUTHORITIES_ENTRIES_TO_KEEP' blocks. -pub(crate) const AUTHORITIES_ENTRIES_TO_KEEP: u64 = cht::SIZE; - -/// Light blockchain storage. Stores most recent headers + CHTs for older headers. -pub struct LightStorage { - db: Arc, - meta: RwLock::Header as HeaderT>::Number, Block::Hash>>, - cache: DbCache, -} - -#[derive(Clone, PartialEq, Debug)] -struct BestAuthorities { - /// first block, when this set became actual - valid_from: N, - /// None means that we do not know the set starting from `valid_from` block - authorities: Option>, -} - -impl LightStorage - where - Block: BlockT, -{ - /// Create new storage with given settings. - pub fn new(config: DatabaseSettings) -> ClientResult { - let db = open_database(&config, "light")?; - - Self::from_kvdb(db as Arc<_>) - } - - #[cfg(test)] - pub(crate) fn new_test() -> Self { - use utils::NUM_COLUMNS; - - let db = Arc::new(::kvdb_memorydb::create(NUM_COLUMNS)); - - Self::from_kvdb(db as Arc<_>).expect("failed to create test-db") - } - - fn from_kvdb(db: Arc) -> ClientResult { - let cache = DbCache::new(db.clone(), columns::BLOCK_INDEX, columns::AUTHORITIES)?; - let meta = RwLock::new(read_meta::(&*db, columns::HEADER)?); - - Ok(LightStorage { - db, - meta, - cache, - }) - } - - #[cfg(test)] - pub(crate) fn db(&self) -> &Arc { - &self.db - } - - #[cfg(test)] - pub(crate) fn cache(&self) -> &DbCache { - &self.cache - } - - fn update_meta(&self, hash: Block::Hash, number: <::Header as HeaderT>::Number, is_best: bool) { - if is_best { - let mut meta = self.meta.write(); - if number == <::Header as HeaderT>::Number::zero() { - meta.genesis_hash = hash; - } - - meta.best_number = number; - meta.best_hash = hash; - } - } -} - -impl BlockchainHeaderBackend for LightStorage - where - Block: BlockT, -{ - fn header(&self, id: BlockId) -> ClientResult> { - match read_db(&*self.db, columns::BLOCK_INDEX, columns::HEADER, id)? { - Some(header) => match Block::Header::decode(&mut &header[..]) { - Some(header) => Ok(Some(header)), - None => return Err(ClientErrorKind::Backend("Error decoding header".into()).into()), - } - None => Ok(None), - } - } - - fn info(&self) -> ClientResult> { - let meta = self.meta.read(); - Ok(BlockchainInfo { - best_hash: meta.best_hash, - best_number: meta.best_number, - genesis_hash: meta.genesis_hash, - }) - } - - fn status(&self, id: BlockId) -> ClientResult { - let exists = match id { - BlockId::Hash(_) => read_id(&*self.db, columns::BLOCK_INDEX, id)?.is_some(), - BlockId::Number(n) => n <= self.meta.read().best_number, - }; - match exists { - true => Ok(BlockStatus::InChain), - false => Ok(BlockStatus::Unknown), - } - } - - fn number(&self, hash: Block::Hash) -> ClientResult::Header as HeaderT>::Number>> { - read_id::(&*self.db, columns::BLOCK_INDEX, BlockId::Hash(hash)) - .and_then(|key| match key { - Some(key) => Ok(Some(db_key_to_number(&key)?)), - None => Ok(None), - }) - } - - fn hash(&self, number: <::Header as HeaderT>::Number) -> ClientResult> { - read_db::(&*self.db, columns::BLOCK_INDEX, columns::HEADER, BlockId::Number(number)).map(|x| - x.map(|raw| HashFor::::hash(&raw[..])).map(Into::into) - ) - } -} - -impl LightBlockchainStorage for LightStorage - where - Block: BlockT, - Block::Hash: From, -{ - fn import_header(&self, is_new_best: bool, header: Block::Header, authorities: Option>) -> ClientResult<()> { - let mut transaction = DBTransaction::new(); - - let hash = header.hash(); - let number = *header.number(); - let key = number_to_db_key(number); - - transaction.put(columns::HEADER, &key, &header.encode()); - transaction.put(columns::BLOCK_INDEX, hash.as_ref(), &key); - - let best_authorities = if is_new_best { - transaction.put(columns::META, meta_keys::BEST_BLOCK, &key); - - // cache authorities for previous block - let number: u64 = number.as_(); - let previous_number = number.checked_sub(1); - let best_authorities = previous_number - .and_then(|previous_number| self.cache.authorities_at_cache() - .commit_best_entry(&mut transaction, As::sa(previous_number), authorities)); - - // prune authorities from 'ancient' blocks - if let Some(ancient_number) = number.checked_sub(AUTHORITIES_ENTRIES_TO_KEEP) { - self.cache.authorities_at_cache().prune_entries(&mut transaction, As::sa(ancient_number))?; - } - - best_authorities - } else { - None - }; - - // build new CHT if required - if let Some(new_cht_number) = cht::is_build_required(cht::SIZE, *header.number()) { - let new_cht_start: NumberFor = cht::start_number(cht::SIZE, new_cht_number); - let new_cht_root: Option = cht::compute_root::( - cht::SIZE, new_cht_number, (new_cht_start.as_()..) - .map(|num| self.hash(As::sa(num)).unwrap_or_default())); - - if let Some(new_cht_root) = new_cht_root { - transaction.put(columns::CHT, &number_to_db_key(new_cht_start), new_cht_root.as_ref()); - - let mut prune_block = new_cht_start; - let new_cht_end = cht::end_number(cht::SIZE, new_cht_number); - trace!(target: "db", "Replacing blocks [{}..{}] with CHT#{}", new_cht_start, new_cht_end, new_cht_number); - - while prune_block <= new_cht_end { - transaction.delete(columns::HEADER, &number_to_db_key(prune_block)); - prune_block += <::Header as HeaderT>::Number::one(); - } - } - } - - debug!("Light DB Commit {:?} ({})", hash, number); - self.db.write(transaction).map_err(db_err)?; - self.update_meta(hash, number, is_new_best); - if let Some(best_authorities) = best_authorities { - self.cache.authorities_at_cache().update_best_entry(Some(best_authorities)); - } - - Ok(()) - } - - fn cht_root(&self, cht_size: u64, block: <::Header as HeaderT>::Number) -> ClientResult { - let no_cht_for_block = || ClientErrorKind::Backend(format!("CHT for block {} not exists", block)).into(); - - 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, &number_to_db_key(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)) - } - - fn cache(&self) -> Option<&BlockchainCache> { - Some(&self.cache) - } -} - -#[cfg(test)] -pub(crate) mod tests { - use client::cht; - use runtime_primitives::testing::{H256 as Hash, Header, Block as RawBlock}; - use super::*; - - type Block = RawBlock; - - pub fn insert_block( - db: &LightStorage, - parent: &Hash, - number: u64, - authorities: Option> - ) -> Hash { - let header = Header { - number: number.into(), - parent_hash: *parent, - state_root: Default::default(), - digest: Default::default(), - extrinsics_root: Default::default(), - }; - - let hash = header.hash(); - db.import_header(true, header, authorities).unwrap(); - hash - } - - #[test] - fn returns_known_header() { - let db = LightStorage::new_test(); - let known_hash = insert_block(&db, &Default::default(), 0, None); - let header_by_hash = db.header(BlockId::Hash(known_hash)).unwrap().unwrap(); - let header_by_number = db.header(BlockId::Number(0)).unwrap().unwrap(); - assert_eq!(header_by_hash, header_by_number); - } - - #[test] - fn does_not_return_unknown_header() { - let db = LightStorage::::new_test(); - assert!(db.header(BlockId::Hash(1.into())).unwrap().is_none()); - assert!(db.header(BlockId::Number(0)).unwrap().is_none()); - } - - #[test] - fn returns_info() { - let db = LightStorage::new_test(); - let genesis_hash = insert_block(&db, &Default::default(), 0, None); - let info = db.info().unwrap(); - assert_eq!(info.best_hash, genesis_hash); - assert_eq!(info.best_number, 0); - assert_eq!(info.genesis_hash, genesis_hash); - let best_hash = insert_block(&db, &genesis_hash, 1, None); - let info = db.info().unwrap(); - assert_eq!(info.best_hash, best_hash); - assert_eq!(info.best_number, 1); - assert_eq!(info.genesis_hash, genesis_hash); - } - - #[test] - fn returns_block_status() { - let db = LightStorage::new_test(); - let genesis_hash = insert_block(&db, &Default::default(), 0, None); - assert_eq!(db.status(BlockId::Hash(genesis_hash)).unwrap(), BlockStatus::InChain); - assert_eq!(db.status(BlockId::Number(0)).unwrap(), BlockStatus::InChain); - assert_eq!(db.status(BlockId::Hash(1.into())).unwrap(), BlockStatus::Unknown); - assert_eq!(db.status(BlockId::Number(1)).unwrap(), BlockStatus::Unknown); - } - - #[test] - fn returns_block_hash() { - let db = LightStorage::new_test(); - let genesis_hash = insert_block(&db, &Default::default(), 0, None); - assert_eq!(db.hash(0).unwrap(), Some(genesis_hash)); - assert_eq!(db.hash(1).unwrap(), None); - } - - #[test] - fn import_header_works() { - let db = LightStorage::new_test(); - - let genesis_hash = insert_block(&db, &Default::default(), 0, None); - assert_eq!(db.db.iter(columns::HEADER).count(), 1); - assert_eq!(db.db.iter(columns::BLOCK_INDEX).count(), 1); - - let _ = insert_block(&db, &genesis_hash, 1, None); - assert_eq!(db.db.iter(columns::HEADER).count(), 2); - assert_eq!(db.db.iter(columns::BLOCK_INDEX).count(), 2); - } - - #[test] - fn ancient_headers_are_replaced_with_cht() { - let db = LightStorage::new_test(); - - // insert genesis block header (never pruned) - let mut prev_hash = insert_block(&db, &Default::default(), 0, None); - - // insert SIZE blocks && ensure that nothing is pruned - for number in 0..cht::SIZE { - prev_hash = insert_block(&db, &prev_hash, 1 + number, None); - } - assert_eq!(db.db.iter(columns::HEADER).count(), (1 + cht::SIZE) as usize); - assert_eq!(db.db.iter(columns::CHT).count(), 0); - - // insert next SIZE blocks && ensure that nothing is pruned - for number in 0..cht::SIZE { - prev_hash = insert_block(&db, &prev_hash, 1 + cht::SIZE + number, None); - } - assert_eq!(db.db.iter(columns::HEADER).count(), (1 + cht::SIZE + cht::SIZE) as usize); - assert_eq!(db.db.iter(columns::CHT).count(), 0); - - // insert block #{2 * cht::SIZE + 1} && check that new CHT is created + headers of this CHT are pruned - insert_block(&db, &prev_hash, 1 + cht::SIZE + cht::SIZE, None); - assert_eq!(db.db.iter(columns::HEADER).count(), (1 + cht::SIZE + 1) as usize); - assert_eq!(db.db.iter(columns::CHT).count(), 1); - assert!((0..cht::SIZE).all(|i| db.db.get(columns::HEADER, &number_to_db_key(1 + i)).unwrap().is_none())); - } - - #[test] - fn get_cht_fails_for_genesis_block() { - assert!(LightStorage::::new_test().cht_root(cht::SIZE, 0).is_err()); - } - - #[test] - fn get_cht_fails_for_non_existant_cht() { - assert!(LightStorage::::new_test().cht_root(cht::SIZE, (cht::SIZE / 2) as u64).is_err()); - } - - #[test] - fn get_cht_works() { - let db = LightStorage::new_test(); - - // insert 1 + SIZE + SIZE + 1 blocks so that CHT#0 is created - let mut prev_hash = Default::default(); - for i in 0..1 + cht::SIZE + cht::SIZE + 1 { - prev_hash = insert_block(&db, &prev_hash, i as u64, None); - } - - let cht_root_1 = db.cht_root(cht::SIZE, cht::start_number(cht::SIZE, 0)).unwrap(); - let cht_root_2 = db.cht_root(cht::SIZE, (cht::start_number(cht::SIZE, 0) + cht::SIZE / 2) as u64).unwrap(); - let cht_root_3 = db.cht_root(cht::SIZE, cht::end_number(cht::SIZE, 0)).unwrap(); - assert_eq!(cht_root_1, cht_root_2); - assert_eq!(cht_root_2, cht_root_3); - } -} diff --git a/substrate/client/db/src/utils.rs b/substrate/client/db/src/utils.rs deleted file mode 100644 index a2c24ddce0635..0000000000000 --- a/substrate/client/db/src/utils.rs +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright 2017 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 . - -//! Db-based backend utility structures and functions, used by both -//! full and light storages. - -use std::sync::Arc; -use std::io; - -use kvdb::{KeyValueDB, DBTransaction}; -use kvdb_rocksdb::{Database, DatabaseConfig}; - -use client; -use codec::Decode; -use hashdb::DBValue; -use runtime_primitives::generic::BlockId; -use runtime_primitives::traits::{As, Block as BlockT, Header as HeaderT, Hash, HashFor, Zero}; -use 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 = 7; -/// Meta column. The set of keys in the column is shared by full && light storages. -pub const COLUMN_META: Option = Some(0); - -/// Keys of entries in COLUMN_META. -pub mod meta_keys { - /// Type of storage (full or light). - pub const TYPE: &[u8; 4] = b"type"; - /// Best block key. - pub const BEST_BLOCK: &[u8; 4] = b"best"; - /// Best authorities block key. - pub const BEST_AUTHORITIES: &[u8; 4] = b"auth"; -} - -/// Database metadata. -pub struct Meta { - /// Hash of the best known block. - pub best_hash: H, - /// Number of the best known block. - pub best_number: N, - /// Hash of the genesis block. - pub genesis_hash: H, -} - -/// Type of block key in the database (LE block number). -pub type BlockKey = [u8; 4]; - -/// Convert block number into key (LE representation). -pub fn number_to_db_key(n: N) -> BlockKey where N: As { - let n: u64 = n.as_(); - assert!(n & 0xffffffff00000000 == 0); - - [ - (n >> 24) as u8, - ((n >> 16) & 0xff) as u8, - ((n >> 8) & 0xff) as u8, - (n & 0xff) as u8 - ] -} - -/// Convert block key into block number. -pub fn db_key_to_number(key: &[u8]) -> client::error::Result where N: As { - match key.len() { - 4 => Ok((key[0] as u64) << 24 - | (key[1] as u64) << 16 - | (key[2] as u64) << 8 - | (key[3] as u64)).map(As::sa), - _ => Err(client::error::ErrorKind::Backend("Invalid block key".into()).into()), - } -} - -/// Maps database error to client error -pub fn db_err(err: io::Error) -> client::error::Error { - use std::error::Error; - client::error::ErrorKind::Backend(err.description().into()).into() -} - -/// Open RocksDB database. -pub fn open_database(config: &DatabaseSettings, db_type: &str) -> client::error::Result> { - let mut db_config = DatabaseConfig::with_columns(Some(NUM_COLUMNS)); - db_config.memory_budget = config.cache_size; - let path = config.path.to_str().ok_or_else(|| client::error::ErrorKind::Backend("Invalid database path".into()))?; - let db = Database::open(&db_config, &path).map_err(db_err)?; - - // check database type - match db.get(COLUMN_META, meta_keys::TYPE).map_err(db_err)? { - Some(stored_type) => { - if db_type.as_bytes() != &*stored_type { - return Err(client::error::ErrorKind::Backend( - format!("Unexpected database type. Expected: {}", db_type)).into()); - } - }, - None => { - let mut transaction = DBTransaction::new(); - transaction.put(COLUMN_META, meta_keys::TYPE, db_type.as_bytes()); - db.write(transaction).map_err(db_err)?; - }, - } - - Ok(Arc::new(db)) -} - -/// Convert block id to block key, reading number from db if required. -pub fn read_id(db: &KeyValueDB, col_index: Option, id: BlockId) -> Result, client::error::Error> - where - Block: BlockT, -{ - match id { - BlockId::Hash(h) => db.get(col_index, h.as_ref()) - .map(|v| v.map(|v| { - let mut key: [u8; 4] = [0; 4]; - key.copy_from_slice(&v); - key - })).map_err(db_err), - BlockId::Number(n) => Ok(Some(number_to_db_key(n))), - } -} - -/// Read database column entry for the given block. -pub fn read_db(db: &KeyValueDB, col_index: Option, col: Option, id: BlockId) -> client::error::Result> - where - Block: BlockT, -{ - read_id(db, col_index, id).and_then(|key| match key { - Some(key) => db.get(col, &key).map_err(db_err), - None => Ok(None), - }) -} - -/// Read meta from the database. -pub fn read_meta(db: &KeyValueDB, col_header: Option) -> Result::Header as HeaderT>::Number, Block::Hash>, client::error::Error> - where - Block: BlockT, -{ - let genesis_number = <::Header as HeaderT>::Number::zero(); - let (best_hash, best_number) = if let Some(Some(header)) = db.get(COLUMN_META, meta_keys::BEST_BLOCK).and_then(|id| - match id { - Some(id) => db.get(col_header, &id).map(|h| h.map(|b| Block::Header::decode(&mut &b[..]))), - None => Ok(None), - }).map_err(db_err)? - { - let hash = header.hash(); - debug!("DB Opened blockchain db, best {:?} ({})", hash, header.number()); - (hash, *header.number()) - } else { - (Default::default(), genesis_number) - }; - - let genesis_hash = db.get(col_header, &number_to_db_key(genesis_number)) - .map_err(db_err)? - .map(|raw| HashFor::::hash(&raw[..])) - .unwrap_or_default() - .into(); - - Ok(Meta { - best_hash, - best_number, - genesis_hash, - }) -} diff --git a/substrate/client/src/backend.rs b/substrate/client/src/backend.rs deleted file mode 100644 index 7466c8fb33260..0000000000000 --- a/substrate/client/src/backend.rs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2017 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 . - -//! Polkadot Client data backend - -use error; -use primitives::AuthorityId; -use runtime_primitives::bft::Justification; -use runtime_primitives::generic::BlockId; -use runtime_primitives::traits::{Block as BlockT, NumberFor}; -use state_machine::backend::Backend as StateBackend; -use patricia_trie::NodeCodec; -use hashdb::Hasher; - -/// Block insertion operation. Keeps hold if the inserted block state and data. -pub trait BlockImportOperation -where - Block: BlockT, - H: Hasher, - C: NodeCodec, -{ - /// Associated state backend type. - type State: StateBackend; - - /// Returns pending state. Returns None for backends with locally-unavailable state data. - fn state(&self) -> error::Result>; - /// Append block data to the transaction. - fn set_block_data( - &mut self, - header: Block::Header, - body: Option>, - justification: Option>, - is_new_best: bool - ) -> error::Result<()>; - - /// Append authorities set to the transaction. This is a set of parent block (set which - /// has been used to check justification of this block). - fn update_authorities(&mut self, authorities: Vec); - /// Inject storage data into the database. - fn update_storage(&mut self, update: >::Transaction) -> error::Result<()>; - /// Inject storage data into the database replacing any existing data. - fn reset_storage, Vec)>>(&mut self, iter: I) -> error::Result<()>; -} - -/// Client backend. Manages the data layer. -/// -/// Note on state pruning: while an object from `state_at` is alive, the state -/// should not be pruned. The backend should internally reference-count -/// its state objects. -/// -/// The same applies for live `BlockImportOperation`s: while an import operation building on a parent `P` -/// is alive, the state for `P` should not be pruned. -pub trait Backend: Send + Sync -where - Block: BlockT, - H: Hasher, - C: NodeCodec, -{ - /// Associated block insertion operation type. - type BlockImportOperation: BlockImportOperation; - /// Associated blockchain backend type. - type Blockchain: ::blockchain::Backend; - /// Associated state backend type. - type State: StateBackend; - - /// Begin a new block insertion transaction with given parent block id. - /// When constructing the genesis, this is called with all-zero hash. - fn begin_operation(&self, block: BlockId) -> error::Result; - /// Commit block insertion. - fn commit_operation(&self, transaction: Self::BlockImportOperation) -> error::Result<()>; - /// Returns reference to blockchain backend. - fn blockchain(&self) -> &Self::Blockchain; - /// Returns state backend with post-state of given block. - fn state_at(&self, block: BlockId) -> error::Result; - /// Attempts to revert the chain by `n` blocks. Returns the number of blocks that were - /// successfully reverted. - fn revert(&self, n: NumberFor) -> error::Result>; -} - -/// Mark for all Backend implementations, that are making use of state data, stored locally. -pub trait LocalBackend: Backend -where - Block: BlockT, - H: Hasher, - C: NodeCodec, -{} - -/// Mark for all Backend implementations, that are fetching required state data from remote nodes. -pub trait RemoteBackend: Backend -where - Block: BlockT, - H: Hasher, - C: NodeCodec, -{} diff --git a/substrate/client/src/block_builder.rs b/substrate/client/src/block_builder.rs deleted file mode 100644 index 0d78f30cc413b..0000000000000 --- a/substrate/client/src/block_builder.rs +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! Utility struct to build a block. - -use std::vec::Vec; -use codec::{Decode, Encode}; -use state_machine::{self, native_when_possible}; -use runtime_primitives::traits::{Header as HeaderT, Hash, Block as BlockT, One, HashFor}; -use runtime_primitives::generic::BlockId; -use {backend, error, Client, CallExecutor}; -use runtime_primitives::{ApplyResult, ApplyOutcome}; -use patricia_trie::NodeCodec; -use primitives::{Blake2Hasher, RlpCodec}; -use hashdb::Hasher; -use rlp::Encodable; - -/// Utility for building new (valid) blocks from a stream of extrinsics. -pub struct BlockBuilder -where - B: backend::Backend, - E: CallExecutor + Clone, - Block: BlockT, - H: Hasher, - H::Out: Encodable + Ord, - C: NodeCodec, -{ - header: ::Header, - extrinsics: Vec<::Extrinsic>, - executor: E, - state: B::State, - changes: state_machine::OverlayedChanges, -} - -impl BlockBuilder -where - B: backend::Backend, - E: CallExecutor + Clone, - Block: BlockT, -{ - /// Create a new instance of builder from the given client, building on the latest block. - pub fn new(client: &Client) -> error::Result { - client.info().and_then(|i| Self::at_block(&BlockId::Hash(i.chain.best_hash), client)) - } - - /// Create a new instance of builder from the given client using a particular block's ID to - /// build upon. - pub fn at_block(block_id: &BlockId, client: &Client) -> error::Result { - let number = client.block_number_from_id(block_id)? - .ok_or_else(|| error::ErrorKind::UnknownBlock(format!("{}", block_id)))? - + One::one(); - - let parent_hash = client.block_hash_from_id(block_id)? - .ok_or_else(|| error::ErrorKind::UnknownBlock(format!("{}", block_id)))?; - - let executor = client.executor().clone(); - let state = client.state_at(block_id)?; - let mut changes = Default::default(); - let header = <::Header as HeaderT>::new( - number, - Default::default(), - Default::default(), - parent_hash, - Default::default() - ); - - executor.call_at_state(&state, &mut changes, "initialise_block", &header.encode(), native_when_possible())?; - changes.commit_prospective(); - - Ok(BlockBuilder { - header, - extrinsics: Vec::new(), - executor, - state, - changes, - }) - } - - /// Push onto the block's list of extrinsics. This will ensure the extrinsic - /// can be validly executed (by executing it); if it is invalid, it'll be returned along with - /// the error. Otherwise, it will return a mutable reference to self (in order to chain). - pub fn push(&mut self, xt: ::Extrinsic) -> error::Result<()> { - match self.executor.call_at_state(&self.state, &mut self.changes, "apply_extrinsic", &xt.encode(), native_when_possible()) { - Ok((result, _)) => { - match ApplyResult::decode(&mut result.as_slice()) { - Some(Ok(ApplyOutcome::Success)) | Some(Ok(ApplyOutcome::Fail)) => { - self.extrinsics.push(xt); - self.changes.commit_prospective(); - Ok(()) - } - Some(Err(e)) => { - self.changes.discard_prospective(); - Err(error::ErrorKind::ApplyExtinsicFailed(e).into()) - } - None => { - self.changes.discard_prospective(); - Err(error::ErrorKind::CallResultDecode("apply_extrinsic").into()) - } - } - } - Err(e) => { - self.changes.discard_prospective(); - Err(e) - } - } - } - - /// Consume the builder to return a valid `Block` containing all pushed extrinsics. - pub fn bake(mut self) -> error::Result { - let (output, _) = self.executor.call_at_state( - &self.state, - &mut self.changes, - "finalise_block", - &[], - native_when_possible(), - )?; - self.header = <::Header as Decode>::decode(&mut &output[..]) - .expect("Header came straight out of runtime so must be valid"); - - debug_assert_eq!( - self.header.extrinsics_root().clone(), - HashFor::::ordered_trie_root(self.extrinsics.iter().map(Encode::encode)), - ); - - Ok(::new(self.header, self.extrinsics)) - } -} diff --git a/substrate/client/src/blockchain.rs b/substrate/client/src/blockchain.rs deleted file mode 100644 index ab5dc036e960e..0000000000000 --- a/substrate/client/src/blockchain.rs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2017 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 . - -//! Polkadot blockchain trait - -use primitives::AuthorityId; -use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, NumberFor}; -use runtime_primitives::generic::BlockId; -use runtime_primitives::bft::Justification; - -use error::{ErrorKind, Result}; - -/// Blockchain database header backend. Does not perform any validation. -pub trait HeaderBackend: Send + Sync { - /// Get block header. Returns `None` if block is not found. - fn header(&self, id: BlockId) -> Result>; - /// Get blockchain info. - fn info(&self) -> Result>; - /// Get block status. - fn status(&self, id: BlockId) -> Result; - /// Get block number by hash. Returns `None` if the header is not in the chain. - fn number(&self, hash: Block::Hash) -> Result::Header as HeaderT>::Number>>; - /// Get block hash by number. Returns `None` if the header is not in the chain. - fn hash(&self, number: NumberFor) -> Result>; - - /// Get block header. Returns `UnknownBlock` error if block is not found. - fn expect_header(&self, id: BlockId) -> Result { - self.header(id)?.ok_or_else(|| ErrorKind::UnknownBlock(format!("{}", id)).into()) - } -} - -/// Blockchain database backend. Does not perform any validation. -pub trait Backend: HeaderBackend { - /// Get block body. Returns `None` if block is not found. - fn body(&self, id: BlockId) -> Result::Extrinsic>>>; - /// Get block justification. Returns `None` if justification does not exist. - fn justification(&self, id: BlockId) -> Result>>; - - /// Returns data cache reference, if it is enabled on this backend. - fn cache(&self) -> Option<&Cache>; -} - -/// Blockchain optional data cache. -pub trait Cache: Send + Sync { - /// Returns the set of authorities, that was active at given block or None if there's no entry in the cache. - fn authorities_at(&self, block: BlockId) -> Option>; -} - -/// Block import outcome -pub enum ImportResult { - /// Imported successfully. - Imported, - /// Block already exists, skippped. - AlreadyInChain, - /// Unknown parent. - UnknownParent, - /// Other errror. - Err(E), -} - -/// Blockchain info -#[derive(Debug)] -pub struct Info { - /// Best block hash. - pub best_hash: <::Header as HeaderT>::Hash, - /// Best block number. - pub best_number: <::Header as HeaderT>::Number, - /// Genesis block hash. - pub genesis_hash: <::Header as HeaderT>::Hash, -} - -/// Block status. -#[derive(Debug, PartialEq, Eq)] -pub enum BlockStatus { - /// Already in the blockchain. - InChain, - /// Not in the queue or the blockchain. - Unknown, -} diff --git a/substrate/client/src/call_executor.rs b/substrate/client/src/call_executor.rs deleted file mode 100644 index d78025574185a..0000000000000 --- a/substrate/client/src/call_executor.rs +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright 2017 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 std::cmp::Ord; -use runtime_primitives::generic::BlockId; -use runtime_primitives::traits::Block as BlockT; -use state_machine::{self, OverlayedChanges, Ext, - CodeExecutor, ExecutionManager, native_when_possible}; -use executor::{RuntimeVersion, RuntimeInfo}; -use patricia_trie::NodeCodec; -use hashdb::Hasher; -use rlp::Encodable; -use codec::Decode; -use primitives::{Blake2Hasher, RlpCodec}; - -use backend; -use error; - -/// Information regarding the result of a call. -#[derive(Debug, Clone)] -pub struct CallResult { - /// The data that was returned from the call. - pub return_data: Vec, - /// The changes made to the state by the call. - pub changes: OverlayedChanges, -} - -/// Method call executor. -pub trait CallExecutor -where - B: BlockT, - H: Hasher, - H::Out: Ord + Encodable, - C: NodeCodec, -{ - /// Externalities error type. - type Error: state_machine::Error; - - /// Execute a call to a contract on top of state in a block of given hash. - /// - /// No changes are made. - fn call(&self, - id: &BlockId, - method: &str, - call_data: &[u8], - ) -> Result; - - /// Extract RuntimeVersion of given block - /// - /// No changes are made. - fn runtime_version(&self, id: &BlockId) -> Result; - - /// Execute a call to a contract on top of given state. - /// - /// No changes are made. - fn call_at_state< - S: state_machine::Backend, - F: FnOnce(Result, Self::Error>, Result, Self::Error>) -> Result, Self::Error>, - >(&self, - state: &S, - overlay: &mut OverlayedChanges, - method: &str, - call_data: &[u8], - manager: ExecutionManager - ) -> Result<(Vec, S::Transaction), error::Error>; - - /// Execute a call to a contract on top of given state, gathering execution proof. - /// - /// No changes are made. - fn prove_at_state>(&self, - state: S, - overlay: &mut OverlayedChanges, - method: &str, - call_data: &[u8] - ) -> Result<(Vec, Vec>), error::Error>; - - /// Get runtime version if supported. - fn native_runtime_version(&self) -> Option; -} - -/// Call executor that executes methods locally, querying all required -/// data from local backend. -pub struct LocalCallExecutor { - backend: Arc, - executor: E, -} - -impl LocalCallExecutor { - /// Creates new instance of local call executor. - pub fn new(backend: Arc, executor: E) -> Self { - LocalCallExecutor { backend, executor } - } -} - -impl Clone for LocalCallExecutor where E: Clone { - fn clone(&self) -> Self { - LocalCallExecutor { - backend: self.backend.clone(), - executor: self.executor.clone(), - } - } -} - -impl CallExecutor for LocalCallExecutor -where - B: backend::LocalBackend, - E: CodeExecutor + RuntimeInfo, - Block: BlockT, -{ - type Error = E::Error; - - fn call(&self, - id: &BlockId, - method: &str, - call_data: &[u8], - ) -> error::Result { - let mut changes = OverlayedChanges::default(); - let (return_data, _) = self.call_at_state( - &self.backend.state_at(*id)?, - &mut changes, - method, - call_data, - native_when_possible(), - )?; - Ok(CallResult { return_data, changes }) - } - - fn runtime_version(&self, id: &BlockId) -> error::Result { - let mut overlay = OverlayedChanges::default(); - let state = self.backend.state_at(*id)?; - use state_machine::Backend; - let code = state.storage(b":code") - .map_err(|e| error::ErrorKind::Execution(Box::new(e)))? - .ok_or(error::ErrorKind::VersionInvalid)? - .to_vec(); - let heap_pages = state.storage(b":heappages") - .map_err(|e| error::ErrorKind::Execution(Box::new(e)))? - .and_then(|v| u64::decode(&mut &v[..])) - .unwrap_or(8) as usize; - - self.executor.runtime_version(&mut Ext::new(&mut overlay, &state), heap_pages, &code) - .ok_or(error::ErrorKind::VersionInvalid.into()) - } - - fn call_at_state< - S: state_machine::Backend, - F: FnOnce(Result, Self::Error>, Result, Self::Error>) -> Result, Self::Error>, - >(&self, - state: &S, - changes: &mut OverlayedChanges, - method: &str, - call_data: &[u8], - manager: ExecutionManager, - ) -> error::Result<(Vec, S::Transaction)> { - state_machine::execute_using_consensus_failure_handler( - state, - changes, - &self.executor, - method, - call_data, - manager, - ).map_err(Into::into) - } - - fn prove_at_state>(&self, - state: S, - changes: &mut OverlayedChanges, - method: &str, - call_data: &[u8] - ) -> Result<(Vec, Vec>), error::Error> { - state_machine::prove_execution( - state, - changes, - &self.executor, - method, - call_data, - ) - .map(|(result, proof, _)| (result, proof)) - .map_err(Into::into) - } - - fn native_runtime_version(&self) -> Option { - ::NATIVE_VERSION - } -} diff --git a/substrate/client/src/cht.rs b/substrate/client/src/cht.rs deleted file mode 100644 index 2f2f38523b179..0000000000000 --- a/substrate/client/src/cht.rs +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright 2017 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 . - -//! Canonical hash trie definitions and helper functions. -//! -//! Each CHT is a trie mapping block numbers to canonical hash. -//! One is generated for every `SIZE` blocks, allowing us to discard those blocks in -//! favor of the trie root. When the "ancient" blocks need to be accessed, we simply -//! request an inclusion proof of a specific block number against the trie with the -//! root has. A correct proof implies that the claimed block is identical to the one -//! we discarded. - -use hashdb; -use heapsize::HeapSizeOf; -use patricia_trie::NodeCodec; -use rlp::Encodable; -use triehash; - -use primitives::H256; -use runtime_primitives::traits::{As, Header as HeaderT, SimpleArithmetic, One}; -use state_machine::backend::InMemory as InMemoryState; -use state_machine::{prove_read, read_proof_check}; - -use error::{Error as ClientError, ErrorKind as ClientErrorKind, Result as ClientResult}; - -/// The size of each CHT. This value is passed to every CHT-related function from -/// production code. Other values are passed from tests. -pub const SIZE: u64 = 2048; - -/// Returns Some(cht_number) if CHT is need to be built when the block with given number is canonized. -pub fn is_build_required(cht_size: u64, block_num: N) -> Option - where - N: Clone + SimpleArithmetic, -{ - let block_cht_num = block_to_cht_number(cht_size, block_num.clone())?; - let two = N::one() + N::one(); - if block_cht_num < two { - return None; - } - let cht_start = start_number(cht_size, block_cht_num.clone()); - if cht_start != block_num { - return None; - } - - Some(block_cht_num - two) -} - -/// Compute a CHT root from an iterator of block hashes. Fails if shorter than -/// SIZE items. The items are assumed to proceed sequentially from `start_number(cht_num)`. -/// Discards the trie's nodes. -pub fn compute_root( - cht_size: u64, - cht_num: Header::Number, - hashes: I, -) -> Option - where - Header: HeaderT, - Header::Hash: From, - Hasher: hashdb::Hasher, - Hasher::Out: Ord + Encodable, - I: IntoIterator>, -{ - build_pairs::(cht_size, cht_num, hashes) - .map(|pairs| triehash::trie_root::(pairs).into()) -} - -/// Build CHT-based header proof. -pub fn build_proof( - cht_size: u64, - cht_num: Header::Number, - block_num: Header::Number, - hashes: I -) -> Option>> - where - Header: HeaderT, - Hasher: hashdb::Hasher, - Hasher::Out: Ord + Encodable + HeapSizeOf, - Codec: NodeCodec, - I: IntoIterator>, -{ - let transaction = build_pairs::(cht_size, cht_num, hashes)? - .into_iter() - .map(|(k, v)| (k, Some(v))) - .collect::>(); - let storage = InMemoryState::::default().update(transaction); - let (value, proof) = prove_read(storage, &encode_cht_key(block_num)).ok()?; - if value.is_none() { - None - } else { - Some(proof) - } -} - -/// Check CHT-based header proof. -pub fn check_proof( - local_root: Header::Hash, - local_number: Header::Number, - remote_hash: Header::Hash, - remote_proof: Vec> -) -> ClientResult<()> - where - Header: HeaderT, - Header::Hash: From, - Hasher: hashdb::Hasher, - Hasher::Out: Ord + Encodable + HeapSizeOf + From, - Codec: NodeCodec, -{ - let local_cht_key = encode_cht_key(local_number); - let local_cht_value = read_proof_check::(local_root.into(), remote_proof, - &local_cht_key).map_err(|e| ClientError::from(e))?; - let local_cht_value = local_cht_value.ok_or_else(|| ClientErrorKind::InvalidHeaderProof)?; - let local_hash: Header::Hash = decode_cht_value(&local_cht_value).ok_or_else(|| ClientErrorKind::InvalidHeaderProof)?; - match local_hash == remote_hash { - true => Ok(()), - false => Err(ClientErrorKind::InvalidHeaderProof.into()), - } -} - -/// Build pairs for computing CHT. -fn build_pairs( - cht_size: u64, - cht_num: Header::Number, - hashes: I -) -> Option, Vec)>> - where - Header: HeaderT, - I: IntoIterator>, -{ - let start_num = start_number(cht_size, cht_num); - let mut pairs = Vec::new(); - let mut hash_number = start_num; - for hash in hashes.into_iter().take(cht_size as usize) { - pairs.push(hash.map(|hash| ( - encode_cht_key(hash_number).to_vec(), - encode_cht_value(hash) - ))?); - hash_number += Header::Number::one(); - } - - if pairs.len() as u64 == cht_size { - Some(pairs) - } else { - None - } -} - -/// Get the starting block of a given CHT. -/// CHT 0 includes block 1...SIZE, -/// CHT 1 includes block SIZE + 1 ... 2*SIZE -/// More generally: CHT N includes block (1 + N*SIZE)...((N+1)*SIZE). -/// This is because the genesis hash is assumed to be known -/// and including it would be redundant. -pub fn start_number(cht_size: u64, cht_num: N) -> N { - (cht_num * As::sa(cht_size)) + N::one() -} - -/// Get the ending block of a given CHT. -pub fn end_number(cht_size: u64, cht_num: N) -> N { - (cht_num + N::one()) * As::sa(cht_size) -} - -/// Convert a block number to a CHT number. -/// Returns `None` for `block_num` == 0, `Some` otherwise. -pub fn block_to_cht_number(cht_size: u64, block_num: N) -> Option { - if block_num == N::zero() { - None - } else { - Some((block_num - N::one()) / As::sa(cht_size)) - } -} - -/// Convert header number into CHT key. -pub fn encode_cht_key>(number: N) -> Vec { - let number: u64 = number.as_(); - vec![ - (number >> 56) as u8, - ((number >> 48) & 0xff) as u8, - ((number >> 40) & 0xff) as u8, - ((number >> 32) & 0xff) as u8, - ((number >> 24) & 0xff) as u8, - ((number >> 16) & 0xff) as u8, - ((number >> 8) & 0xff) as u8, - (number & 0xff) as u8 - ] -} - -/// Convert header hash into CHT value. -fn encode_cht_value>(hash: Hash) -> Vec { - hash.as_ref().to_vec() -} - -/// Convert CHT value into block header hash. -pub fn decode_cht_value>(value: &[u8]) -> Option { - match value.len() { - 32 => Some(H256::from_slice(&value[0..32]).into()), - _ => None, - } - -} - -#[cfg(test)] -mod tests { - use primitives::{Blake2Hasher, RlpCodec}; - use test_client::runtime::Header; - use super::*; - - #[test] - fn is_build_required_works() { - assert_eq!(is_build_required(SIZE, 0), None); - assert_eq!(is_build_required(SIZE, 1), None); - assert_eq!(is_build_required(SIZE, SIZE), None); - assert_eq!(is_build_required(SIZE, SIZE + 1), None); - assert_eq!(is_build_required(SIZE, 2 * SIZE), None); - assert_eq!(is_build_required(SIZE, 2 * SIZE + 1), Some(0)); - assert_eq!(is_build_required(SIZE, 3 * SIZE), None); - assert_eq!(is_build_required(SIZE, 3 * SIZE + 1), Some(1)); - } - - #[test] - fn start_number_works() { - assert_eq!(start_number(SIZE, 0), 1); - assert_eq!(start_number(SIZE, 1), SIZE + 1); - assert_eq!(start_number(SIZE, 2), SIZE + SIZE + 1); - } - - #[test] - fn end_number_works() { - assert_eq!(end_number(SIZE, 0), SIZE); - assert_eq!(end_number(SIZE, 1), SIZE + SIZE); - assert_eq!(end_number(SIZE, 2), SIZE + SIZE + SIZE); - } - - #[test] - fn build_pairs_fails_when_no_enough_blocks() { - assert!(build_pairs::(SIZE, 0, vec![Some(1.into()); SIZE as usize / 2]).is_none()); - } - - #[test] - fn build_pairs_fails_when_missing_block() { - assert!(build_pairs::(SIZE, 0, ::std::iter::repeat(Some(1.into())).take(SIZE as usize / 2) - .chain(::std::iter::once(None)) - .chain(::std::iter::repeat(Some(2.into())).take(SIZE as usize / 2 - 1))).is_none()); - } - - #[test] - fn compute_root_works() { - assert!(compute_root::(SIZE, 42, vec![Some(1.into()); SIZE as usize]).is_some()); - } - - #[test] - fn build_proof_fails_when_querying_wrong_block() { - assert!(build_proof::( - SIZE, 0, (SIZE * 1000) as u64, vec![Some(1.into()); SIZE as usize]).is_none()); - } - - #[test] - fn build_proof_works() { - assert!(build_proof::( - SIZE, 0, (SIZE / 2) as u64, vec![Some(1.into()); SIZE as usize]).is_some()); - } -} diff --git a/substrate/client/src/client.rs b/substrate/client/src/client.rs deleted file mode 100644 index 9246e61ec85d9..0000000000000 --- a/substrate/client/src/client.rs +++ /dev/null @@ -1,810 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! Substrate Client - -use std::sync::Arc; -use futures::sync::mpsc; -use parking_lot::{Mutex, RwLock}; -use primitives::AuthorityId; -use runtime_primitives::{bft::Justification, generic::{BlockId, SignedBlock, Block as RuntimeBlock}}; -use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, Zero, One, As, NumberFor}; -use runtime_primitives::BuildStorage; -use runtime_support::metadata::JSONMetadataDecodable; -use primitives::{Blake2Hasher, RlpCodec}; -use primitives::storage::{StorageKey, StorageData}; -use codec::{Encode, Decode}; -use state_machine::{ - Ext, OverlayedChanges, Backend as StateBackend, CodeExecutor, - ExecutionStrategy, ExecutionManager, prove_read -}; - -use backend::{self, BlockImportOperation}; -use blockchain::{self, Info as ChainInfo, Backend as ChainBackend, HeaderBackend as ChainHeaderBackend}; -use call_executor::{CallExecutor, LocalCallExecutor}; -use executor::{RuntimeVersion, RuntimeInfo}; -use notifications::{StorageNotifications, StorageEventStream}; -use {cht, error, in_mem, block_builder, runtime_io, bft, genesis}; - -/// Type that implements `futures::Stream` of block import events. -pub type BlockchainEventStream = mpsc::UnboundedReceiver>; - -/// Substrate Client -pub struct Client where Block: BlockT { - backend: Arc, - executor: E, - storage_notifications: Mutex>, - import_notification_sinks: Mutex>>>, - import_lock: Mutex<()>, - importing_block: RwLock>, // holds the block hash currently being imported. TODO: replace this with block queue - execution_strategy: ExecutionStrategy, -} - -/// A source of blockchain evenets. -pub trait BlockchainEvents { - /// Get block import event stream. - fn import_notification_stream(&self) -> BlockchainEventStream; - - /// Get storage changes event stream. - /// - /// Passing `None` as `filter_keys` subscribes to all storage changes. - fn storage_changes_notification_stream(&self, filter_keys: Option<&[StorageKey]>) -> error::Result>; -} - -/// Chain head information. -pub trait ChainHead { - /// Get best block header. - fn best_block_header(&self) -> Result<::Header, error::Error>; -} - -/// Fetch block body by ID. -pub trait BlockBody { - /// Get block body by ID. Returns `None` if the body is not stored. - fn block_body(&self, id: &BlockId) -> error::Result::Extrinsic>>>; -} - -/// Client info -// TODO: split queue info from chain info and amalgamate into single struct. -#[derive(Debug)] -pub struct ClientInfo { - /// Best block hash. - pub chain: ChainInfo, - /// Best block number in the queue. - pub best_queued_number: Option<<::Header as HeaderT>::Number>, - /// Best queued block hash. - pub best_queued_hash: Option, -} - -/// Block import result. -#[derive(Debug)] -pub enum ImportResult { - /// Added to the import queue. - Queued, - /// Already in the import queue. - AlreadyQueued, - /// Already in the blockchain. - AlreadyInChain, - /// Block or parent is known to be bad. - KnownBad, - /// Block parent is not in the chain. - UnknownParent, -} - -/// Block status. -#[derive(Debug, PartialEq, Eq)] -pub enum BlockStatus { - /// Added to the import queue. - Queued, - /// Already in the blockchain. - InChain, - /// Block or parent is known to be bad. - KnownBad, - /// Not in the queue or the blockchain. - Unknown, -} - -/// Block data origin. -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum BlockOrigin { - /// Genesis block built into the client. - Genesis, - /// Block is part of the initial sync with the network. - NetworkInitialSync, - /// Block was broadcasted on the network. - NetworkBroadcast, - /// Block that was received from the network and validated in the consensus process. - ConsensusBroadcast, - /// Block that was collated by this node. - Own, - /// Block was imported from a file. - File, -} - -/// Summary of an imported block -#[derive(Clone, Debug)] -pub struct BlockImportNotification { - /// Imported block header hash. - pub hash: Block::Hash, - /// Imported block origin. - pub origin: BlockOrigin, - /// Imported block header. - pub header: Block::Header, - /// Is this the new best block. - pub is_new_best: bool, -} - -/// A header paired with a justification which has already been checked. -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct JustifiedHeader { - header: ::Header, - justification: ::bft::Justification, - authorities: Vec, -} - -impl JustifiedHeader { - /// Deconstruct the justified header into parts. - pub fn into_inner(self) -> (::Header, ::bft::Justification, Vec) { - (self.header, self.justification, self.authorities) - } -} - -/// Create an instance of in-memory client. -pub fn new_in_mem( - executor: E, - genesis_storage: S, -) -> error::Result, LocalCallExecutor, E>, Block>> - where - E: CodeExecutor + RuntimeInfo, - S: BuildStorage, - Block: BlockT, -{ - let backend = Arc::new(in_mem::Backend::new()); - let executor = LocalCallExecutor::new(backend.clone(), executor); - Client::new(backend, executor, genesis_storage, ExecutionStrategy::NativeWhenPossible) -} - -impl Client where - B: backend::Backend, - E: CallExecutor, - Block: BlockT, -{ - /// Creates new Substrate Client with given blockchain and code executor. - pub fn new( - backend: Arc, - executor: E, - build_genesis_storage: S, - execution_strategy: ExecutionStrategy, - ) -> error::Result { - if backend.blockchain().header(BlockId::Number(Zero::zero()))?.is_none() { - let genesis_storage = build_genesis_storage.build_storage()?; - let genesis_block = genesis::construct_genesis_block::(&genesis_storage); - info!("Initialising Genesis block/state (state: {}, header-hash: {})", genesis_block.header().state_root(), genesis_block.header().hash()); - let mut op = backend.begin_operation(BlockId::Hash(Default::default()))?; - op.reset_storage(genesis_storage.into_iter())?; - op.set_block_data(genesis_block.deconstruct().0, Some(vec![]), None, true)?; - backend.commit_operation(op)?; - } - Ok(Client { - backend, - executor, - storage_notifications: Default::default(), - import_notification_sinks: Default::default(), - import_lock: Default::default(), - importing_block: Default::default(), - execution_strategy, - }) - } - - /// Get a reference to the state at a given block. - pub fn state_at(&self, block: &BlockId) -> error::Result { - self.backend.state_at(*block) - } - - /// Expose backend reference. To be used in tests only - pub fn backend(&self) -> &Arc { - &self.backend - } - - /// Return single storage entry of contract under given address in state in a block of given hash. - pub fn storage(&self, id: &BlockId, key: &StorageKey) -> error::Result> { - Ok(self.state_at(id)? - .storage(&key.0).map_err(|e| error::Error::from_state(Box::new(e)))? - .map(StorageData)) - } - - /// Get the code at a given block. - pub fn code_at(&self, id: &BlockId) -> error::Result> { - Ok(self.storage(id, &StorageKey(b":code".to_vec()))? - .expect("None is returned if there's no value stored for the given key; ':code' key is always defined; qed").0) - } - - /// Get the set of authorities at a given block. - pub fn authorities_at(&self, id: &BlockId) -> error::Result> { - match self.backend.blockchain().cache().and_then(|cache| cache.authorities_at(*id)) { - Some(cached_value) => Ok(cached_value), - None => self.executor.call(id, "authorities",&[]) - .and_then(|r| Vec::::decode(&mut &r.return_data[..]) - .ok_or(error::ErrorKind::AuthLenInvalid.into())) - } - } - - /// Get the RuntimeVersion at a given block. - pub fn runtime_version_at(&self, id: &BlockId) -> error::Result { - // TODO: Post Poc-2 return an error if version is missing - self.executor.runtime_version(id) - } - - /// Get call executor reference. - pub fn executor(&self) -> &E { - &self.executor - } - - /// Returns the runtime metadata as JSON. - pub fn json_metadata(&self, id: &BlockId) -> error::Result { - self.executor.call(id, "json_metadata",&[]) - .and_then(|r| Vec::::decode(&mut &r.return_data[..]) - .ok_or("JSON Metadata decoding failed".into())) - .and_then(|metadata| { - let mut json = metadata.into_iter().enumerate().fold(String::from("{"), - |mut json, (i, m)| { - if i > 0 { - json.push_str(","); - } - let (mtype, val) = m.into_json_string(); - json.push_str(&format!(r#" "{}": {}"#, mtype, val)); - json - } - ); - json.push_str(" }"); - - Ok(json) - }) - } - - /// Reads storage value at a given block + key, returning read proof. - pub fn read_proof(&self, id: &BlockId, key: &[u8]) -> error::Result>> { - self.state_at(id) - .and_then(|state| prove_read(state, key) - .map(|(_, proof)| proof) - .map_err(Into::into)) - } - - /// Execute a call to a contract on top of state in a block of given hash - /// AND returning execution proof. - /// - /// No changes are made. - pub fn execution_proof(&self, id: &BlockId, method: &str, call_data: &[u8]) -> error::Result<(Vec, Vec>)> { - self.state_at(id).and_then(|state| self.executor.prove_at_state(state, &mut Default::default(), method, call_data)) - } - - /// Reads given header and generates CHT-based header proof. - pub fn header_proof(&self, id: &BlockId) -> error::Result<(Block::Header, Vec>)> { - self.header_proof_with_cht_size(id, cht::SIZE) - } - - /// Reads given header and generates CHT-based header proof for CHT of given size. - pub fn header_proof_with_cht_size(&self, id: &BlockId, cht_size: u64) -> error::Result<(Block::Header, Vec>)> { - let proof_error = || error::ErrorKind::Backend(format!("Failed to generate header proof for {:?}", id)); - let header = self.header(id)?.ok_or_else(|| error::ErrorKind::UnknownBlock(format!("{:?}", id)))?; - let block_num = *header.number(); - let cht_num = cht::block_to_cht_number(cht_size, block_num).ok_or_else(proof_error)?; - let cht_start = cht::start_number(cht_size, cht_num); - let headers = (cht_start.as_()..).map(|num| self.block_hash(As::sa(num)).unwrap_or_default()); - let proof = cht::build_proof::(cht_size, cht_num, block_num, headers) - .ok_or_else(proof_error)?; - Ok((header, proof)) - } - - /// Set up the native execution environment to call into a native runtime code. - pub fn using_environment T, T>( - &self, f: F - ) -> error::Result { - self.using_environment_at(&BlockId::Number(self.info()?.chain.best_number), &mut Default::default(), f) - } - - /// Set up the native execution environment to call into a native runtime code. - pub fn using_environment_at T, T>( - &self, - id: &BlockId, - overlay: &mut OverlayedChanges, - f: F - ) -> error::Result { - Ok(runtime_io::with_externalities(&mut Ext::new(overlay, &self.state_at(id)?), f)) - } - - /// Create a new block, built on the head of the chain. - pub fn new_block(&self) -> error::Result> - where E: Clone - { - block_builder::BlockBuilder::new(self) - } - - /// Create a new block, built on top of `parent`. - pub fn new_block_at(&self, parent: &BlockId) -> error::Result> - where E: Clone - { - block_builder::BlockBuilder::at_block(parent, &self) - } - - /// Call a runtime function at given block. - pub fn call_api(&self, at: &BlockId, function: &'static str, args: &A) -> error::Result - where - A: Encode, - R: Decode, - { - let parent = at; - let header = <::Header as HeaderT>::new( - self.block_number_from_id(&parent)? - .ok_or_else(|| error::ErrorKind::UnknownBlock(format!("{:?}", parent)))? + As::sa(1), - Default::default(), - Default::default(), - self.block_hash_from_id(&parent)? - .ok_or_else(|| error::ErrorKind::UnknownBlock(format!("{:?}", parent)))?, - Default::default() - ); - self.state_at(&parent).and_then(|state| { - let mut overlay = Default::default(); - let execution_manager = || ExecutionManager::Both(|wasm_result, native_result| { - warn!("Consensus error between wasm and native runtime execution at block {:?}", at); - warn!(" Function {:?}", function); - warn!(" Native result {:?}", native_result); - warn!(" Wasm result {:?}", wasm_result); - wasm_result - }); - self.executor().call_at_state( - &state, - &mut overlay, - "initialise_block", - &header.encode(), - execution_manager() - )?; - let (r, _) = args.using_encoded(|input| - self.executor().call_at_state( - &state, - &mut overlay, - function, - input, - execution_manager() - ))?; - Ok(R::decode(&mut &r[..]) - .ok_or_else(|| error::Error::from(error::ErrorKind::CallResultDecode(function)))?) - }) - } - - /// Check a header's justification. - pub fn check_justification( - &self, - header: ::Header, - justification: ::bft::UncheckedJustification, - ) -> error::Result> { - let parent_hash = header.parent_hash().clone(); - let authorities = self.authorities_at(&BlockId::Hash(parent_hash))?; - let just = ::bft::check_justification::(&authorities[..], parent_hash, justification) - .map_err(|_| - error::ErrorKind::BadJustification( - format!("{}", header.hash()) - ) - )?; - Ok(JustifiedHeader { - header, - justification: just, - authorities, - }) - } - - /// Queue a block for import. - pub fn import_block( - &self, - origin: BlockOrigin, - header: JustifiedHeader, - body: Option::Extrinsic>>, - ) -> error::Result { - let (header, justification, authorities) = header.into_inner(); - let parent_hash = header.parent_hash().clone(); - match self.backend.blockchain().status(BlockId::Hash(parent_hash))? { - blockchain::BlockStatus::InChain => {}, - blockchain::BlockStatus::Unknown => return Ok(ImportResult::UnknownParent), - } - let hash = header.hash(); - let _import_lock = self.import_lock.lock(); - let height: u64 = header.number().as_(); - *self.importing_block.write() = Some(hash); - let result = self.execute_and_import_block(origin, hash, header, justification, body, authorities); - *self.importing_block.write() = None; - telemetry!("block.import"; - "height" => height, - "best" => ?hash, - "origin" => ?origin - ); - result - } - - fn execute_and_import_block( - &self, - origin: BlockOrigin, - hash: Block::Hash, - header: Block::Header, - justification: bft::Justification, - body: Option>, - authorities: Vec, - ) -> error::Result { - let parent_hash = header.parent_hash().clone(); - match self.backend.blockchain().status(BlockId::Hash(hash))? { - blockchain::BlockStatus::InChain => return Ok(ImportResult::AlreadyInChain), - blockchain::BlockStatus::Unknown => {}, - } - - let mut transaction = self.backend.begin_operation(BlockId::Hash(parent_hash))?; - let (storage_update, storage_changes) = match transaction.state()? { - Some(transaction_state) => { - let mut overlay = Default::default(); - let mut r = self.executor.call_at_state( - transaction_state, - &mut overlay, - "execute_block", - &::new(header.clone(), body.clone().unwrap_or_default()).encode(), - match (origin, self.execution_strategy) { - (BlockOrigin::NetworkInitialSync, _) | (_, ExecutionStrategy::NativeWhenPossible) => - ExecutionManager::NativeWhenPossible, - (_, ExecutionStrategy::AlwaysWasm) => ExecutionManager::AlwaysWasm, - _ => ExecutionManager::Both(|wasm_result, native_result| { - warn!("Consensus error between wasm and native block execution at block {}", hash); - warn!(" Header {:?}", header); - warn!(" Native result {:?}", native_result); - warn!(" Wasm result {:?}", wasm_result); - telemetry!("block.execute.consensus_failure"; - "hash" => ?hash, - "origin" => ?origin, - "header" => ?header - ); - wasm_result - }), - }, - ); - let (_, storage_update) = r?; - overlay.commit_prospective(); - (Some(storage_update), Some(overlay.into_committed())) - }, - None => (None, None) - }; - - let is_new_best = header.number() == &(self.backend.blockchain().info()?.best_number + One::one()); - trace!("Imported {}, (#{}), best={}, origin={:?}", hash, header.number(), is_new_best, origin); - let unchecked: bft::UncheckedJustification<_> = justification.uncheck().into(); - transaction.set_block_data(header.clone(), body, Some(unchecked.into()), is_new_best)?; - transaction.update_authorities(authorities); - if let Some(storage_update) = storage_update { - transaction.update_storage(storage_update)?; - } - self.backend.commit_operation(transaction)?; - - if origin == BlockOrigin::NetworkBroadcast || origin == BlockOrigin::Own || origin == BlockOrigin::ConsensusBroadcast { - - if let Some(storage_changes) = storage_changes { - // TODO [ToDr] How to handle re-orgs? Should we re-emit all storage changes? - self.storage_notifications.lock() - .trigger(&hash, storage_changes); - } - - let notification = BlockImportNotification:: { - hash: hash, - origin: origin, - header: header, - is_new_best: is_new_best, - }; - self.import_notification_sinks.lock() - .retain(|sink| sink.unbounded_send(notification.clone()).is_ok()); - } - Ok(ImportResult::Queued) - } - - /// Attempts to revert the chain by `n` blocks. Returns the number of blocks that were - /// successfully reverted. - pub fn revert(&self, n: NumberFor) -> error::Result> { - Ok(self.backend.revert(n)?) - } - - /// Get blockchain info. - pub fn info(&self) -> error::Result> { - let info = self.backend.blockchain().info().map_err(|e| error::Error::from_blockchain(Box::new(e)))?; - Ok(ClientInfo { - chain: info, - best_queued_hash: None, - best_queued_number: None, - }) - } - - /// Get block status. - pub fn block_status(&self, id: &BlockId) -> error::Result { - // TODO: more efficient implementation - if let BlockId::Hash(ref h) = id { - if self.importing_block.read().as_ref().map_or(false, |importing| h == importing) { - return Ok(BlockStatus::Queued); - } - } - match self.backend.blockchain().header(*id).map_err(|e| error::Error::from_blockchain(Box::new(e)))?.is_some() { - true => Ok(BlockStatus::InChain), - false => Ok(BlockStatus::Unknown), - } - } - - /// Get block hash by number. - pub fn block_hash(&self, block_number: <::Header as HeaderT>::Number) -> error::Result> { - self.backend.blockchain().hash(block_number) - } - - /// Convert an arbitrary block ID into a block hash. - pub fn block_hash_from_id(&self, id: &BlockId) -> error::Result> { - match *id { - BlockId::Hash(h) => Ok(Some(h)), - BlockId::Number(n) => self.block_hash(n), - } - } - - /// Convert an arbitrary block ID into a block hash. - pub fn block_number_from_id(&self, id: &BlockId) -> error::Result::Header as HeaderT>::Number>> { - match *id { - BlockId::Hash(_) => Ok(self.header(id)?.map(|h| h.number().clone())), - BlockId::Number(n) => Ok(Some(n)), - } - } - - /// Get block header by id. - pub fn header(&self, id: &BlockId) -> error::Result::Header>> { - self.backend.blockchain().header(*id) - } - - /// Get block body by id. - pub fn body(&self, id: &BlockId) -> error::Result::Extrinsic>>> { - self.backend.blockchain().body(*id) - } - - /// Get block justification set by id. - pub fn justification(&self, id: &BlockId) -> error::Result>> { - self.backend.blockchain().justification(*id) - } - - /// Get full block by id. - pub fn block(&self, id: &BlockId) -> error::Result>> { - Ok(match (self.header(id)?, self.body(id)?, self.justification(id)?) { - (Some(header), Some(extrinsics), Some(justification)) => - Some(SignedBlock { block: RuntimeBlock { header, extrinsics }, justification }), - _ => None, - }) - } - - /// Get best block header. - pub fn best_block_header(&self) -> error::Result<::Header> { - let info = self.backend.blockchain().info().map_err(|e| error::Error::from_blockchain(Box::new(e)))?; - Ok(self.header(&BlockId::Hash(info.best_hash))?.expect("Best block header must always exist")) - } -} - -impl bft::BlockImport for Client - where - B: backend::Backend, - E: CallExecutor, - Block: BlockT, -{ - fn import_block( - &self, - block: Block, - justification: ::bft::Justification, - authorities: &[AuthorityId] - ) -> bool { - let (header, extrinsics) = block.deconstruct(); - let justified_header = JustifiedHeader { - header: header, - justification, - authorities: authorities.to_vec(), - }; - - self.import_block(BlockOrigin::ConsensusBroadcast, justified_header, Some(extrinsics)).is_ok() - } -} - -impl bft::Authorities for Client - where - B: backend::Backend, - E: CallExecutor, - Block: BlockT, -{ - fn authorities(&self, at: &BlockId) -> Result, bft::Error> { - let on_chain_version: Result<_, bft::Error> = self.runtime_version_at(at) - .map_err(|e| { trace!("Error getting runtime version {:?}", e); bft::ErrorKind::RuntimeVersionMissing.into() }); - let on_chain_version = on_chain_version?; - let native_version: Result<_, bft::Error> = self.executor.native_runtime_version() - .ok_or_else(|| bft::ErrorKind::NativeRuntimeMissing.into()); - let native_version = native_version?; - if !on_chain_version.can_author_with(&native_version) { - return Err(bft::ErrorKind::IncompatibleAuthoringRuntime(on_chain_version, native_version).into()) - } - self.authorities_at(at).map_err(|_| { - let descriptor = format!("{:?}", at); - bft::ErrorKind::StateUnavailable(descriptor).into() - }) - } -} - -impl BlockchainEvents for Client -where - E: CallExecutor, - Block: BlockT, -{ - /// Get block import event stream. - fn import_notification_stream(&self) -> BlockchainEventStream { - let (sink, stream) = mpsc::unbounded(); - self.import_notification_sinks.lock().push(sink); - stream - } - - /// Get storage changes event stream. - fn storage_changes_notification_stream(&self, filter_keys: Option<&[StorageKey]>) -> error::Result> { - Ok(self.storage_notifications.lock().listen(filter_keys)) - } -} - -impl ChainHead for Client -where - B: backend::Backend, - E: CallExecutor, - Block: BlockT, -{ - fn best_block_header(&self) -> error::Result<::Header> { - Client::best_block_header(self) - } -} - -impl BlockBody for Client - where - B: backend::Backend, - E: CallExecutor, - Block: BlockT, -{ - fn block_body(&self, id: &BlockId) -> error::Result::Extrinsic>>> { - self.body(id) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use keyring::Keyring; - use test_client::{self, TestClient}; - use test_client::client::BlockOrigin; - use test_client::client::backend::Backend as TestBackend; - use test_client::{runtime as test_runtime, BlockBuilderExt}; - use test_client::runtime::Transfer; - - #[test] - fn client_initialises_from_genesis_ok() { - let client = test_client::new(); - - assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Alice.to_raw_public().into())).unwrap(), 1000); - assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Ferdie.to_raw_public().into())).unwrap(), 0); - } - - #[test] - fn authorities_call_works() { - let client = test_client::new(); - - assert_eq!(client.info().unwrap().chain.best_number, 0); - assert_eq!(client.authorities_at(&BlockId::Number(0)).unwrap(), vec![ - Keyring::Alice.to_raw_public().into(), - Keyring::Bob.to_raw_public().into(), - Keyring::Charlie.to_raw_public().into() - ]); - } - - #[test] - fn block_builder_works_with_no_transactions() { - let client = test_client::new(); - - let builder = client.new_block().unwrap(); - - client.justify_and_import(BlockOrigin::Own, builder.bake().unwrap()).unwrap(); - - assert_eq!(client.info().unwrap().chain.best_number, 1); - } - - #[test] - fn block_builder_works_with_transactions() { - let client = test_client::new(); - - let mut builder = client.new_block().unwrap(); - - builder.push_transfer(Transfer { - from: Keyring::Alice.to_raw_public().into(), - to: Keyring::Ferdie.to_raw_public().into(), - amount: 42, - nonce: 0, - }).unwrap(); - - client.justify_and_import(BlockOrigin::Own, builder.bake().unwrap()).unwrap(); - - assert_eq!(client.info().unwrap().chain.best_number, 1); - assert!(client.state_at(&BlockId::Number(1)).unwrap() != client.state_at(&BlockId::Number(0)).unwrap()); - assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Alice.to_raw_public().into())).unwrap(), 958); - assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Ferdie.to_raw_public().into())).unwrap(), 42); - } - - #[test] - fn client_uses_authorities_from_blockchain_cache() { - let client = test_client::new(); - test_client::client::in_mem::cache_authorities_at( - client.backend().blockchain(), - Default::default(), - Some(vec![[1u8; 32].into()])); - assert_eq!(client.authorities_at( - &BlockId::Hash(Default::default())).unwrap(), - vec![[1u8; 32].into()]); - } - - #[test] - fn block_builder_does_not_include_invalid() { - let client = test_client::new(); - - let mut builder = client.new_block().unwrap(); - - builder.push_transfer(Transfer { - from: Keyring::Alice.to_raw_public().into(), - to: Keyring::Ferdie.to_raw_public().into(), - amount: 42, - nonce: 0, - }).unwrap(); - - assert!(builder.push_transfer(Transfer { - from: Keyring::Eve.to_raw_public().into(), - to: Keyring::Alice.to_raw_public().into(), - amount: 42, - nonce: 0, - }).is_err()); - - client.justify_and_import(BlockOrigin::Own, builder.bake().unwrap()).unwrap(); - - assert_eq!(client.info().unwrap().chain.best_number, 1); - assert!(client.state_at(&BlockId::Number(1)).unwrap() != client.state_at(&BlockId::Number(0)).unwrap()); - assert_eq!(client.body(&BlockId::Number(1)).unwrap().unwrap().len(), 1) - } - - #[test] - fn json_metadata() { - let client = test_client::new(); - - let mut builder = client.new_block().unwrap(); - - builder.push_transfer(Transfer { - from: Keyring::Alice.to_raw_public().into(), - to: Keyring::Ferdie.to_raw_public().into(), - amount: 42, - nonce: 0, - }).unwrap(); - - assert!(builder.push_transfer(Transfer { - from: Keyring::Eve.to_raw_public().into(), - to: Keyring::Alice.to_raw_public().into(), - amount: 42, - nonce: 0, - }).is_err()); - - client.justify_and_import(BlockOrigin::Own, builder.bake().unwrap()).unwrap(); - - assert_eq!( - client.json_metadata(&BlockId::Number(1)).unwrap(), - r#"{ "events": { "name": "Test", "events": { "event": hallo } } }"# - ); - } -} diff --git a/substrate/client/src/error.rs b/substrate/client/src/error.rs deleted file mode 100644 index b1eb27385ba44..0000000000000 --- a/substrate/client/src/error.rs +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright 2017 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 . - -//! Polkadot client possible errors. - -use std; -use state_machine; -use runtime_primitives::ApplyError; - -error_chain! { - errors { - /// Backend error. - Backend(s: String) { - description("Unrecoverable backend error"), - display("Backend error: {}", s), - } - - /// Unknown block. - UnknownBlock(h: String) { - description("unknown block"), - display("UnknownBlock: {}", &*h), - } - - /// Applying extrinsic error. - ApplyExtinsicFailed(e: ApplyError) { - description("Extrinsic error"), - display("Extrinsic error: {:?}", e), - } - - /// Execution error. - Execution(e: Box) { - description("execution error"), - display("Execution: {}", e), - } - - /// Blockchain error. - Blockchain(e: Box) { - description("Blockchain error"), - display("Blockchain: {}", e), - } - - /// Invalid state data. - AuthLenEmpty { - description("authority count state error"), - display("Current state of blockchain has no authority count value"), - } - - /// Invalid state data. - AuthEmpty(i: u32) { - description("authority value state error"), - display("Current state of blockchain has no authority value for index {}", i), - } - - /// Invalid state data. - AuthLenInvalid { - description("authority count state error"), - display("Current state of blockchain has invalid authority count value"), - } - - /// Cound not get runtime version. - VersionInvalid { - description("Runtime version error"), - display("On-chain runtime does not specify version"), - } - - /// Invalid state data. - AuthInvalid(i: u32) { - description("authority value state error"), - display("Current state of blockchain has invalid authority value for index {}", i), - } - - /// Bad justification for header. - BadJustification(h: String) { - description("bad justification for header"), - display("bad justification for header: {}", &*h), - } - - /// Not available on light client. - NotAvailableOnLightClient { - description("not available on light client"), - display("This method is not currently available when running in light client mode"), - } - - /// Invalid remote header proof. - InvalidHeaderProof { - description("invalid header proof"), - display("Remote node has responded with invalid header proof"), - } - - /// Invalid remote execution proof. - InvalidExecutionProof { - description("invalid execution proof"), - display("Remote node has responded with invalid execution proof"), - } - - /// Remote fetch has been cancelled. - RemoteFetchCancelled { - description("remote fetch cancelled"), - display("Remote data fetch has been cancelled"), - } - - /// Remote fetch has been failed. - RemoteFetchFailed { - description("remote fetch failed"), - display("Remote data fetch has been failed"), - } - - /// Error decoding call result. - CallResultDecode(method: &'static str) { - description("Error decoding call result") - display("Error decoding call result of {}", method) - } - } -} - -// TODO [ToDr] Temporary, state_machine::Error should be a regular error not Box. -impl From> for Error { - fn from(e: Box) -> Self { - ErrorKind::Execution(e).into() - } -} - -impl From for Error { - fn from(e: state_machine::backend::Void) -> Self { - match e {} - } -} - -impl Error { - /// Chain a blockchain error. - pub fn from_blockchain(e: Box) -> Self { - ErrorKind::Blockchain(e).into() - } - - /// Chain a state error. - pub fn from_state(e: Box) -> Self { - ErrorKind::Execution(e).into() - } -} - -impl state_machine::Error for Error {} diff --git a/substrate/client/src/genesis.rs b/substrate/client/src/genesis.rs index 93b8244fec534..9c7810aedca3d 100644 --- a/substrate/client/src/genesis.rs +++ b/substrate/client/src/genesis.rs @@ -50,8 +50,7 @@ mod tests { use test_client; use test_client::runtime::genesismap::{GenesisConfig, additional_storage_with_genesis}; use test_client::runtime::{Hash, Transfer, Block, BlockNumber, Header, Digest, Extrinsic}; - use ed25519::{Public, Pair}; - use primitives::{Blake2Hasher, RlpCodec}; + use primitives::{Blake2Hasher, RlpCodec, ed25519::{Public, Pair}}; native_executor_instance!(Executor, test_client::runtime::api::dispatch, test_client::runtime::VERSION, include_bytes!("../../test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm")); diff --git a/substrate/client/src/in_mem.rs b/substrate/client/src/in_mem.rs deleted file mode 100644 index 74a1fdbabe52c..0000000000000 --- a/substrate/client/src/in_mem.rs +++ /dev/null @@ -1,439 +0,0 @@ -// Copyright 2017 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 . - -//! In memory client backend - -use std::collections::HashMap; -use std::sync::Arc; -use parking_lot::RwLock; -use error; -use backend; -use light; -use primitives::AuthorityId; -use runtime_primitives::generic::BlockId; -use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, Zero, NumberFor, As}; -use runtime_primitives::bft::Justification; -use blockchain::{self, BlockStatus}; -use state_machine::backend::{Backend as StateBackend, InMemory}; -use patricia_trie::NodeCodec; -use hashdb::Hasher; -use heapsize::HeapSizeOf; - -struct PendingBlock { - block: StoredBlock, - is_best: bool, -} - -#[derive(PartialEq, Eq, Clone)] -enum StoredBlock { - Header(B::Header, Option>), - Full(B, Option>), -} - -impl StoredBlock { - fn new(header: B::Header, body: Option>, just: Option>) -> Self { - match body { - Some(body) => StoredBlock::Full(B::new(header, body), just), - None => StoredBlock::Header(header, just), - } - } - - fn header(&self) -> &B::Header { - match *self { - StoredBlock::Header(ref h, _) => h, - StoredBlock::Full(ref b, _) => b.header(), - } - } - - fn justification(&self) -> Option<&Justification> { - match *self { - StoredBlock::Header(_, ref j) | StoredBlock::Full(_, ref j) => j.as_ref() - } - } - - fn extrinsics(&self) -> Option<&[B::Extrinsic]> { - match *self { - StoredBlock::Header(_, _) => None, - StoredBlock::Full(ref b, _) => Some(b.extrinsics()) - } - } - - fn into_inner(self) -> (B::Header, Option>, Option>) { - match self { - StoredBlock::Header(header, just) => (header, None, just), - StoredBlock::Full(block, just) => { - let (header, body) = block.deconstruct(); - (header, Some(body), just) - } - } - } -} - -#[derive(Clone)] -struct BlockchainStorage { - blocks: HashMap>, - hashes: HashMap<<::Header as HeaderT>::Number, Block::Hash>, - best_hash: Block::Hash, - best_number: <::Header as HeaderT>::Number, - genesis_hash: Block::Hash, - cht_roots: HashMap, Block::Hash>, -} - -/// In-memory blockchain. Supports concurrent reads. -pub struct Blockchain { - storage: Arc>>, - cache: Cache, -} - -struct Cache { - storage: Arc>>, - authorities_at: RwLock>>>, -} - -impl Clone for Blockchain { - fn clone(&self) -> Self { - let storage = Arc::new(RwLock::new(self.storage.read().clone())); - Blockchain { - storage: storage.clone(), - cache: Cache { - storage, - authorities_at: RwLock::new(self.cache.authorities_at.read().clone()), - }, - } - } -} - -impl Blockchain { - /// Get header hash of given block. - pub fn id(&self, id: BlockId) -> Option { - match id { - BlockId::Hash(h) => Some(h), - BlockId::Number(n) => self.storage.read().hashes.get(&n).cloned(), - } - } - - /// Create new in-memory blockchain storage. - pub fn new() -> Blockchain { - let storage = Arc::new(RwLock::new( - BlockchainStorage { - blocks: HashMap::new(), - hashes: HashMap::new(), - best_hash: Default::default(), - best_number: Zero::zero(), - genesis_hash: Default::default(), - cht_roots: HashMap::new(), - })); - Blockchain { - storage: storage.clone(), - cache: Cache { - storage: storage, - authorities_at: Default::default(), - }, - } - } - - /// Insert a block header and associated data. - pub fn insert( - &self, - hash: Block::Hash, - header: ::Header, - justification: Option>, - body: Option::Extrinsic>>, - is_new_best: bool - ) { - let number = header.number().clone(); - let mut storage = self.storage.write(); - storage.blocks.insert(hash.clone(), StoredBlock::new(header, body, justification)); - storage.hashes.insert(number, hash.clone()); - if is_new_best { - storage.best_hash = hash.clone(); - storage.best_number = number.clone(); - } - if number == Zero::zero() { - storage.genesis_hash = hash; - } - } - - /// Compare this blockchain with another in-mem blockchain - pub fn equals_to(&self, other: &Self) -> bool { - self.canon_equals_to(other) && self.storage.read().blocks == other.storage.read().blocks - } - - /// Compare canonical chain to other canonical chain. - pub fn canon_equals_to(&self, other: &Self) -> bool { - let this = self.storage.read(); - let other = other.storage.read(); - this.hashes == other.hashes - && this.best_hash == other.best_hash - && this.best_number == other.best_number - && this.genesis_hash == other.genesis_hash - } - - /// Insert CHT root. - pub fn insert_cht_root(&self, block: NumberFor, cht_root: Block::Hash) { - self.storage.write().cht_roots.insert(block, cht_root); - } -} - -impl blockchain::HeaderBackend for Blockchain { - fn header(&self, id: BlockId) -> error::Result::Header>> { - Ok(self.id(id).and_then(|hash| { - self.storage.read().blocks.get(&hash).map(|b| b.header().clone()) - })) - } - - fn info(&self) -> error::Result> { - let storage = self.storage.read(); - Ok(blockchain::Info { - best_hash: storage.best_hash, - best_number: storage.best_number, - genesis_hash: storage.genesis_hash, - }) - } - - fn status(&self, id: BlockId) -> error::Result { - match self.id(id).map_or(false, |hash| self.storage.read().blocks.contains_key(&hash)) { - true => Ok(BlockStatus::InChain), - false => Ok(BlockStatus::Unknown), - } - } - - fn number(&self, hash: Block::Hash) -> error::Result>> { - Ok(self.storage.read().blocks.get(&hash).map(|b| *b.header().number())) - } - - fn hash(&self, number: <::Header as HeaderT>::Number) -> error::Result> { - Ok(self.id(BlockId::Number(number))) - } -} - - -impl blockchain::Backend for Blockchain { - fn body(&self, id: BlockId) -> error::Result::Extrinsic>>> { - Ok(self.id(id).and_then(|hash| { - self.storage.read().blocks.get(&hash) - .and_then(|b| b.extrinsics().map(|x| x.to_vec())) - })) - } - - fn justification(&self, id: BlockId) -> error::Result>> { - Ok(self.id(id).and_then(|hash| self.storage.read().blocks.get(&hash).and_then(|b| - b.justification().map(|x| x.clone())) - )) - } - - fn cache(&self) -> Option<&blockchain::Cache> { - Some(&self.cache) - } -} - -impl light::blockchain::Storage for Blockchain - where - Block::Hash: From<[u8; 32]>, -{ - fn import_header( - &self, - is_new_best: bool, - header: Block::Header, - authorities: Option> - ) -> error::Result<()> { - let hash = header.hash(); - let parent_hash = *header.parent_hash(); - self.insert(hash, header, None, None, is_new_best); - if is_new_best { - self.cache.insert(parent_hash, authorities); - } - Ok(()) - } - - fn cht_root(&self, _cht_size: u64, block: NumberFor) -> error::Result { - self.storage.read().cht_roots.get(&block).cloned() - .ok_or_else(|| error::ErrorKind::Backend(format!("CHT for block {} not exists", block)).into()) - } - - fn cache(&self) -> Option<&blockchain::Cache> { - Some(&self.cache) - } -} - -/// In-memory operation. -pub struct BlockImportOperation> { - pending_block: Option>, - pending_authorities: Option>, - old_state: InMemory, - new_state: Option>, -} - -impl backend::BlockImportOperation for BlockImportOperation -where - Block: BlockT, - H: Hasher, - C: NodeCodec, - H::Out: HeapSizeOf, -{ - type State = InMemory; - - fn state(&self) -> error::Result> { - Ok(Some(&self.old_state)) - } - - fn set_block_data( - &mut self, - header: ::Header, - body: Option::Extrinsic>>, - justification: Option>, - is_new_best: bool - ) -> error::Result<()> { - assert!(self.pending_block.is_none(), "Only one block per operation is allowed"); - self.pending_block = Some(PendingBlock { - block: StoredBlock::new(header, body, justification), - is_best: is_new_best, - }); - Ok(()) - } - - fn update_authorities(&mut self, authorities: Vec) { - self.pending_authorities = Some(authorities); - } - - fn update_storage(&mut self, update: as StateBackend>::Transaction) -> error::Result<()> { - self.new_state = Some(self.old_state.update(update)); - Ok(()) - } - - fn reset_storage, Vec)>>(&mut self, iter: I) -> error::Result<()> { - self.new_state = Some(InMemory::from(iter.collect::>())); - Ok(()) - } -} - -/// In-memory backend. Keeps all states and blocks in memory. Useful for testing. -pub struct Backend -where - Block: BlockT, - H: Hasher, - C: NodeCodec -{ - states: RwLock>>, - blockchain: Blockchain, -} - -impl Backend -where - Block: BlockT, - H: Hasher, - C: NodeCodec -{ - /// Create a new instance of in-mem backend. - pub fn new() -> Backend { - Backend { - states: RwLock::new(HashMap::new()), - blockchain: Blockchain::new(), - } - } -} - -impl backend::Backend for Backend -where - Block: BlockT, - H: Hasher, - H::Out: HeapSizeOf, - C: NodeCodec + Send + Sync, -{ - type BlockImportOperation = BlockImportOperation; - type Blockchain = Blockchain; - type State = InMemory; - - fn begin_operation(&self, block: BlockId) -> error::Result { - let state = match block { - BlockId::Hash(ref h) if h.clone() == Default::default() => Self::State::default(), - _ => self.state_at(block)?, - }; - - Ok(BlockImportOperation { - pending_block: None, - pending_authorities: None, - old_state: state, - new_state: None, - }) - } - - fn commit_operation(&self, operation: Self::BlockImportOperation) -> error::Result<()> { - if let Some(pending_block) = operation.pending_block { - let old_state = &operation.old_state; - let (header, body, justification) = pending_block.block.into_inner(); - let hash = header.hash(); - let parent_hash = *header.parent_hash(); - - self.states.write().insert(hash, operation.new_state.unwrap_or_else(|| old_state.clone())); - self.blockchain.insert(hash, header, justification, body, pending_block.is_best); - // dumb implementation - store value for each block - if pending_block.is_best { - self.blockchain.cache.insert(parent_hash, operation.pending_authorities); - } - } - Ok(()) - } - - fn blockchain(&self) -> &Self::Blockchain { - &self.blockchain - } - - fn state_at(&self, block: BlockId) -> error::Result { - match self.blockchain.id(block).and_then(|id| self.states.read().get(&id).cloned()) { - Some(state) => Ok(state), - None => Err(error::ErrorKind::UnknownBlock(format!("{}", block)).into()), - } - } - - fn revert(&self, _n: NumberFor) -> error::Result> { - Ok(As::sa(0)) - } -} - -impl backend::LocalBackend for Backend -where - Block: BlockT, - H: Hasher, - H::Out: HeapSizeOf, - C: NodeCodec + Send + Sync, -{} - -impl Cache { - fn insert(&self, at: Block::Hash, authorities: Option>) { - self.authorities_at.write().insert(at, authorities); - } -} - -impl blockchain::Cache for Cache { - fn authorities_at(&self, block: BlockId) -> Option> { - let hash = match block { - BlockId::Hash(hash) => hash, - BlockId::Number(number) => self.storage.read().hashes.get(&number).cloned()?, - }; - - self.authorities_at.read().get(&hash).cloned().unwrap_or(None) - } -} - -/// Insert authorities entry into in-memory blockchain cache. Extracted as a separate function to use it in tests. -pub fn cache_authorities_at( - blockchain: &Blockchain, - at: Block::Hash, - authorities: Option> -) { - blockchain.cache.insert(at, authorities); -} diff --git a/substrate/client/src/lib.rs b/substrate/client/src/lib.rs deleted file mode 100644 index 127aafe848bfb..0000000000000 --- a/substrate/client/src/lib.rs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! Substrate Client and associated logic. - -#![warn(missing_docs)] -#![recursion_limit="128"] - -extern crate substrate_bft as bft; -extern crate substrate_codec as codec; -extern crate substrate_primitives as primitives; -extern crate substrate_runtime_io as runtime_io; -extern crate substrate_runtime_support as runtime_support; -extern crate substrate_runtime_primitives as runtime_primitives; -extern crate substrate_state_machine as state_machine; -#[cfg(test)] extern crate substrate_keyring as keyring; -#[cfg(test)] extern crate substrate_test_client as test_client; -#[macro_use] extern crate substrate_telemetry; -#[macro_use] extern crate slog; // needed until we can reexport `slog_info` from `substrate_telemetry` - -extern crate ed25519; -extern crate fnv; -extern crate futures; -extern crate parking_lot; -extern crate triehash; -extern crate patricia_trie; -extern crate hashdb; -extern crate rlp; -extern crate heapsize; - -#[macro_use] extern crate error_chain; -#[macro_use] extern crate log; -#[cfg_attr(test, macro_use)] extern crate substrate_executor as executor; -#[cfg(test)] #[macro_use] extern crate hex_literal; - -pub mod error; -pub mod blockchain; -pub mod backend; -pub mod cht; -pub mod in_mem; -pub mod genesis; -pub mod block_builder; -pub mod light; -mod call_executor; -mod client; -mod notifications; - -pub use blockchain::Info as ChainInfo; -pub use call_executor::{CallResult, CallExecutor, LocalCallExecutor}; -pub use client::{ - new_in_mem, - BlockBody, BlockStatus, BlockOrigin, BlockchainEventStream, BlockchainEvents, - Client, ClientInfo, ChainHead, - ImportResult, JustifiedHeader, -}; -pub use notifications::{StorageEventStream, StorageChangeSet}; -pub use state_machine::ExecutionStrategy; diff --git a/substrate/client/src/light/backend.rs b/substrate/client/src/light/backend.rs deleted file mode 100644 index 54291521bb0da..0000000000000 --- a/substrate/client/src/light/backend.rs +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright 2017 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 . - -//! Light client backend. Only stores headers and justifications of blocks. -//! Everything else is requested from full nodes on demand. - -use std::sync::{Arc, Weak}; -use futures::{Future, IntoFuture}; -use parking_lot::RwLock; - -use primitives::AuthorityId; -use runtime_primitives::{bft::Justification, generic::BlockId}; -use runtime_primitives::traits::{Block as BlockT, NumberFor}; -use state_machine::{ - Backend as StateBackend, - TrieBackend as StateTrieBackend, - TryIntoTrieBackend as TryIntoStateTrieBackend -}; - -use backend::{Backend as ClientBackend, BlockImportOperation, RemoteBackend}; -use blockchain::HeaderBackend as BlockchainHeaderBackend; -use error::{Error as ClientError, ErrorKind as ClientErrorKind, Result as ClientResult}; -use light::blockchain::{Blockchain, Storage as BlockchainStorage}; -use light::fetcher::{Fetcher, RemoteReadRequest}; -use patricia_trie::NodeCodec; -use hashdb::Hasher; - -/// Light client backend. -pub struct Backend { - blockchain: Arc>, -} - -/// Light block (header and justification) import operation. -pub struct ImportOperation { - is_new_best: bool, - header: Option, - authorities: Option>, - _phantom: ::std::marker::PhantomData<(S, F)>, -} - -/// On-demand state. -pub struct OnDemandState { - fetcher: Weak, - blockchain: Weak>, - block: Block::Hash, - cached_header: RwLock>, -} - -impl Backend { - /// Create new light backend. - pub fn new(blockchain: Arc>) -> Self { - Self { blockchain } - } - - /// Get shared blockchain reference. - pub fn blockchain(&self) -> &Arc> { - &self.blockchain - } -} - -impl ClientBackend for Backend where - Block: BlockT, - S: BlockchainStorage, - F: Fetcher, - H: Hasher, - C: NodeCodec, -{ - type BlockImportOperation = ImportOperation; - type Blockchain = Blockchain; - type State = OnDemandState; - - fn begin_operation(&self, _block: BlockId) -> ClientResult { - Ok(ImportOperation { - is_new_best: false, - header: None, - authorities: None, - _phantom: Default::default(), - }) - } - - fn commit_operation(&self, operation: Self::BlockImportOperation) -> ClientResult<()> { - let header = operation.header.expect("commit is called after set_block_data; set_block_data sets header; qed"); - self.blockchain.storage().import_header(operation.is_new_best, header, operation.authorities) - } - - fn blockchain(&self) -> &Blockchain { - &self.blockchain - } - - fn state_at(&self, block: BlockId) -> ClientResult { - let block_hash = match block { - BlockId::Hash(h) => Some(h), - BlockId::Number(n) => self.blockchain.hash(n).unwrap_or_default(), - }; - - Ok(OnDemandState { - fetcher: self.blockchain.fetcher(), - blockchain: Arc::downgrade(&self.blockchain), - block: block_hash.ok_or_else(|| ClientErrorKind::UnknownBlock(format!("{}", block)))?, - cached_header: RwLock::new(None), - }) - } - - fn revert(&self, _n: NumberFor) -> ClientResult> { - unimplemented!() - } -} - -impl RemoteBackend for Backend -where - Block: BlockT, - S: BlockchainStorage, - F: Fetcher, - H: Hasher, - C: NodeCodec, -{} - -impl BlockImportOperation for ImportOperation -where - Block: BlockT, - F: Fetcher, - S: BlockchainStorage, - H: Hasher, - C: NodeCodec, -{ - type State = OnDemandState; - - fn state(&self) -> ClientResult> { - // None means 'locally-stateless' backend - Ok(None) - } - - fn set_block_data( - &mut self, - header: Block::Header, - _body: Option>, - _justification: Option>, - is_new_best: bool - ) -> ClientResult<()> { - self.is_new_best = is_new_best; - self.header = Some(header); - Ok(()) - } - - fn update_authorities(&mut self, authorities: Vec) { - self.authorities = Some(authorities); - } - - fn update_storage(&mut self, _update: >::Transaction) -> ClientResult<()> { - // we're not storing anything locally => ignore changes - Ok(()) - } - - fn reset_storage, Vec)>>(&mut self, _iter: I) -> ClientResult<()> { - // we're not storing anything locally => ignore changes - Ok(()) - } -} - -impl StateBackend for OnDemandState - where - Block: BlockT, - S: BlockchainStorage, - F: Fetcher, - H: Hasher, - C: NodeCodec, -{ - type Error = ClientError; - type Transaction = (); - - fn storage(&self, key: &[u8]) -> ClientResult>> { - let mut header = self.cached_header.read().clone(); - if header.is_none() { - let cached_header = self.blockchain.upgrade() - .ok_or_else(|| ClientErrorKind::UnknownBlock(format!("{}", self.block)).into()) - .and_then(|blockchain| blockchain.expect_header(BlockId::Hash(self.block)))?; - header = Some(cached_header.clone()); - *self.cached_header.write() = Some(cached_header); - } - - self.fetcher.upgrade().ok_or(ClientErrorKind::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() - } - - fn for_keys_with_prefix(&self, _prefix: &[u8], _action: A) { - // whole state is not available on light node - } - - fn storage_root(&self, _delta: I) -> (H::Out, Self::Transaction) - where I: IntoIterator, Option>)> { - (H::Out::default(), ()) - } - - fn pairs(&self) -> Vec<(Vec, Vec)> { - // whole state is not available on light node - Vec::new() - } -} - -impl TryIntoStateTrieBackend for OnDemandState -where - Block: BlockT, - F: Fetcher, - H: Hasher, - C: NodeCodec, -{ - fn try_into_trie_backend(self) -> Option> { - None - } -} diff --git a/substrate/client/src/light/blockchain.rs b/substrate/client/src/light/blockchain.rs deleted file mode 100644 index 11f3070aa6d4d..0000000000000 --- a/substrate/client/src/light/blockchain.rs +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2017 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 . - -//! Light client blockchin backend. Only stores headers and justifications of recent -//! blocks. CHT roots are stored for headers of ancient blocks. - -use std::sync::Weak; -use futures::{Future, IntoFuture}; -use parking_lot::Mutex; - -use primitives::AuthorityId; -use runtime_primitives::{bft::Justification, generic::BlockId}; -use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero}; - -use blockchain::{Backend as BlockchainBackend, BlockStatus, Cache as BlockchainCache, - HeaderBackend as BlockchainHeaderBackend, Info as BlockchainInfo}; -use cht; -use error::{ErrorKind as ClientErrorKind, Result as ClientResult}; -use light::fetcher::{Fetcher, RemoteHeaderRequest}; - -/// Light client blockchain storage. -pub trait Storage: BlockchainHeaderBackend { - /// Store new header. - fn import_header( - &self, - is_new_best: bool, - header: Block::Header, - authorities: Option> - ) -> ClientResult<()>; - - /// Get CHT root for given block. Fails if the block is not pruned (not a part of any CHT). - fn cht_root(&self, cht_size: u64, block: NumberFor) -> ClientResult; - - /// Get storage cache. - fn cache(&self) -> Option<&BlockchainCache>; -} - -/// Light client blockchain. -pub struct Blockchain { - fetcher: Mutex>, - storage: S, -} - -impl Blockchain { - /// Create new light blockchain backed with given storage. - pub fn new(storage: S) -> Self { - Self { - fetcher: Mutex::new(Default::default()), - storage, - } - } - - /// Sets fetcher reference. - pub fn set_fetcher(&self, fetcher: Weak) { - *self.fetcher.lock() = fetcher; - } - - /// Get fetcher weak reference. - pub fn fetcher(&self) -> Weak { - self.fetcher.lock().clone() - } - - /// Get storage reference. - pub fn storage(&self) -> &S { - &self.storage - } -} - -impl BlockchainHeaderBackend for Blockchain where Block: BlockT, S: Storage, F: Fetcher { - fn header(&self, id: BlockId) -> ClientResult> { - match self.storage.header(id)? { - Some(header) => Ok(Some(header)), - None => { - let number = match id { - BlockId::Hash(hash) => match self.storage.number(hash)? { - Some(number) => number, - None => return Ok(None), - }, - BlockId::Number(number) => number, - }; - - // if the header is from future or genesis (we never prune genesis) => return - if number.is_zero() || self.storage.status(BlockId::Number(number))? != BlockStatus::InChain { - return Ok(None); - } - - self.fetcher().upgrade().ok_or(ClientErrorKind::NotAvailableOnLightClient)? - .remote_header(RemoteHeaderRequest { - cht_root: self.storage.cht_root(cht::SIZE, number)?, - block: number, - retry_count: None, - }) - .into_future().wait() - .map(Some) - } - } - } - - fn info(&self) -> ClientResult> { - self.storage.info() - } - - fn status(&self, id: BlockId) -> ClientResult { - self.storage.status(id) - } - - fn number(&self, hash: Block::Hash) -> ClientResult>> { - self.storage.number(hash) - } - - fn hash(&self, number: <::Header as HeaderT>::Number) -> ClientResult> { - self.storage.hash(number) - } -} - -impl BlockchainBackend for Blockchain where Block: BlockT, S: Storage, F: Fetcher { - fn body(&self, _id: BlockId) -> ClientResult>> { - // TODO [light]: fetch from remote node - Ok(None) - } - - fn justification(&self, _id: BlockId) -> ClientResult>> { - Ok(None) - } - - fn cache(&self) -> Option<&BlockchainCache> { - self.storage.cache() - } -} diff --git a/substrate/client/src/light/call_executor.rs b/substrate/client/src/light/call_executor.rs deleted file mode 100644 index 354a2dbe33e71..0000000000000 --- a/substrate/client/src/light/call_executor.rs +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright 2017 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 . - -//! Light client call exector. Executes methods on remote full nodes, fetching -//! execution proof and checking it locally. - -use std::sync::Arc; -use futures::{IntoFuture, Future}; - -use runtime_primitives::generic::BlockId; -use runtime_primitives::traits::{Block as BlockT, Header as HeaderT}; -use state_machine::{Backend as StateBackend, CodeExecutor, OverlayedChanges, - execution_proof_check, ExecutionManager}; -use primitives::H256; -use patricia_trie::NodeCodec; -use hashdb::Hasher; -use rlp::Encodable; - -use blockchain::Backend as ChainBackend; -use call_executor::{CallExecutor, CallResult}; -use error::{Error as ClientError, ErrorKind as ClientErrorKind, Result as ClientResult}; -use light::fetcher::{Fetcher, RemoteCallRequest}; -use executor::RuntimeVersion; -use codec::Decode; -use heapsize::HeapSizeOf; -use std::marker::PhantomData; - -/// Call executor that executes methods on remote node, querying execution proof -/// and checking proof by re-executing locally. -pub struct RemoteCallExecutor { - blockchain: Arc, - fetcher: Arc, - _hasher: PhantomData, - _codec: PhantomData, -} - -impl Clone for RemoteCallExecutor { - fn clone(&self) -> Self { - RemoteCallExecutor { - blockchain: self.blockchain.clone(), - fetcher: self.fetcher.clone(), - _hasher: Default::default(), - _codec: Default::default(), - } - } -} - -impl RemoteCallExecutor { - /// Creates new instance of remote call executor. - pub fn new(blockchain: Arc, fetcher: Arc) -> Self { - RemoteCallExecutor { blockchain, fetcher, _hasher: PhantomData, _codec: PhantomData } - } -} - -impl CallExecutor for RemoteCallExecutor -where - Block: BlockT, - B: ChainBackend, - F: Fetcher, - H: Hasher, - H::Out: Ord + Encodable, - C: NodeCodec -{ - type Error = ClientError; - - fn call(&self, id: &BlockId, method: &str, call_data: &[u8]) -> ClientResult { - let block_hash = match *id { - BlockId::Hash(hash) => hash, - BlockId::Number(number) => self.blockchain.hash(number)? - .ok_or_else(|| ClientErrorKind::UnknownBlock(format!("{}", number)))?, - }; - let block_header = self.blockchain.expect_header(id.clone())?; - - 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 runtime_version(&self, id: &BlockId) -> ClientResult { - let call_result = self.call(id, "version", &[])?; - RuntimeVersion::decode(&mut call_result.return_data.as_slice()) - .ok_or_else(|| ClientErrorKind::VersionInvalid.into()) - } - - fn call_at_state< - S: StateBackend, - FF: FnOnce(Result, Self::Error>, Result, Self::Error>) -> Result, Self::Error> - >(&self, - _state: &S, - _changes: &mut OverlayedChanges, - _method: &str, - _call_data: &[u8], - _m: ExecutionManager - ) -> ClientResult<(Vec, S::Transaction)> { - Err(ClientErrorKind::NotAvailableOnLightClient.into()) - } - - fn prove_at_state>( - &self, - _state: S, - _changes: &mut OverlayedChanges, - _method: &str, - _call_data: &[u8] - ) -> ClientResult<(Vec, Vec>)> { - Err(ClientErrorKind::NotAvailableOnLightClient.into()) - } - - fn native_runtime_version(&self) -> Option { - None - } -} - -/// Check remote execution proof using given backend. -pub fn check_execution_proof( - executor: &E, - request: &RemoteCallRequest

, - remote_proof: Vec> -) -> ClientResult - where - Header: HeaderT, - E: CodeExecutor, - H: Hasher, - H::Out: Ord + Encodable + HeapSizeOf + From, - C: NodeCodec, -{ - let local_state_root = request.header.state_root(); - - let mut changes = OverlayedChanges::default(); - let (local_result, _) = execution_proof_check::( - H256::from_slice(local_state_root.as_ref()).into(), - remote_proof, - &mut changes, - executor, - &request.method, - &request.call_data)?; - - Ok(CallResult { return_data: local_result, changes }) -} - -#[cfg(test)] -mod tests { - use test_client; - use executor::NativeExecutionDispatch; - use super::*; - use primitives::RlpCodec; - - #[test] - fn execution_proof_is_generated_and_checked() { - // prepare remote client - let remote_client = test_client::new(); - let remote_block_id = BlockId::Number(0); - let remote_block_storage_root = remote_client.state_at(&remote_block_id) - .unwrap().storage_root(::std::iter::empty()).0; - - // 'fetch' execution proof from remote node - let remote_execution_proof = remote_client.execution_proof(&remote_block_id, "authorities", &[]).unwrap().1; - - // check remote execution proof locally - let local_executor = test_client::LocalExecutor::new(); - check_execution_proof::<_, _, _, RlpCodec>(&local_executor, &RemoteCallRequest { - block: test_client::runtime::Hash::default(), - header: test_client::runtime::Header { - state_root: remote_block_storage_root.into(), - parent_hash: Default::default(), - number: 0, - extrinsics_root: Default::default(), - digest: Default::default(), - }, - method: "authorities".into(), - call_data: vec![], - retry_count: None, - }, remote_execution_proof).unwrap(); - } -} diff --git a/substrate/client/src/light/fetcher.rs b/substrate/client/src/light/fetcher.rs deleted file mode 100644 index 02090a4bc6076..0000000000000 --- a/substrate/client/src/light/fetcher.rs +++ /dev/null @@ -1,308 +0,0 @@ -// Copyright 2017 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 . - -//! Light client data fetcher. Fetches requested data from remote full nodes. - -use futures::IntoFuture; - -use primitives::H256; -use hashdb::Hasher; -use patricia_trie::NodeCodec; -use rlp::Encodable; -use heapsize::HeapSizeOf; -use runtime_primitives::traits::{Block as BlockT, Header as HeaderT}; -use state_machine::{CodeExecutor, read_proof_check}; -use std::marker::PhantomData; - -use call_executor::CallResult; -use cht; -use error::{Error as ClientError, ErrorKind as ClientErrorKind, Result as ClientResult}; -use light::call_executor::check_execution_proof; - -/// Remote call request. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct RemoteCallRequest { - /// Call at state of given block. - pub block: Header::Hash, - /// Head of block at which call is perormed. - pub header: Header, - /// Method to call. - pub method: String, - /// Call data. - pub call_data: Vec, - /// Number of times to retry request. None means that default RETRY_COUNT is used. - pub retry_count: Option, -} - -/// Remote canonical header request. -#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] -pub struct RemoteHeaderRequest { - /// The root of CHT this block is included in. - pub cht_root: Header::Hash, - /// Number of the header to query. - pub block: Header::Number, - /// Number of times to retry request. None means that default RETRY_COUNT is used. - pub retry_count: Option, -} - -/// Remote storage read request. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct RemoteReadRequest { - /// Read at state of given block. - pub block: Header::Hash, - /// Head of block at which read is perormed. - pub header: Header, - /// Storage key to read. - pub key: Vec, - /// Number of times to retry request. None means that default RETRY_COUNT is used. - pub retry_count: Option, -} - -/// Light client data fetcher. Implementations of this trait must check if remote data -/// is correct (see FetchedDataChecker) and return already checked data. -pub trait Fetcher: Send + Sync { - /// Remote header future. - type RemoteHeaderResult: IntoFuture; - /// Remote storage read future. - type RemoteReadResult: IntoFuture>, Error=ClientError>; - /// Remote call result future. - type RemoteCallResult: IntoFuture; - - /// Fetch remote header. - fn remote_header(&self, request: RemoteHeaderRequest) -> Self::RemoteHeaderResult; - /// Fetch remote storage value. - fn remote_read(&self, request: RemoteReadRequest) -> Self::RemoteReadResult; - /// Fetch remote call result. - fn remote_call(&self, request: RemoteCallRequest) -> Self::RemoteCallResult; -} - -/// Light client remote data checker. -/// -/// Implementations of this trait should not use any blockchain data except that is -/// passed to its methods. -pub trait FetchChecker: Send + Sync { - /// Check remote header proof. - fn check_header_proof( - &self, - request: &RemoteHeaderRequest, - header: Option, - remote_proof: Vec> - ) -> ClientResult; - /// Check remote storage read proof. - fn check_read_proof( - &self, - request: &RemoteReadRequest, - remote_proof: Vec> - ) -> ClientResult>>; - /// Check remote method execution proof. - fn check_execution_proof( - &self, - request: &RemoteCallRequest, - remote_proof: Vec> - ) -> ClientResult; -} - -/// Remote data checker. -pub struct LightDataChecker { - executor: E, - _hasher: PhantomData, - _codec: PhantomData, -} - -impl LightDataChecker { - /// Create new light data checker. - pub fn new(executor: E) -> Self { - Self { - executor, _hasher: PhantomData, _codec: PhantomData - } - } -} - -impl FetchChecker for LightDataChecker - where - Block: BlockT, - Block::Hash: Into + From, - E: CodeExecutor, - H: Hasher, - C: NodeCodec + Sync + Send, - H::Out: Ord + Encodable + HeapSizeOf + From + From, -{ - fn check_header_proof( - &self, - request: &RemoteHeaderRequest, - remote_header: Option, - remote_proof: Vec> - ) -> ClientResult { - let remote_header = remote_header.ok_or_else(|| - ClientError::from(ClientErrorKind::InvalidHeaderProof))?; - let remote_header_hash = remote_header.hash(); - cht::check_proof::( - request.cht_root, - request.block, - remote_header_hash, - remote_proof) - .map(|_| remote_header) - } - - fn check_read_proof( - &self, - request: &RemoteReadRequest, - remote_proof: Vec> - ) -> ClientResult>> { - let local_state_root = request.header.state_root().clone(); - read_proof_check::(local_state_root.into(), remote_proof, &request.key).map_err(Into::into) - } - - fn check_execution_proof( - &self, - request: &RemoteCallRequest, - remote_proof: Vec> - ) -> ClientResult { - check_execution_proof::<_, _, H, C>(&self.executor, request, remote_proof) - } -} - -#[cfg(test)] -pub mod tests { - use futures::future::{ok, err, FutureResult}; - use parking_lot::Mutex; - use call_executor::CallResult; - use executor::{self, NativeExecutionDispatch}; - use error::Error as ClientError; - use test_client::{self, TestClient, runtime::{Hash, Block, Header}}; - use test_client::client::BlockOrigin; - use in_mem::{Blockchain as InMemoryBlockchain}; - use light::fetcher::{Fetcher, FetchChecker, LightDataChecker, - RemoteCallRequest, RemoteHeaderRequest}; - use primitives::{Blake2Hasher, RlpCodec}; - use runtime_primitives::generic::BlockId; - use state_machine::Backend; - use super::*; - - pub type OkCallFetcher = Mutex; - - impl Fetcher for OkCallFetcher { - type RemoteHeaderResult = FutureResult; - type RemoteReadResult = FutureResult>, ClientError>; - type RemoteCallResult = FutureResult; - - fn remote_header(&self, _request: RemoteHeaderRequest
) -> Self::RemoteHeaderResult { - err("Not implemented on test node".into()) - } - - fn remote_read(&self, _request: RemoteReadRequest
) -> Self::RemoteReadResult { - err("Not implemented on test node".into()) - } - - fn remote_call(&self, _request: RemoteCallRequest
) -> Self::RemoteCallResult { - ok((*self.lock()).clone()) - } - } - - fn prepare_for_read_proof_check() -> ( - LightDataChecker, Blake2Hasher, RlpCodec>, - Header, Vec>, usize) - { - // prepare remote client - let remote_client = test_client::new(); - let remote_block_id = BlockId::Number(0); - let remote_block_hash = remote_client.block_hash(0).unwrap().unwrap(); - let mut remote_block_header = remote_client.header(&remote_block_id).unwrap().unwrap(); - remote_block_header.state_root = remote_client.state_at(&remote_block_id).unwrap().storage_root(::std::iter::empty()).0.into(); - - // 'fetch' read proof from remote node - let authorities_len = remote_client.authorities_at(&remote_block_id).unwrap().len(); - let remote_read_proof = remote_client.read_proof(&remote_block_id, b":auth:len").unwrap(); - - // check remote read proof locally - let local_storage = InMemoryBlockchain::::new(); - local_storage.insert(remote_block_hash, remote_block_header.clone(), None, None, true); - let local_executor = test_client::LocalExecutor::new(); - let local_checker = LightDataChecker::new(local_executor); - (local_checker, remote_block_header, remote_read_proof, authorities_len) - } - - fn prepare_for_header_proof_check(insert_cht: bool) -> ( - LightDataChecker, Blake2Hasher, RlpCodec>, - Hash, Header, Vec>) - { - // prepare remote client - let remote_client = test_client::new(); - let mut local_headers_hashes = Vec::new(); - for i in 0..4 { - let builder = remote_client.new_block().unwrap(); - remote_client.justify_and_import(BlockOrigin::Own, builder.bake().unwrap()).unwrap(); - local_headers_hashes.push(remote_client.block_hash(i + 1).unwrap()); - } - - // 'fetch' header proof from remote node - let remote_block_id = BlockId::Number(1); - let (remote_block_header, remote_header_proof) = remote_client.header_proof_with_cht_size(&remote_block_id, 4).unwrap(); - - // check remote read proof locally - let local_storage = InMemoryBlockchain::::new(); - let local_cht_root = cht::compute_root::(4, 0, local_headers_hashes.into_iter()).unwrap(); - if insert_cht { - local_storage.insert_cht_root(1, local_cht_root); - } - let local_executor = test_client::LocalExecutor::new(); - let local_checker = LightDataChecker::new(local_executor); - (local_checker, local_cht_root, remote_block_header, remote_header_proof) - } - - #[test] - fn storage_read_proof_is_generated_and_checked() { - let (local_checker, remote_block_header, remote_read_proof, authorities_len) = prepare_for_read_proof_check(); - assert_eq!((&local_checker as &FetchChecker).check_read_proof(&RemoteReadRequest::
{ - block: remote_block_header.hash(), - header: remote_block_header, - key: b":auth:len".to_vec(), - retry_count: None, - }, remote_read_proof).unwrap().unwrap()[0], authorities_len as u8); - } - - #[test] - fn header_proof_is_generated_and_checked() { - let (local_checker, local_cht_root, remote_block_header, remote_header_proof) = prepare_for_header_proof_check(true); - assert_eq!((&local_checker as &FetchChecker).check_header_proof(&RemoteHeaderRequest::
{ - cht_root: local_cht_root, - block: 1, - retry_count: None, - }, Some(remote_block_header.clone()), remote_header_proof).unwrap(), remote_block_header); - } - - #[test] - fn check_header_proof_fails_if_cht_root_is_invalid() { - let (local_checker, _, mut remote_block_header, remote_header_proof) = prepare_for_header_proof_check(true); - remote_block_header.number = 100; - assert!((&local_checker as &FetchChecker).check_header_proof(&RemoteHeaderRequest::
{ - cht_root: Default::default(), - block: 1, - retry_count: None, - }, Some(remote_block_header.clone()), remote_header_proof).is_err()); - } - - #[test] - fn check_header_proof_fails_if_invalid_header_provided() { - let (local_checker, local_cht_root, mut remote_block_header, remote_header_proof) = prepare_for_header_proof_check(true); - remote_block_header.number = 100; - assert!((&local_checker as &FetchChecker).check_header_proof(&RemoteHeaderRequest::
{ - cht_root: local_cht_root, - block: 1, - retry_count: None, - }, Some(remote_block_header.clone()), remote_header_proof).is_err()); - } -} diff --git a/substrate/client/src/light/mod.rs b/substrate/client/src/light/mod.rs deleted file mode 100644 index d16814ae2fca5..0000000000000 --- a/substrate/client/src/light/mod.rs +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2017 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 . - -//! Light client components. - -pub mod backend; -pub mod blockchain; -pub mod call_executor; -pub mod fetcher; - -use std::sync::Arc; - -use primitives::{Blake2Hasher, RlpCodec}; -use runtime_primitives::BuildStorage; -use runtime_primitives::traits::Block as BlockT; -use state_machine::{CodeExecutor, ExecutionStrategy}; - -use client::Client; -use error::Result as ClientResult; -use light::backend::Backend; -use light::blockchain::{Blockchain, Storage as BlockchainStorage}; -use light::call_executor::RemoteCallExecutor; -use light::fetcher::{Fetcher, LightDataChecker}; -use hashdb::Hasher; -use patricia_trie::NodeCodec; - -/// Create an instance of light client blockchain backend. -pub fn new_light_blockchain, F>(storage: S) -> Arc> { - Arc::new(Blockchain::new(storage)) -} - -/// Create an instance of light client backend. -pub fn new_light_backend, F: Fetcher>(blockchain: Arc>, fetcher: Arc) -> Arc> { - blockchain.set_fetcher(Arc::downgrade(&fetcher)); - Arc::new(Backend::new(blockchain)) -} - -/// Create an instance of light client. -pub fn new_light( - backend: Arc>, - fetcher: Arc, - genesis_storage: GS, -) -> ClientResult, RemoteCallExecutor, F, Blake2Hasher, RlpCodec>, B>> - where - B: BlockT, - S: BlockchainStorage, - F: Fetcher, - GS: BuildStorage, -{ - let executor = RemoteCallExecutor::new(backend.blockchain().clone(), fetcher); - Client::new(backend, executor, genesis_storage, ExecutionStrategy::NativeWhenPossible) -} - -/// Create an instance of fetch data checker. -pub fn new_fetch_checker( - executor: E, -) -> LightDataChecker - where - E: CodeExecutor, - H: Hasher, - C: NodeCodec, -{ - LightDataChecker::new(executor) -} diff --git a/substrate/client/src/notifications.rs b/substrate/client/src/notifications.rs deleted file mode 100644 index 390513d37a8f1..0000000000000 --- a/substrate/client/src/notifications.rs +++ /dev/null @@ -1,289 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! Storage notifications - -use std::{ - collections::{HashSet, HashMap}, - sync::Arc, -}; - -use fnv::{FnvHashSet, FnvHashMap}; -use futures::sync::mpsc; -use primitives::storage::{StorageKey, StorageData}; -use runtime_primitives::traits::Block as BlockT; - -/// Storage change set -#[derive(Debug)] -pub struct StorageChangeSet { - changes: Arc)>>, - filter: Option>, -} - -impl StorageChangeSet { - /// Convert the change set into iterator over storage items. - pub fn iter<'a>(&'a self) -> impl Iterator)> + 'a { - self.changes - .iter() - .filter(move |&(key, _)| match self.filter { - Some(ref filter) => filter.contains(key), - None => true, - }) - } -} - -/// Type that implements `futures::Stream` of storage change events. -pub type StorageEventStream = mpsc::UnboundedReceiver<(H, StorageChangeSet)>; - -type SubscriberId = u64; - -/// Manages storage listeners. -#[derive(Debug)] -pub struct StorageNotifications { - next_id: SubscriberId, - wildcard_listeners: FnvHashSet, - listeners: HashMap>, - sinks: FnvHashMap, - Option>, - )>, -} - -impl Default for StorageNotifications { - fn default() -> Self { - StorageNotifications { - next_id: Default::default(), - wildcard_listeners: Default::default(), - listeners: Default::default(), - sinks: Default::default(), - } - } -} - -impl StorageNotifications { - /// Trigger notification to all listeners. - /// - /// Note the changes are going to be filtered by listener's filter key. - /// In fact no event might be sent if clients are not interested in the changes. - pub fn trigger(&mut self, hash: &Block::Hash, changeset: impl Iterator, Option>)>) { - let has_wildcard = !self.wildcard_listeners.is_empty(); - - // early exit if no listeners - if !has_wildcard && self.listeners.is_empty() { - return; - } - - let mut subscribers = self.wildcard_listeners.clone(); - let mut changes = Vec::new(); - - // Collect subscribers and changes - for (k, v) in changeset { - let k = StorageKey(k); - let listeners = self.listeners.get(&k); - - if let Some(ref listeners) = listeners { - subscribers.extend(listeners.iter()); - } - - if has_wildcard || listeners.is_some() { - changes.push((k, v.map(StorageData))); - } - } - - // Don't send empty notifications - if changes.is_empty() { - return; - } - - let changes = Arc::new(changes); - // Trigger the events - for subscriber in subscribers { - let should_remove = { - let &(ref sink, ref filter) = self.sinks.get(&subscriber) - .expect("subscribers returned from self.listeners are always in self.sinks; qed"); - sink.unbounded_send((hash.clone(), StorageChangeSet { - changes: changes.clone(), - filter: filter.clone(), - })).is_err() - }; - - if should_remove { - self.remove_subscriber(subscriber); - } - } - } - - fn remove_subscriber(&mut self, subscriber: SubscriberId) { - if let Some((_, filters)) = self.sinks.remove(&subscriber) { - match filters { - None => { - self.wildcard_listeners.remove(&subscriber); - }, - Some(filters) => { - for key in filters { - let remove_key = match self.listeners.get_mut(&key) { - Some(ref mut set) => { - set.remove(&subscriber); - set.is_empty() - }, - None => false, - }; - - if remove_key { - self.listeners.remove(&key); - } - } - }, - } - } - } - - /// Start listening for particular storage keys. - pub fn listen(&mut self, filter_keys: Option<&[StorageKey]>) -> StorageEventStream { - self.next_id += 1; - - // add subscriber for every key - let keys = match filter_keys { - None => { - self.wildcard_listeners.insert(self.next_id); - None - }, - Some(keys) => Some(keys.iter().map(|key| { - self.listeners - .entry(key.clone()) - .or_insert_with(Default::default) - .insert(self.next_id); - key.clone() - }).collect()) - }; - - // insert sink - let (tx, rx) = mpsc::unbounded(); - self.sinks.insert(self.next_id, (tx, keys)); - rx - } -} - -#[cfg(test)] -mod tests { - use runtime_primitives::testing::{H256 as Hash, Block as RawBlock}; - use super::*; - use futures::Stream; - - - #[cfg(test)] - impl From)>> for StorageChangeSet { - fn from(changes: Vec<(StorageKey, Option)>) -> Self { - StorageChangeSet { - changes: Arc::new(changes), - filter: None, - } - } - } - - #[cfg(test)] - impl PartialEq for StorageChangeSet { - fn eq(&self, other: &Self) -> bool { - self.iter().eq(other.iter()) - } - } - - type Block = RawBlock; - - #[test] - fn triggering_change_should_notify_wildcard_listeners() { - // given - let mut notifications = StorageNotifications::::default(); - let mut recv = notifications.listen(None).wait(); - - // when - let changeset = vec![ - (vec![2], Some(vec![3])), - (vec![3], None), - ]; - notifications.trigger(&1.into(), changeset.into_iter()); - - // then - assert_eq!(recv.next().unwrap(), Ok((1.into(), vec![ - (StorageKey(vec![2]), Some(StorageData(vec![3]))), - (StorageKey(vec![3]), None), - ].into()))); - } - - #[test] - fn should_only_notify_interested_listeners() { - // given - let mut notifications = StorageNotifications::::default(); - let mut recv1 = notifications.listen(Some(&[StorageKey(vec![1])])).wait(); - let mut recv2 = notifications.listen(Some(&[StorageKey(vec![2])])).wait(); - - // when - let changeset = vec![ - (vec![2], Some(vec![3])), - (vec![1], None), - ]; - notifications.trigger(&1.into(), changeset.into_iter()); - - // then - assert_eq!(recv1.next().unwrap(), Ok((1.into(), vec![ - (StorageKey(vec![1]), None), - ].into()))); - assert_eq!(recv2.next().unwrap(), Ok((1.into(), vec![ - (StorageKey(vec![2]), Some(StorageData(vec![3]))), - ].into()))); - } - - #[test] - fn should_cleanup_subscribers_if_dropped() { - // given - let mut notifications = StorageNotifications::::default(); - { - let _recv1 = notifications.listen(Some(&[StorageKey(vec![1])])).wait(); - let _recv2 = notifications.listen(Some(&[StorageKey(vec![2])])).wait(); - let _recv3 = notifications.listen(None).wait(); - assert_eq!(notifications.listeners.len(), 2); - assert_eq!(notifications.wildcard_listeners.len(), 1); - } - - // when - let changeset = vec![ - (vec![2], Some(vec![3])), - (vec![1], None), - ]; - notifications.trigger(&1.into(), changeset.into_iter()); - - // then - assert_eq!(notifications.listeners.len(), 0); - assert_eq!(notifications.wildcard_listeners.len(), 0); - } - - #[test] - fn should_not_send_empty_notifications() { - // given - let mut recv = { - let mut notifications = StorageNotifications::::default(); - let recv = notifications.listen(None).wait(); - - // when - let changeset = vec![]; - notifications.trigger(&1.into(), changeset.into_iter()); - recv - }; - - // then - assert_eq!(recv.next(), None); - } -} diff --git a/substrate/codec/Cargo.toml b/substrate/codec/Cargo.toml deleted file mode 100644 index a228686e90b1d..0000000000000 --- a/substrate/codec/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "substrate-codec" -description = "Serialization and deserialization codec for runtime values" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -arrayvec = { version = "0.4", default_features = false } - -[features] -default = ["std"] -std = [] diff --git a/substrate/codec/README.adoc b/substrate/codec/README.adoc deleted file mode 100644 index 12d3953789a0a..0000000000000 --- a/substrate/codec/README.adoc +++ /dev/null @@ -1,13 +0,0 @@ - -= Codec - -.Summary -[source, toml] ----- -include::Cargo.toml[lines=2..5] ----- - -.Description ----- -include::src/lib.rs[tag=description] ----- diff --git a/substrate/codec/derive/Cargo.toml b/substrate/codec/derive/Cargo.toml deleted file mode 100644 index d5ccd9fdd57aa..0000000000000 --- a/substrate/codec/derive/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "substrate-codec-derive" -description = "Serialization and deserialization derive macro" -version = "0.1.0" -authors = ["Parity Technologies "] - -[lib] -proc-macro = true - -[dependencies] -syn = "0.14" -quote = "0.6" -proc-macro2 = "0.4" - -[dev-dependencies] -substrate-codec = { path = "../" } - -[features] -default = ["std"] -std = [] diff --git a/substrate/codec/derive/src/decode.rs b/substrate/codec/derive/src/decode.rs deleted file mode 100644 index 0d33a7c350de1..0000000000000 --- a/substrate/codec/derive/src/decode.rs +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2018 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 proc_macro2::{Span, TokenStream, Ident}; -use syn::{ - Data, Fields, - spanned::Spanned, -}; - -pub fn quote(data: &Data, type_name: &Ident, input: &TokenStream) -> TokenStream { - let call_site = Span::call_site(); - match *data { - Data::Struct(ref data) => match data.fields { - Fields::Named(_) | Fields::Unnamed(_) => create_instance( - call_site, - quote! { #type_name }, - input, - &data.fields, - ), - Fields::Unit => { - quote_spanned! {call_site => - drop(#input); - Some(#type_name) - } - }, - }, - Data::Enum(ref data) => { - assert!(data.variants.len() < 256, "Currently only enums with at most 256 variants are encodable."); - - let recurse = data.variants.iter().enumerate().map(|(i, v)| { - let name = &v.ident; - let index = super::index(v, i); - - let create = create_instance( - call_site, - quote! { #type_name :: #name }, - input, - &v.fields, - ); - - quote_spanned! { v.span() => - x if x == #index as u8 => { - #create - }, - } - }); - - quote! { - match #input.read_byte()? { - #( #recurse )* - _ => None, - } - - } - - }, - Data::Union(_) => panic!("Union types are not supported."), - } -} - -fn create_instance(call_site: Span, name: TokenStream, input: &TokenStream, fields: &Fields) -> TokenStream { - match *fields { - Fields::Named(ref fields) => { - let recurse = fields.named.iter().map(|f| { - let name = &f.ident; - let field = quote_spanned!(call_site => #name); - - quote_spanned! { f.span() => - #field: ::codec::Decode::decode(#input)? - } - }); - - quote_spanned! {call_site => - Some(#name { - #( #recurse, )* - }) - } - }, - Fields::Unnamed(ref fields) => { - let recurse = fields.unnamed.iter().map(|f| { - quote_spanned! { f.span() => - ::codec::Decode::decode(#input)? - } - }); - - quote_spanned! {call_site => - Some(#name ( - #( #recurse, )* - )) - } - }, - Fields::Unit => { - quote_spanned! {call_site => - Some(#name) - } - }, - } -} diff --git a/substrate/codec/derive/src/encode.rs b/substrate/codec/derive/src/encode.rs deleted file mode 100644 index c378c9af51016..0000000000000 --- a/substrate/codec/derive/src/encode.rs +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -#[cfg(not(feature = "std"))] -use core::str::from_utf8; -#[cfg(feature = "std")] -use std::str::from_utf8; - -use proc_macro2::{Span, TokenStream}; -use syn::{ - Data, Field, Fields, Ident, Index, - punctuated::Punctuated, - spanned::Spanned, - token::Comma, -}; - -type FieldsList = Punctuated; - -fn encode_fields( - dest: &TokenStream, - fields: &FieldsList, - field_name: F, -) -> TokenStream where - F: Fn(usize, &Option) -> TokenStream, -{ - let recurse = fields.iter().enumerate().map(|(i, f)| { - let field = field_name(i, &f.ident); - - quote_spanned! { f.span() => - #dest.push(#field); - } - }); - - quote! { - #( #recurse )* - } -} - -pub fn quote(data: &Data, type_name: &Ident, self_: &TokenStream, dest: &TokenStream) -> TokenStream { - let call_site = Span::call_site(); - match *data { - Data::Struct(ref data) => match data.fields { - Fields::Named(ref fields) => encode_fields( - dest, - &fields.named, - |_, name| quote_spanned!(call_site => &#self_.#name), - ), - Fields::Unnamed(ref fields) => encode_fields( - dest, - &fields.unnamed, - |i, _| { - let index = Index { index: i as u32, span: call_site }; - quote_spanned!(call_site => &#self_.#index) - }, - ), - Fields::Unit => quote_spanned! { call_site => - drop(#dest); - }, - }, - Data::Enum(ref data) => { - assert!(data.variants.len() < 256, "Currently only enums with at most 256 variants are encodable."); - - let recurse = data.variants.iter().enumerate().map(|(i, f)| { - let name = &f.ident; - let index = super::index(f, i); - - match f.fields { - Fields::Named(ref fields) => { - let field_name = |_, ident: &Option| quote_spanned!(call_site => #ident); - let names = fields.named - .iter() - .enumerate() - .map(|(i, f)| field_name(i, &f.ident)); - - let encode_fields = encode_fields( - dest, - &fields.named, - |a, b| field_name(a, b), - ); - - quote_spanned! { f.span() => - #type_name :: #name { #( ref #names, )* } => { - #dest.push_byte(#index as u8); - #encode_fields - } - } - }, - Fields::Unnamed(ref fields) => { - let field_name = |i, _: &Option| { - let data = stringify(i as u8); - let ident = from_utf8(&data).expect("We never go beyond ASCII"); - let ident = Ident::new(ident, call_site); - quote_spanned!(call_site => #ident) - }; - let names = fields.unnamed - .iter() - .enumerate() - .map(|(i, f)| field_name(i, &f.ident)); - - let encode_fields = encode_fields( - dest, - &fields.unnamed, - |a, b| field_name(a, b), - ); - - quote_spanned! { f.span() => - #type_name :: #name ( #( ref #names, )* ) => { - #dest.push_byte(#index as u8); - #encode_fields - } - } - }, - Fields::Unit => { - quote_spanned! { f.span() => - #type_name :: #name => { - #dest.push_byte(#index as u8); - } - } - }, - } - }); - - quote! { - match *#self_ { - #( #recurse )*, - } - } - }, - Data::Union(_) => panic!("Union types are not supported."), - } -} -pub fn stringify(id: u8) -> [u8; 2] { - const CHARS: &[u8] = b"abcdefghijklmnopqrstuvwxyz"; - let len = CHARS.len() as u8; - let symbol = |id: u8| CHARS[(id % len) as usize]; - let a = symbol(id); - let b = symbol(id / len); - - [a, b] -} diff --git a/substrate/codec/derive/src/lib.rs b/substrate/codec/derive/src/lib.rs deleted file mode 100644 index 1bf3aebe7522a..0000000000000 --- a/substrate/codec/derive/src/lib.rs +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2018 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 . - -//! Derives serialization and deserialization codec for complex structs for simple marshalling. - -#![cfg_attr(not(feature = "std"), no_std)] - -extern crate proc_macro; -extern crate proc_macro2; - -#[macro_use] -extern crate syn; - -#[macro_use] -extern crate quote; - -use proc_macro::TokenStream; -use syn::{DeriveInput, Generics, GenericParam, Ident}; - -mod decode; -mod encode; - -const ENCODE_ERR: &str = "derive(Encode) failed"; - -#[proc_macro_derive(Encode, attributes(codec))] -pub fn encode_derive(input: TokenStream) -> TokenStream { - let input: DeriveInput = syn::parse(input).expect(ENCODE_ERR); - let name = &input.ident; - - let generics = add_trait_bounds(input.generics, parse_quote!(::codec::Encode)); - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - - let self_ = quote!(self); - let dest_ = quote!(dest); - let encoding = encode::quote(&input.data, name, &self_, &dest_); - - let expanded = quote! { - impl #impl_generics ::codec::Encode for #name #ty_generics #where_clause { - fn encode_to(&#self_, #dest_: &mut EncOut) { - #encoding - } - } - }; - - expanded.into() -} - -#[proc_macro_derive(Decode, attributes(codec))] -pub fn decode_derive(input: TokenStream) -> TokenStream { - let input: DeriveInput = syn::parse(input).expect(ENCODE_ERR); - let name = &input.ident; - - let generics = add_trait_bounds(input.generics, parse_quote!(::codec::Decode)); - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - - let input_ = quote!(input); - let decoding = decode::quote(&input.data, name, &input_); - - let expanded = quote! { - impl #impl_generics ::codec::Decode for #name #ty_generics #where_clause { - fn decode(#input_: &mut DecIn) -> Option { - #decoding - } - } - }; - - expanded.into() -} - -fn add_trait_bounds(mut generics: Generics, bounds: syn::TypeParamBound) -> Generics { - for param in &mut generics.params { - if let GenericParam::Type(ref mut type_param) = *param { - type_param.bounds.push(bounds.clone()); - } - } - generics -} - -fn index(v: &syn::Variant, i: usize) -> proc_macro2::TokenStream { - // look for an index in attributes - let index = v.attrs.iter().filter_map(|attr| { - let pair = attr.path.segments.first()?; - let seg = pair.value(); - - if seg.ident == Ident::new("codec", seg.ident.span()) { - assert_eq!(attr.path.segments.len(), 1); - - let meta = attr.interpret_meta(); - if let Some(syn::Meta::List(ref l)) = meta { - if let syn::NestedMeta::Meta(syn::Meta::NameValue(ref nv)) = l.nested.last().unwrap().value() { - assert_eq!(nv.ident, Ident::new("index", nv.ident.span())); - if let syn::Lit::Str(ref s) = nv.lit { - let byte: u8 = s.value().parse().expect("Numeric index expected."); - return Some(byte) - } - panic!("Invalid syntax for `codec` attribute: Expected string literal.") - } - } - panic!("Invalid syntax for `codec` attribute: Expected `name = value` pair.") - } else { - None - } - }).next(); - - // then fallback to discriminant or just index - index.map(|i| quote! { #i }) - .unwrap_or_else(|| v.discriminant - .as_ref() - .map(|&(_, ref expr)| quote! { #expr }) - .unwrap_or_else(|| quote! { #i }) - ) -} - diff --git a/substrate/codec/derive/tests/mod.rs b/substrate/codec/derive/tests/mod.rs deleted file mode 100644 index 6b3260f8f64e2..0000000000000 --- a/substrate/codec/derive/tests/mod.rs +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2018 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 . - -extern crate substrate_codec as codec; - -#[macro_use] -extern crate substrate_codec_derive; - -use codec::{Encode, Decode}; - -#[derive(Debug, PartialEq, Encode, Decode)] -struct Unit; - -#[derive(Debug, PartialEq, Encode, Decode)] -struct Indexed(u32, u64); - -#[derive(Debug, PartialEq, Encode, Decode)] -struct Struct { - pub a: A, - pub b: B, - pub c: C, -} - -type TestType = Struct>; - -impl Struct { - fn new(a: A, b: B, c: C) -> Self { - Self { a, b, c } - } -} - -#[derive(Debug, PartialEq, Encode, Decode)] -enum EnumType { - #[codec(index = "15")] - A, - B(u32, u64), - C { - a: u32, - b: u64, - }, -} - -#[derive(Debug, PartialEq, Encode, Decode)] -enum EnumWithDiscriminant { - A = 1, - B = 15, - C = 255, -} - -#[test] -fn should_work_for_simple_enum() { - let a = EnumType::A; - let b = EnumType::B(1, 2); - let c = EnumType::C { a: 1, b: 2 }; - - a.using_encoded(|ref slice| { - assert_eq!(slice, &b"\x0f"); - }); - b.using_encoded(|ref slice| { - assert_eq!(slice, &b"\x01\x01\0\0\0\x02\0\0\0\0\0\0\0"); - }); - c.using_encoded(|ref slice| { - assert_eq!(slice, &b"\x02\x01\0\0\0\x02\0\0\0\0\0\0\0"); - }); - - let mut da: &[u8] = b"\x0f"; - assert_eq!(EnumType::decode(&mut da), Some(a)); - let mut db: &[u8] = b"\x01\x01\0\0\0\x02\0\0\0\0\0\0\0"; - assert_eq!(EnumType::decode(&mut db), Some(b)); - let mut dc: &[u8] = b"\x02\x01\0\0\0\x02\0\0\0\0\0\0\0"; - assert_eq!(EnumType::decode(&mut dc), Some(c)); - let mut dz: &[u8] = &[0]; - assert_eq!(EnumType::decode(&mut dz), None); -} - -#[test] -fn should_work_for_enum_with_discriminant() { - EnumWithDiscriminant::A.using_encoded(|ref slice| { - assert_eq!(slice, &[1]); - }); - EnumWithDiscriminant::B.using_encoded(|ref slice| { - assert_eq!(slice, &[15]); - }); - EnumWithDiscriminant::C.using_encoded(|ref slice| { - assert_eq!(slice, &[255]); - }); - - let mut da: &[u8] = &[1]; - assert_eq!(EnumWithDiscriminant::decode(&mut da), Some(EnumWithDiscriminant::A)); - let mut db: &[u8] = &[15]; - assert_eq!(EnumWithDiscriminant::decode(&mut db), Some(EnumWithDiscriminant::B)); - let mut dc: &[u8] = &[255]; - assert_eq!(EnumWithDiscriminant::decode(&mut dc), Some(EnumWithDiscriminant::C)); - let mut dz: &[u8] = &[2]; - assert_eq!(EnumWithDiscriminant::decode(&mut dz), None); -} - -#[test] -fn should_derive_encode() { - let v = TestType::new(15, 9, b"Hello world".to_vec()); - - v.using_encoded(|ref slice| { - assert_eq!(slice, &b"\x0f\0\0\0\x09\0\0\0\0\0\0\0\x0b\0\0\0Hello world") - }); -} - -#[test] -fn should_derive_decode() { - let slice = b"\x0f\0\0\0\x09\0\0\0\0\0\0\0\x0b\0\0\0Hello world".to_vec(); - - let v = TestType::decode(&mut &*slice); - - assert_eq!(v, Some(TestType::new(15, 9, b"Hello world".to_vec()))); -} - -#[test] -fn should_work_for_unit() { - let v = Unit; - - v.using_encoded(|ref slice| { - assert_eq!(slice, &[]); - }); - - let mut a: &[u8] = &[]; - assert_eq!(Unit::decode(&mut a), Some(Unit)); -} - -#[test] -fn should_work_for_indexed() { - let v = Indexed(1, 2); - - v.using_encoded(|ref slice| { - assert_eq!(slice, &b"\x01\0\0\0\x02\0\0\0\0\0\0\0") - }); - - let mut v: &[u8] = b"\x01\0\0\0\x02\0\0\0\0\0\0\0"; - assert_eq!(Indexed::decode(&mut v), Some(Indexed(1, 2))); -} diff --git a/substrate/codec/src/codec.rs b/substrate/codec/src/codec.rs deleted file mode 100644 index c92790f097a88..0000000000000 --- a/substrate/codec/src/codec.rs +++ /dev/null @@ -1,540 +0,0 @@ -// Copyright 2017 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 . - -//! Serialisation. - -use alloc::vec::Vec; -use alloc::boxed::Box; -use core::{mem, slice}; -use arrayvec::ArrayVec; - -/// Trait that allows reading of data into a slice. -pub trait Input { - /// Read into the provided input slice. Returns the number of bytes read. - fn read(&mut self, into: &mut [u8]) -> usize; - - /// Read a single byte from the input. - fn read_byte(&mut self) -> Option { - let mut buf = [0u8]; - match self.read(&mut buf[..]) { - 0 => None, - 1 => Some(buf[0]), - _ => unreachable!(), - } - } -} - -#[cfg(not(feature = "std"))] -impl<'a> Input for &'a [u8] { - fn read(&mut self, into: &mut [u8]) -> usize { - let len = ::core::cmp::min(into.len(), self.len()); - into[..len].copy_from_slice(&self[..len]); - *self = &self[len..]; - len - } -} - -#[cfg(feature = "std")] -impl Input for R { - fn read(&mut self, into: &mut [u8]) -> usize { - match (self as &mut ::std::io::Read).read_exact(into) { - Ok(()) => into.len(), - Err(_) => 0, - } - } -} - -/// Trait that allows writing of data. -pub trait Output: Sized { - /// Write to the output. - fn write(&mut self, bytes: &[u8]); - - fn push_byte(&mut self, byte: u8) { - self.write(&[byte]); - } - - fn push(&mut self, value: &V) { - value.encode_to(self); - } -} - -#[cfg(not(feature = "std"))] -impl Output for Vec { - fn write(&mut self, bytes: &[u8]) { - self.extend(bytes); - } -} - -#[cfg(feature = "std")] -impl Output for W { - fn write(&mut self, bytes: &[u8]) { - (self as &mut ::std::io::Write).write_all(bytes).expect("Codec outputs are infallible"); - } -} - -/// Trait that allows zero-copy write of value-references to slices in LE format. -/// Implementations should override `using_encoded` for value types and `encode_to` for allocating types. -pub trait Encode { - /// Convert self to a slice and append it to the destination. - fn encode_to(&self, dest: &mut T) { - self.using_encoded(|buf| dest.write(buf)); - } - - /// Convert self to an owned vector. - fn encode(&self) -> Vec { - let mut r = Vec::new(); - self.encode_to(&mut r); - r - } - - /// Convert self to a slice and then invoke the given closure with it. - fn using_encoded R>(&self, f: F) -> R { - f(&self.encode()) - } -} - -/// Trait that allows zero-copy read of value-references from slices in LE format. -pub trait Decode: Sized { - /// Attempt to deserialise the value from input. - fn decode(value: &mut I) -> Option; -} - -/// Trait that allows zero-copy read/write of value-references to/from slices in LE format. -pub trait Codec: Decode + Encode {} - -impl Codec for S {} - -impl Encode for Result { - fn encode_to(&self, dest: &mut W) { - match *self { - Ok(ref t) => { - dest.push_byte(0); - t.encode_to(dest); - } - Err(ref e) => { - dest.push_byte(1); - e.encode_to(dest); - } - } - } -} - -impl Decode for Result { - fn decode(input: &mut I) -> Option { - match input.read_byte()? { - 0 => Some(Ok(T::decode(input)?)), - 1 => Some(Err(E::decode(input)?)), - _ => None, - } - } -} - -/// Shim type because we can't do a specialised implementation for `Option` directly. -pub struct OptionBool(pub Option); - -impl Encode for OptionBool { - fn using_encoded R>(&self, f: F) -> R { - f(&[match *self { - OptionBool(None) => 0u8, - OptionBool(Some(true)) => 1u8, - OptionBool(Some(false)) => 2u8, - }]) - } -} - -impl Decode for OptionBool { - fn decode(input: &mut I) -> Option { - match input.read_byte()? { - 0 => Some(OptionBool(None)), - 1 => Some(OptionBool(Some(true))), - 2 => Some(OptionBool(Some(false))), - _ => None, - } - } -} - -impl Encode for Option { - fn encode_to(&self, dest: &mut W) { - match *self { - Some(ref t) => { - dest.push_byte(1); - t.encode_to(dest); - } - None => dest.push_byte(0), - } - } -} - -impl Decode for Option { - fn decode(input: &mut I) -> Option { - match input.read_byte()? { - 0 => Some(None), - 1 => Some(Some(T::decode(input)?)), - _ => None, - } - } -} - -macro_rules! impl_array { - ( $( $n:expr )* ) => { $( - impl Encode for [T; $n] { - fn encode_to(&self, dest: &mut W) { - for item in self.iter() { - item.encode_to(dest); - } - } - } - - impl Decode for [T; $n] { - fn decode(input: &mut I) -> Option { - let mut r = ArrayVec::new(); - for _ in 0..$n { - r.push(T::decode(input)?); - } - r.into_inner().ok() - } - } - )* } -} - -impl_array!(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 - 40 48 56 64 72 96 128 160 192 224 256); - -impl Encode for Box { - fn encode_to(&self, dest: &mut W) { - self.as_ref().encode_to(dest) - } -} - -impl Decode for Box { - fn decode(input: &mut I) -> Option { - Some(Box::new(T::decode(input)?)) - } -} - -impl Encode for [u8] { - fn encode_to(&self, dest: &mut W) { - let len = self.len(); - assert!(len <= u32::max_value() as usize, "Attempted to serialize a collection with too many elements."); - (len as u32).encode_to(dest); - dest.write(self) - } -} - -impl Encode for Vec { - fn encode_to(&self, dest: &mut W) { - self.as_slice().encode_to(dest) - } -} - -impl Decode for Vec { - fn decode(input: &mut I) -> Option { - u32::decode(input).and_then(move |len| { - let len = len as usize; - let mut vec = vec![0; len]; - if input.read(&mut vec[..len]) != len { - None - } else { - Some(vec) - } - }) - } -} - -impl<'a> Encode for &'a str { - fn encode_to(&self, dest: &mut W) { - self.as_bytes().encode_to(dest) - } -} - -#[cfg(feature = "std")] -impl<'a > Encode for ::std::borrow::Cow<'a, str> { - fn encode_to(&self, dest: &mut W) { - self.as_bytes().encode_to(dest) - } -} - -#[cfg(feature = "std")] -impl<'a> Decode for ::std::borrow::Cow<'a, str> { - fn decode(input: &mut I) -> Option { - Some(::std::borrow::Cow::Owned(String::from_utf8_lossy(&Vec::decode(input)?).into())) - } -} - -#[cfg(feature = "std")] -impl Encode for String { - fn encode_to(&self, dest: &mut W) { - self.as_bytes().encode_to(dest) - } -} - -#[cfg(feature = "std")] -impl Decode for String { - fn decode(input: &mut I) -> Option { - Some(Self::from_utf8_lossy(&Vec::decode(input)?).into()) - } -} - -impl Encode for [T] { - fn encode_to(&self, dest: &mut W) { - let len = self.len(); - assert!(len <= u32::max_value() as usize, "Attempted to serialize a collection with too many elements."); - (len as u32).encode_to(dest); - for item in self { - item.encode_to(dest); - } - } -} - -impl Encode for Vec { - fn encode_to(&self, dest: &mut W) { - self.as_slice().encode_to(dest) - } -} - -impl Decode for Vec { - fn decode(input: &mut I) -> Option { - u32::decode(input).and_then(move |len| { - let mut r = Vec::with_capacity(len as usize); - for _ in 0..len { - r.push(T::decode(input)?); - } - Some(r) - }) - } -} - -impl Encode for () { - fn encode_to(&self, _dest: &mut T) { - } - - fn using_encoded R>(&self, f: F) -> R { - f(&[]) - } - - fn encode(&self) -> Vec { - Vec::new() - } -} - -impl<'a, T: 'a + Encode + ?Sized> Encode for &'a T { - fn encode_to(&self, dest: &mut D) { - (&**self).encode_to(dest) - } - - fn using_encoded R>(&self, f: F) -> R { - (&**self).using_encoded(f) - } - - fn encode(&self) -> Vec { - (&**self).encode() - } -} - -impl Decode for () { - fn decode(_: &mut I) -> Option<()> { - Some(()) - } -} - -macro_rules! tuple_impl { - ($one:ident,) => { - impl<$one: Encode> Encode for ($one,) { - fn encode_to(&self, dest: &mut T) { - self.0.encode_to(dest); - } - } - - impl<$one: Decode> Decode for ($one,) { - fn decode(input: &mut I) -> Option { - match $one::decode(input) { - None => None, - Some($one) => Some(($one,)), - } - } - } - }; - ($first:ident, $($rest:ident,)+) => { - impl<$first: Encode, $($rest: Encode),+> - Encode for - ($first, $($rest),+) { - fn encode_to(&self, dest: &mut T) { - let ( - ref $first, - $(ref $rest),+ - ) = *self; - - $first.encode_to(dest); - $($rest.encode_to(dest);)+ - } - } - - impl<$first: Decode, $($rest: Decode),+> - Decode for - ($first, $($rest),+) { - fn decode(input: &mut INPUT) -> Option { - Some(( - match $first::decode(input) { - Some(x) => x, - None => return None, - }, - $(match $rest::decode(input) { - Some(x) => x, - None => return None, - },)+ - )) - } - } - - tuple_impl!($($rest,)+); - } -} - -#[allow(non_snake_case)] -mod inner_tuple_impl { - use super::{Input, Output, Decode, Encode}; - tuple_impl!(A, B, C, D, E, F, G, H, I, J, K,); -} - -/// Trait to allow conversion to a know endian representation when sensitive. -/// Types implementing this trait must have a size > 0. -// note: the copy bound and static lifetimes are necessary for safety of `Codec` blanket -// implementation. -trait EndianSensitive: Copy + 'static { - fn to_le(self) -> Self { self } - fn to_be(self) -> Self { self } - fn from_le(self) -> Self { self } - fn from_be(self) -> Self { self } - fn as_be_then T>(&self, f: F) -> T { f(&self) } - fn as_le_then T>(&self, f: F) -> T { f(&self) } -} - -macro_rules! impl_endians { - ( $( $t:ty ),* ) => { $( - impl EndianSensitive for $t { - fn to_le(self) -> Self { <$t>::to_le(self) } - fn to_be(self) -> Self { <$t>::to_be(self) } - fn from_le(self) -> Self { <$t>::from_le(self) } - fn from_be(self) -> Self { <$t>::from_be(self) } - fn as_be_then T>(&self, f: F) -> T { let d = self.to_be(); f(&d) } - fn as_le_then T>(&self, f: F) -> T { let d = self.to_le(); f(&d) } - } - - impl Encode for $t { - fn using_encoded R>(&self, f: F) -> R { - self.as_le_then(|le| { - let size = mem::size_of::<$t>(); - let value_slice = unsafe { - let ptr = le as *const _ as *const u8; - if size != 0 { - slice::from_raw_parts(ptr, size) - } else { - &[] - } - }; - - f(value_slice) - }) - } - } - - impl Decode for $t { - fn decode(input: &mut I) -> Option { - let size = mem::size_of::<$t>(); - assert!(size > 0, "EndianSensitive can never be implemented for a zero-sized type."); - let mut val: $t = unsafe { mem::zeroed() }; - - unsafe { - let raw: &mut [u8] = slice::from_raw_parts_mut( - &mut val as *mut $t as *mut u8, - size - ); - if input.read(raw) != size { return None } - } - Some(val.from_le()) - } - } - )* } -} -macro_rules! impl_non_endians { - ( $( $t:ty ),* ) => { $( - impl EndianSensitive for $t {} - - impl Encode for $t { - fn using_encoded R>(&self, f: F) -> R { - self.as_le_then(|le| { - let size = mem::size_of::<$t>(); - let value_slice = unsafe { - let ptr = le as *const _ as *const u8; - if size != 0 { - slice::from_raw_parts(ptr, size) - } else { - &[] - } - }; - - f(value_slice) - }) - } - } - - impl Decode for $t { - fn decode(input: &mut I) -> Option { - let size = mem::size_of::<$t>(); - assert!(size > 0, "EndianSensitive can never be implemented for a zero-sized type."); - let mut val: $t = unsafe { mem::zeroed() }; - - unsafe { - let raw: &mut [u8] = slice::from_raw_parts_mut( - &mut val as *mut $t as *mut u8, - size - ); - if input.read(raw) != size { return None } - } - Some(val.from_le()) - } - } - )* } -} - -impl_endians!(u16, u32, u64, u128, usize, i16, i32, i64, i128, isize); -impl_non_endians!(i8, [u8; 1], [u8; 2], [u8; 3], [u8; 4], [u8; 5], [u8; 6], [u8; 7], [u8; 8], - [u8; 10], [u8; 12], [u8; 14], [u8; 16], [u8; 20], [u8; 24], [u8; 28], [u8; 32], [u8; 40], - [u8; 48], [u8; 56], [u8; 64], [u8; 80], [u8; 96], [u8; 112], [u8; 128], bool); - - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn vec_is_slicable() { - let v = b"Hello world".to_vec(); - v.using_encoded(|ref slice| - assert_eq!(slice, &b"\x0b\0\0\0Hello world") - ); - } - - #[test] - fn encode_borrowed_tuple() { - let x = vec![1u8, 2, 3, 4]; - let y = 128i64; - - let encoded = (&x, &y).encode(); - - assert_eq!((x, y), Decode::decode(&mut &encoded[..]).unwrap()); - } -} diff --git a/substrate/codec/src/joiner.rs b/substrate/codec/src/joiner.rs deleted file mode 100644 index 81105b060ce63..0000000000000 --- a/substrate/codec/src/joiner.rs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2017 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 . - -//! Trait - -use core::iter::Extend; -use super::Codec; - -/// Trait to allow itself to be serialised into a value which can be extended -/// by bytes. -pub trait Joiner { - fn and(self, value: &V) -> Self; -} - -impl Joiner for T where T: for<'a> Extend<&'a u8> { - fn and(mut self, value: &V) -> Self { - value.using_encoded(|s| self.extend(s)); - self - } -} diff --git a/substrate/codec/src/keyedvec.rs b/substrate/codec/src/keyedvec.rs deleted file mode 100644 index 37298674a21af..0000000000000 --- a/substrate/codec/src/keyedvec.rs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2017 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 . - -//! Serialiser and prepender. - -use Codec; -use core::iter::Extend; -use alloc::vec::Vec; - -/// Trait to allow itself to be serialised and prepended by a given slice. -pub trait KeyedVec { - fn to_keyed_vec(&self, prepend_key: &[u8]) -> Vec; -} - -impl KeyedVec for T { - fn to_keyed_vec(&self, prepend_key: &[u8]) -> Vec { - self.using_encoded(|slice| { - let mut r = prepend_key.to_vec(); - r.extend(slice); - r - }) - } -} diff --git a/substrate/codec/src/lib.rs b/substrate/codec/src/lib.rs deleted file mode 100644 index c1c29ccaa0abc..0000000000000 --- a/substrate/codec/src/lib.rs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -// tag::description[] -//! Implements a serialization and deserialization codec for simple marshalling. -// end::description[] - -#![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(not(feature = "std"), feature(alloc))] - -#[cfg(not(feature = "std"))] -#[macro_use] -extern crate alloc; - -#[cfg(feature = "std")] -extern crate core; - -extern crate arrayvec; - -#[cfg(feature = "std")] -pub mod alloc { - pub use std::boxed; - pub use std::vec; -} - -mod codec; -mod joiner; -mod keyedvec; - -pub use self::codec::{Input, Output, Encode, Decode, Codec}; -pub use self::joiner::Joiner; -pub use self::keyedvec::KeyedVec; diff --git a/substrate/ed25519/Cargo.toml b/substrate/ed25519/Cargo.toml deleted file mode 100644 index 34494fc8be9b0..0000000000000 --- a/substrate/ed25519/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "ed25519" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -ring = "0.12" -untrusted = "0.5" -substrate-primitives = { version = "0.1", path = "../primitives" } -hex-literal = "0.1" -base58 = "0.1" -blake2-rfc = "0.2" diff --git a/substrate/ed25519/README.adoc b/substrate/ed25519/README.adoc deleted file mode 100644 index 97c10769ee902..0000000000000 --- a/substrate/ed25519/README.adoc +++ /dev/null @@ -1,13 +0,0 @@ - -= ED25519 - -.Summary -[source, toml] ----- -include::Cargo.toml[lines=2..5] ----- - -.Description ----- -include::src/lib.rs[tag=description] ----- diff --git a/substrate/ed25519/src/lib.rs b/substrate/ed25519/src/lib.rs deleted file mode 100644 index a7f419f47842c..0000000000000 --- a/substrate/ed25519/src/lib.rs +++ /dev/null @@ -1,354 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -// tag::description[] -//! Simple Ed25519 API. -// end::description[] - -extern crate ring; -extern crate base58; -extern crate substrate_primitives as primitives; -extern crate untrusted; -extern crate blake2_rfc; - -use ring::{rand, signature}; -use primitives::{hash::H512, AuthorityId}; -use base58::{ToBase58, FromBase58}; - -#[cfg(test)] -#[macro_use] -extern crate hex_literal; - -/// Alias to 512-bit hash when used in the context of a signature on the relay chain. -pub type Signature = H512; - -/// Length of the PKCS#8 encoding of the key. -pub const PKCS_LEN: usize = 85; - -/// A localized signature also contains sender information. -#[derive(PartialEq, Eq, Clone, Debug)] -pub struct LocalizedSignature { - /// The signer of the signature. - pub signer: Public, - /// The signature itself. - pub signature: Signature, -} - -/// Verify a message without type checking the parameters' types for the right size. -pub fn verify>(sig: &[u8], message: &[u8], public: P) -> bool { - let public_key = untrusted::Input::from(public.as_ref()); - let msg = untrusted::Input::from(message); - let sig = untrusted::Input::from(sig); - - match signature::verify(&signature::ED25519, public_key, msg, sig) { - Ok(_) => true, - _ => false, - } -} - -/// A public key. -#[derive(PartialEq, Eq, Clone)] -pub struct Public(pub [u8; 32]); - -/// A key pair. -pub struct Pair(signature::Ed25519KeyPair); - -impl ::std::hash::Hash for Public { - fn hash(&self, state: &mut H) { - self.0.hash(state); - } -} - -#[derive(Clone, Copy, Eq, PartialEq, Debug)] -pub enum PublicError { - BadBase58, - BadLength, - UnknownVersion, - InvalidChecksum, -} - -impl Public { - /// A new instance from the given 32-byte `data`. - pub fn from_raw(data: [u8; 32]) -> Self { - Public(data) - } - - /// A new instance from the given slice that should be 32 bytes long. - pub fn from_slice(data: &[u8]) -> Self { - let mut r = [0u8; 32]; - r.copy_from_slice(data); - Public(r) - } - - /// Some if the string is a properly encoded SS58Check address. - pub fn from_ss58check(s: &str) -> Result { - let d = s.from_base58().map_err(|_| PublicError::BadBase58)?; // failure here would be invalid encoding. - if d.len() != 35 { - // Invalid length. - return Err(PublicError::BadLength); - } - if d[0] != 42 { - // Invalid version. - return Err(PublicError::UnknownVersion); - } - if d[33..35] != blake2_rfc::blake2b::blake2b(64, &[], &d[0..33]).as_bytes()[0..2] { - // Invalid checksum. - return Err(PublicError::InvalidChecksum); - } - Ok(Self::from_slice(&d[1..33])) - } - - /// Return a `Vec` filled with raw data. - pub 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] { - 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() - } - - /// Return the ss58-check string for this key. - pub fn to_ss58check(&self) -> String { - let mut v = vec![42u8]; - v.extend(self.as_slice()); - let r = blake2_rfc::blake2b::blake2b(64, &[], &v); - v.extend(&r.as_bytes()[0..2]); - v.to_base58() - } -} - -impl AsRef<[u8; 32]> for Public { - fn as_ref(&self) -> &[u8; 32] { - &self.0 - } -} - -impl AsRef<[u8]> for Public { - fn as_ref(&self) -> &[u8] { - &self.0[..] - } -} - -impl Into<[u8; 32]> for Public { - fn into(self) -> [u8; 32] { - self.0 - } -} - -impl AsRef for Public { - fn as_ref(&self) -> &Public { - &self - } -} - -impl AsRef for Pair { - fn as_ref(&self) -> &Pair { - &self - } -} - -impl Into for Public { - fn into(self) -> AuthorityId { - AuthorityId(self.0) - } -} - -impl From for Public { - fn from(id: AuthorityId) -> Self { - Public(id.0) - } -} - -impl ::std::fmt::Display for Public { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - write!(f, "{}", self.to_ss58check()) - } -} - -impl ::std::fmt::Debug for Public { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - let s = self.to_ss58check(); - write!(f, "{} ({}...)", ::primitives::hexdisplay::HexDisplay::from(&self.0), &s[0..8]) - } -} - -impl Pair { - /// Generate new secure (random) key pair, yielding it and the corresponding pkcs#8 bytes. - pub fn generate_with_pkcs8() -> (Self, [u8; PKCS_LEN]) { - let rng = rand::SystemRandom::new(); - let pkcs8_bytes = signature::Ed25519KeyPair::generate_pkcs8(&rng).expect("system randomness is available; qed"); - let pair = Self::from_pkcs8(&pkcs8_bytes).expect("just-generated pkcs#8 data is valid; qed"); - - (pair, pkcs8_bytes) - } - - /// Generate new secure (random) key pair. - pub fn generate() -> Pair { - let (pair, _) = Self::generate_with_pkcs8(); - pair - } - - /// Generate from pkcs#8 bytes. - pub fn from_pkcs8(pkcs8_bytes: &[u8]) -> Result { - signature::Ed25519KeyPair::from_pkcs8(untrusted::Input::from(&pkcs8_bytes)).map(Pair) - } - - /// Make a new key pair from a seed phrase. - /// NOTE: prefer pkcs#8 unless security doesn't matter -- this is used primarily for tests. - pub fn from_seed(seed: &[u8; 32]) -> Pair { - let key = signature::Ed25519KeyPair::from_seed_unchecked(untrusted::Input::from(&seed[..])) - .expect("seed has valid length; qed"); - - Pair(key) - } - - /// Sign a message. - pub fn sign(&self, message: &[u8]) -> Signature { - let mut r = [0u8; 64]; - r.copy_from_slice(self.0.sign(message).as_ref()); - Signature::from(r) - } - - /// Get the public key. - pub fn public(&self) -> Public { - let mut r = [0u8; 32]; - let pk = self.0.public_key_bytes(); - r.copy_from_slice(pk); - Public(r) - } - - /// Derive a child key. Probably unsafe and broken. - // TODO: proper HD derivation https://cardanolaunch.com/assets/Ed25519_BIP.pdf - pub fn derive_child_probably_bad(&self, chain_data: &[u8]) -> Pair { - let sig = self.sign(chain_data); - let mut seed = [0u8; 32]; - seed.copy_from_slice(&sig.0[..32]); - - Pair::from_seed(&seed) - } -} - -/// Verify a signature on a message. -pub fn verify_strong>(sig: &Signature, message: &[u8], pubkey: P) -> bool { - let public_key = untrusted::Input::from(&pubkey.as_ref().0[..]); - let msg = untrusted::Input::from(message); - let sig = untrusted::Input::from(&sig.0[..]); - - match signature::verify(&signature::ED25519, public_key, msg, sig) { - Ok(_) => true, - _ => false, - } -} - -pub trait Verifiable { - /// Verify something that acts like a signature. - fn verify>(&self, message: &[u8], pubkey: P) -> bool; -} - -impl Verifiable for Signature { - /// Verify something that acts like a signature. - fn verify>(&self, message: &[u8], pubkey: P) -> bool { - verify_strong(&self, message, pubkey) - } -} - -impl Verifiable for LocalizedSignature { - fn verify>(&self, message: &[u8], pubkey: P) -> bool { - pubkey.as_ref() == &self.signer && self.signature.verify(message, pubkey) - } -} - -#[cfg(test)] -mod test { - use super::*; - - fn _test_primitives_signature_and_local_the_same() { - fn takes_two(_: T, _: T) { } - takes_two(Signature::default(), primitives::Signature::default()) - } - - #[test] - fn test_vector_should_work() { - let pair: Pair = Pair::from_seed(&hex!("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60")); - let public = pair.public(); - assert_eq!(public, Public::from_raw(hex!("d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a"))); - let message = b""; - let signature: Signature = hex!("e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b").into(); - assert!(&pair.sign(&message[..]) == &signature); - assert!(verify_strong(&signature, &message[..], &public)); - } - - #[test] - fn generated_pair_should_work() { - let pair = Pair::generate(); - let public = pair.public(); - let message = b"Something important"; - let signature = pair.sign(&message[..]); - assert!(verify_strong(&signature, &message[..], &public)); - } - - #[test] - fn seeded_pair_should_work() { - use primitives::hexdisplay::HexDisplay; - - let pair = Pair::from_seed(b"12345678901234567890123456789012"); - let public = pair.public(); - assert_eq!(public, Public::from_raw(hex!("2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee"))); - let message = hex!("2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee00000000000000000200d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a4500000000000000"); - let signature = pair.sign(&message[..]); - println!("Correct signature: {}", HexDisplay::from(&signature.0)); - assert!(verify_strong(&signature, &message[..], &public)); - } - - #[test] - fn generate_with_pkcs8_recovery_possible() { - let (pair1, pkcs8) = Pair::generate_with_pkcs8(); - let pair2 = Pair::from_pkcs8(&pkcs8).unwrap(); - - assert_eq!(pair1.public(), pair2.public()); - } - - #[test] - fn derive_child() { - let pair = Pair::generate(); - let _pair2 = pair.derive_child_probably_bad(b"session_1234"); - } - - #[test] - fn ss58check_roundtrip_works() { - let pair = Pair::from_seed(b"12345678901234567890123456789012"); - let public = pair.public(); - let s = public.to_ss58check(); - println!("Correct: {}", s); - let cmp = Public::from_ss58check(&s).unwrap(); - assert_eq!(cmp, public); - } - - #[test] - fn ss58check_known_works() { - let k = "5CGavy93sZgPPjHyziRohwVumxiHXMGmQLyuqQP4ZFx5vRU9"; - let enc = hex!["090fa15cb5b1666222fff584b4cc2b1761fe1e238346b340491b37e25ea183ff"]; - assert_eq!(Public::from_ss58check(k).unwrap(), Public::from_raw(enc)); - } -} diff --git a/substrate/environmental/Cargo.toml b/substrate/environmental/Cargo.toml deleted file mode 100644 index 971dd38b676b5..0000000000000 --- a/substrate/environmental/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "environmental" -version = "0.1.0" -authors = ["Parity Technologies "] - -[features] -default = ["std"] -std = [] diff --git a/substrate/environmental/README.adoc b/substrate/environmental/README.adoc deleted file mode 100644 index b12a0f9f5fad6..0000000000000 --- a/substrate/environmental/README.adoc +++ /dev/null @@ -1,13 +0,0 @@ - -= Environmental - -.Summary -[source, toml] ----- -include::Cargo.toml[lines=2..5] ----- - -.Description ----- -include::src/lib.rs[tag=description] ----- diff --git a/substrate/environmental/src/lib.rs b/substrate/environmental/src/lib.rs deleted file mode 100644 index ea7bbfa4f0ac3..0000000000000 --- a/substrate/environmental/src/lib.rs +++ /dev/null @@ -1,383 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -// tag::description[] -//! Safe global references to stack variables. -//! -//! Set up a global reference with environmental! macro giving it a name and type. -//! Use the `using` function scoped under its name to name a reference and call a function that -//! takes no parameters yet can access said reference through the similarly placed `with` function. -//! -//! # Examples -//! -//! ``` -//! #[macro_use] extern crate environmental; -//! // create a place for the global reference to exist. -//! environmental!(counter: u32); -//! fn stuff() { -//! // do some stuff, accessing the named reference as desired. -//! counter::with(|i| *i += 1); -//! } -//! fn main() { -//! // declare a stack variable of the same type as our global declaration. -//! let mut counter_value = 41u32; -//! // call stuff, setting up our `counter` environment as a reference to our counter_value var. -//! counter::using(&mut counter_value, stuff); -//! println!("The answer is {:?}", counter_value); // will print 42! -//! stuff(); // safe! doesn't do anything. -//! } -//! ``` -// end::description[] - -#![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(not(feature = "std"), feature(const_fn))] - -#[cfg(feature = "std")] -include!("../with_std.rs"); - -#[cfg(not(feature = "std"))] -include!("../without_std.rs"); - -#[doc(hidden)] -pub fn using R>( - global: &'static imp::LocalKey>>, - protected: &mut T, - f: F -) -> R { - // store the `protected` reference as a pointer so we can provide it to logic running within - // `f`. - // while we record this pointer (while it's non-zero) we guarantee: - // - it will only be used once at any time (no reentrancy); - // - that no other thread will use it; and - // - that we do not use the original mutating reference while the pointer. - // exists. - global.with(|r| { - let original = { - let mut global = r.borrow_mut(); - imp::replace(&mut *global, Some(protected as _)) - }; - - // even if `f` panics the original will be replaced. - struct ReplaceOriginal<'a, T: 'a + ?Sized> { - original: Option<*mut T>, - global: &'a imp::RefCell>, - } - - impl<'a, T: 'a + ?Sized> Drop for ReplaceOriginal<'a, T> { - fn drop(&mut self) { - *self.global.borrow_mut() = self.original.take(); - } - } - - let _guard = ReplaceOriginal { - original, - global: r, - }; - - f() - }) -} - -#[doc(hidden)] -pub fn with R>( - global: &'static imp::LocalKey>>, - mutator: F, -) -> Option { - global.with(|r| unsafe { - let ptr = r.borrow_mut(); - match *ptr { - Some(ptr) => { - // safe because it's only non-zero when it's being called from using, which - // is holding on to the underlying reference (and not using it itself) safely. - Some(mutator(&mut *ptr)) - } - None => None, - } - }) -} - -/// Declare a new global reference module whose underlying value does not contain references. -/// -/// Will create a module of a given name that contains two functions: -/// -/// * `pub fn using R>(protected: &mut $t, f: F) -> R` -/// This executes `f`, returning its value. During the call, the module's reference is set to -/// be equal to `protected`. -/// * `pub fn with R>(f: F) -> Option` -/// This executes `f`, returning `Some` of its value if called from code that is being executed -/// as part of a `using` call. If not, it returns `None`. `f` is provided with one argument: the -/// same reference as provided to the most recent `using` call. -/// -/// # Examples -/// -/// Initializing the global context with a given value. -/// -/// ```rust -/// #[macro_use] extern crate environmental; -/// environmental!(counter: u32); -/// fn main() { -/// let mut counter_value = 41u32; -/// counter::using(&mut counter_value, || { -/// let odd = counter::with(|value| -/// if *value % 2 == 1 { -/// *value += 1; true -/// } else { -/// *value -= 3; false -/// }).unwrap(); // safe because we're inside a counter::using -/// println!("counter was {}", match odd { true => "odd", _ => "even" }); -/// }); -/// -/// println!("The answer is {:?}", counter_value); // 42 -/// } -/// ``` -/// -/// Roughly the same, but with a trait object: -/// -/// ```rust -/// #[macro_use] extern crate environmental; -/// -/// trait Increment { fn increment(&mut self); } -/// -/// impl Increment for i32 { -/// fn increment(&mut self) { *self += 1 } -/// } -/// -/// environmental!(val: Increment + 'static); -/// -/// fn main() { -/// let mut local = 0i32; -/// val::using(&mut local, || { -/// val::with(|v| for _ in 0..5 { v.increment() }); -/// }); -/// -/// assert_eq!(local, 5); -/// } -/// ``` -#[macro_export] -macro_rules! environmental { - ($name:ident : $t:ty) => { - #[allow(non_camel_case_types)] - struct $name { __private_field: () } - - thread_local_impl!(static GLOBAL: ::std::cell::RefCell> - = ::std::cell::RefCell::new(None)); - - impl $name { - #[allow(unused_imports)] - - pub fn using R>( - protected: &mut $t, - f: F - ) -> R { - $crate::using(&GLOBAL, protected, f) - } - - pub fn with R>( - f: F - ) -> Option { - $crate::with(&GLOBAL, |x| f(x)) - } - } - }; - ($name:ident : trait @$t:ident [$($args:ty,)*]) => { - #[allow(non_camel_case_types, dead_code)] - struct $name { __private_field: () } - - thread_local_impl!(static GLOBAL: $crate::imp::RefCell + 'static)>> - = $crate::imp::RefCell::new(None)); - - impl $name { - #[allow(unused_imports)] - - pub fn using R>( - protected: &mut $t<$($args),*>, - f: F - ) -> R { - let lifetime_extended = unsafe { - $crate::imp::transmute::<&mut $t<$($args),*>, &mut ($t<$($args),*> + 'static)>(protected) - }; - $crate::using(&GLOBAL, lifetime_extended, f) - } - - pub fn with FnOnce(&'a mut ($t<$($args),*> + 'a)) -> R>( - f: F - ) -> Option { - $crate::with(&GLOBAL, |x| f(x)) - } - } - }; - ($name:ident<$traittype:ident> : trait $t:ident <$concretetype:ty>) => { - #[allow(non_camel_case_types, dead_code)] - struct $name { _private_field: $crate::imp::PhantomData } - - thread_local_impl!(static GLOBAL: $crate::imp::RefCell + 'static)>> - = $crate::imp::RefCell::new(None)); - - impl $name { - #[allow(unused_imports)] - pub fn using R>( - protected: &mut $t, - f: F - ) -> R { - let lifetime_extended = unsafe { - $crate::imp::transmute::<&mut $t, &mut ($t<$concretetype> + 'static)>(protected) - }; - $crate::using(&GLOBAL, lifetime_extended, f) - } - - pub fn with FnOnce(&'a mut ($t<$concretetype> + 'a)) -> R>( - f: F - ) -> Option { - $crate::with(&GLOBAL, |x| f(x)) - } - } - }; - ($name:ident : trait $t:ident <>) => { environmental! { $name : trait @$t [] } }; - ($name:ident : trait $t:ident < $($args:ty),* $(,)* >) => { environmental! { $name : trait @$t [$($args,)*] } }; - ($name:ident : trait $t:ident) => { environmental! { $name : trait @$t [] } }; -} - -#[cfg(test)] -mod tests { - - #[test] - fn simple_works() { - environmental!(counter: u32); - - fn stuff() { counter::with(|value| *value += 1); }; - - // declare a stack variable of the same type as our global declaration. - let mut local = 41u32; - - // call stuff, setting up our `counter` environment as a reference to our local counter var. - counter::using(&mut local, stuff); - assert_eq!(local, 42); - stuff(); // safe! doesn't do anything. - assert_eq!(local, 42); - } - - #[test] - fn overwrite_with_lesser_lifetime() { - environmental!(items: Vec); - - let mut local_items = vec![1, 2, 3]; - items::using(&mut local_items, || { - let dies_at_end = vec![4, 5, 6]; - items::with(|items| *items = dies_at_end); - }); - - assert_eq!(local_items, vec![4, 5, 6]); - } - - #[test] - fn declare_with_trait_object() { - trait Foo { - fn get(&self) -> i32; - fn set(&mut self, x: i32); - } - - impl Foo for i32 { - fn get(&self) -> i32 { *self } - fn set(&mut self, x: i32) { *self = x } - } - - environmental!(foo: Foo + 'static); - - fn stuff() { - foo::with(|value| { - let new_val = value.get() + 1; - value.set(new_val); - }); - } - - let mut local = 41i32; - foo::using(&mut local, stuff); - - assert_eq!(local, 42); - - stuff(); // doesn't do anything. - - assert_eq!(local, 42); - } - - #[test] - fn unwind_recursive() { - use std::panic; - - environmental!(items: Vec); - - let panicked = panic::catch_unwind(|| { - let mut local_outer = vec![1, 2, 3]; - - items::using(&mut local_outer, || { - let mut local_inner = vec![4, 5, 6]; - items::using(&mut local_inner, || { - panic!("are you unsafe?"); - }) - }); - }).is_err(); - - assert!(panicked); - - let mut was_cleared = true; - items::with(|_items| was_cleared = false); - - assert!(was_cleared); - } - - #[test] - fn use_non_static_trait() { - trait Sum { fn sum(&self) -> usize; } - impl<'a> Sum for &'a [usize] { - fn sum(&self) -> usize { - self.iter().fold(0, |a, c| a + c) - } - } - - environmental!(sum: trait Sum); - let numbers = vec![1, 2, 3, 4, 5]; - let mut numbers = &numbers[..]; - let got_sum = sum::using(&mut numbers, || { - sum::with(|x| x.sum()) - }).unwrap(); - - assert_eq!(got_sum, 15); - } - - #[test] - fn use_generic_trait() { - trait Plus { fn plus42() -> usize; } - struct ConcretePlus; - impl Plus for ConcretePlus { - fn plus42() -> usize { 42 } - } - trait Multiplier { fn mul_and_add(&self) -> usize; } - impl<'a, P: Plus> Multiplier

(path: P) - -> Result - where P: AsRef - { - fs::File::open(path) - .and_then(|mut file| { - // We are in 2018 and there is still no method on `std::io::Read` - // that directly returns a `Vec`. - let mut buf = Vec::new(); - file.read_to_end(&mut buf).map(|_| buf) - }) - .and_then(|content| - secio::SecioKeyPair::secp256k1_raw_key(&content) - .map_err(|err| IoError::new(IoErrorKind::InvalidData, err)) - ) -} - -/// Generates a new secret key and tries to write it to the given file. -/// Doesn't error if we couldn't open or write to the file. -fn gen_key_and_try_write_to_file

(path: P) -> secio::SecioKeyPair - where P: AsRef { - let raw_key: [u8; 32] = rand::rngs::EntropyRng::new().gen(); - let secio_key = secio::SecioKeyPair::secp256k1_raw_key(&raw_key) - .expect("randomly-generated key with correct len should always be valid"); - - // And store the newly-generated key in the file if possible. - // Errors that happen while doing so are ignored. - match open_priv_key_file(&path) { - Ok(mut file) => - match file.write_all(&raw_key) { - Ok(()) => (), - Err(err) => warn!(target: "sub-libp2p", - "Failed to write secret key in file {:?} ; err = {:?}", - path.as_ref(), - err - ), - }, - Err(err) => warn!(target: "sub-libp2p", - "Failed to store secret key in file {:?} ; err = {:?}", - path.as_ref(), - err - ), - } - - secio_key -} - -/// Opens a file containing a private key in write mode. -#[cfg(unix)] -fn open_priv_key_file

(path: P) -> Result - where P: AsRef -{ - use std::os::unix::fs::OpenOptionsExt; - fs::OpenOptions::new() - .write(true) - .create_new(true) - .mode(256 | 128) // 0o600 in decimal - .open(path) -} -/// Opens a file containing a private key in write mode. -#[cfg(not(unix))] -fn open_priv_key_file

(path: P) -> Result - where P: AsRef -{ - fs::OpenOptions::new() - .write(true) - .create_new(true) - .open(path) -} - -#[cfg(test)] -mod tests { - use libp2p::core::PublicKey; - use network_state::NetworkState; - - #[test] - fn refuse_disabled_peer() { - let state = NetworkState::new(&Default::default()).unwrap(); - let example_peer = PublicKey::Rsa(vec![1, 2, 3, 4]).into_peer_id(); - - let who = state.assign_node_index(&example_peer).unwrap(); - state.ban_peer(who, "Just a test"); - - assert!(state.assign_node_index(&example_peer).is_err()); - } -} diff --git a/substrate/network-libp2p/src/service.rs b/substrate/network-libp2p/src/service.rs deleted file mode 100644 index 4e82973893240..0000000000000 --- a/substrate/network-libp2p/src/service.rs +++ /dev/null @@ -1,1437 +0,0 @@ -// Copyright 2018 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 bytes::Bytes; -use {Error, ErrorKind, NetworkConfiguration, NetworkProtocolHandler}; -use {NonReservedPeerMode, NetworkContext, Severity, NodeIndex, ProtocolId}; -use parking_lot::RwLock; -use libp2p; -use libp2p::multiaddr::{AddrComponent, Multiaddr}; -use libp2p::kad::{KadSystem, KadConnecConfig, KadSystemConfig}; -use libp2p::kad::{KadIncomingRequest, KadConnecController, KadPeer}; -use libp2p::kad::{KadConnectionType, KadQueryEvent}; -use libp2p::identify::{IdentifyInfo, IdentifyOutput, IdentifySender}; -use libp2p::identify::{IdentifyProtocolConfig}; -use libp2p::core::{upgrade, Transport, MuxedTransport, ConnectionUpgrade}; -use libp2p::core::{Endpoint, PeerId as PeerstorePeerId, PublicKey}; -use libp2p::core::{SwarmController, UniqueConnecState}; -use libp2p::ping; -use libp2p::transport_timeout::TransportTimeout; -use {PacketId, SessionInfo, TimerToken}; -use rand; -use std::io::{Error as IoError, ErrorKind as IoErrorKind}; -use std::iter; -use std::net::SocketAddr; -use std::sync::Arc; -use std::sync::mpsc as sync_mpsc; -use std::thread; -use std::time::{Duration, Instant}; -use futures::{future, Future, stream, Stream, select_all}; -use futures::sync::{mpsc, oneshot}; -use tokio::runtime::current_thread; -use tokio_io::{AsyncRead, AsyncWrite}; -use tokio_timer::{Interval, Timeout}; - -use custom_proto::{RegisteredProtocol, RegisteredProtocols}; -use custom_proto::RegisteredProtocolOutput; -use network_state::{NetworkState, PeriodicUpdate}; -use timeouts; -use transport; - -/// IO Service with networking. -pub struct NetworkService { - shared: Arc, - - /// Holds the networking-running background thread alive. The `Option` is - /// only set to `None` in the destructor. - /// Sending a message on the channel will trigger the end of the - /// background thread. We can then wait on the join handle. - bg_thread: Option<(oneshot::Sender<()>, thread::JoinHandle<()>)>, -} - -/// Common struct shared throughout all the components of the service. -struct Shared { - /// Original configuration of the service. - config: NetworkConfiguration, - - /// Contains the state of the network. - network_state: NetworkState, - - /// Kademlia system. Contains the DHT. - kad_system: KadSystem, - - /// Configuration for the Kademlia upgrade. - kad_upgrade: KadConnecConfig, - - /// List of protocols available on the network. It is a logic error to - /// remove protocols from this list, and the code may assume that protocols - /// stay at the same index forever. - protocols: RegisteredProtocols>, - - /// Use this channel to send a timeout request to the background thread's - /// events loop. After the timeout, elapsed, it will call `timeout` on the - /// `NetworkProtocolHandler`. This can be closed if the background thread - /// is not running. The sender will be overwritten every time we start - /// the service. - timeouts_register_tx: mpsc::UnboundedSender<(Duration, (Arc, ProtocolId, TimerToken))>, - - /// Original address from the configuration, after being adjusted by the `Transport`. - // TODO: because we create the `Shared` before starting to listen, this - // has to be set later ; sort this out - original_listened_addr: RwLock>, - - /// Contains the addresses we known about ourselves. - listened_addrs: RwLock>, -} - -impl NetworkService { - /// Starts the networking service. - /// - /// Note that we could use an iterator for `protocols`, but having a - /// generic here is too much and crashes the Rust compiler. - pub fn new( - config: NetworkConfiguration, - protocols: Vec<(Arc, ProtocolId, &[(u8, u8)])> - ) -> Result { - let network_state = NetworkState::new(&config)?; - - let local_peer_id = network_state.local_public_key().clone() - .into_peer_id(); - for mut addr in config.listen_addresses.iter().cloned() { - addr.append(AddrComponent::P2P(local_peer_id.clone().into())); - info!(target: "sub-libp2p", "Local node address is: {}", addr); - } - - let kad_system = KadSystem::without_init(KadSystemConfig { - parallelism: 3, - local_peer_id: local_peer_id.clone(), - kbuckets_timeout: Duration::from_secs(600), - request_timeout: Duration::from_secs(10), - known_initial_peers: iter::empty(), - }); - - // Channel we use to signal success or failure of the bg thread - // initialization process. - let (init_tx, init_rx) = sync_mpsc::channel(); - // Channel the main thread uses to signal the bg thread that it - // should stop - let (close_tx, close_rx) = oneshot::channel(); - let (timeouts_register_tx, timeouts_register_rx) = mpsc::unbounded(); - - let listened_addrs = config.public_addresses.clone(); - - let shared = Arc::new(Shared { - network_state, - protocols: RegisteredProtocols(protocols.into_iter() - .map(|(handler, protocol, versions)| - RegisteredProtocol::new(handler.clone(), protocol, versions)) - .collect() - ), - kad_system, - kad_upgrade: KadConnecConfig::new(), - config, - timeouts_register_tx, - original_listened_addr: RwLock::new(Vec::new()), - listened_addrs: RwLock::new(listened_addrs), - }); - - // Initialize all the protocols now. - // TODO: what about failure to initialize? we can't uninitialize a protocol - // TODO: remove this `initialize` method eventually, as it's only used for timers - for protocol in shared.protocols.0.iter() { - protocol.custom_data().initialize(&NetworkContextImpl { - inner: shared.clone(), - protocol: protocol.id().clone(), - current_peer: None, - }); - } - - let shared_clone = shared.clone(); - let join_handle = thread::spawn(move || { - // Tokio runtime that is going to run everything in this thread. - let mut runtime = match current_thread::Runtime::new() { - Ok(c) => c, - Err(err) => { - let _ = init_tx.send(Err(err.into())); - return - } - }; - - let fut = match init_thread(shared_clone, timeouts_register_rx, close_rx) { - Ok(future) => { - debug!(target: "sub-libp2p", "Successfully started networking service"); - let _ = init_tx.send(Ok(())); - future - }, - Err(err) => { - let _ = init_tx.send(Err(err)); - return - } - }; - - match runtime.block_on(fut) { - Ok(()) => debug!(target: "sub-libp2p", "libp2p future finished"), - Err(err) => error!(target: "sub-libp2p", "error while running libp2p: {:?}", err), - } - }); - - init_rx.recv().expect("libp2p background thread panicked")?; - - Ok(NetworkService { - shared, - bg_thread: Some((close_tx, join_handle)), - }) - } - - /// Returns network configuration. - pub fn config(&self) -> &NetworkConfiguration { - &self.shared.config - } - - pub fn external_url(&self) -> Option { - // TODO: in the context of libp2p, it is hard to define what an external - // URL is, as different nodes can have multiple different ways to - // reach us - self.shared.original_listened_addr.read().get(0) - .map(|addr| - format!("{}/p2p/{}", addr, self.shared.kad_system.local_peer_id().to_base58()) - ) - } - - /// Get a list of all connected peers by id. - pub fn connected_peers(&self) -> Vec { - self.shared.network_state.connected_peers() - } - - /// Try to add a reserved peer. - pub fn add_reserved_peer(&self, peer: &str) -> Result<(), Error> { - // TODO: try to dial the peer? - self.shared.network_state.add_reserved_peer(peer) - } - - /// Try to remove a reserved peer. - pub fn remove_reserved_peer(&self, peer: &str) -> Result<(), Error> { - self.shared.network_state.remove_reserved_peer(peer) - } - - /// Set the non-reserved peer mode. - pub fn set_non_reserved_mode(&self, mode: NonReservedPeerMode) { - self.shared.network_state.set_non_reserved_mode(mode) - } - - /// Executes action in the network context - pub fn with_context(&self, protocol: ProtocolId, action: F) - where F: FnOnce(&NetworkContext) { - self.with_context_eval(protocol, action); - } - - /// Evaluates function in the network context - pub fn with_context_eval(&self, protocol: ProtocolId, action: F) - -> Option - where F: FnOnce(&NetworkContext) -> T { - if !self.shared.protocols.has_protocol(protocol) { - return None - } - - Some(action(&NetworkContextImpl { - inner: self.shared.clone(), - protocol: protocol.clone(), - current_peer: None, - })) - } -} - -impl Drop for NetworkService { - fn drop(&mut self) { - if let Some((close_tx, join)) = self.bg_thread.take() { - let _ = close_tx.send(()); - if let Err(e) = join.join() { - warn!(target: "sub-libp2p", "error while waiting on libp2p background thread: {:?}", e); - } - } - - debug_assert!(!self.shared.network_state.has_connected_peer()); - } -} - -#[derive(Clone)] -struct NetworkContextImpl { - inner: Arc, - protocol: ProtocolId, - current_peer: Option, -} - -impl NetworkContext for NetworkContextImpl { - fn send(&self, peer: NodeIndex, packet_id: PacketId, data: Vec) { - self.send_protocol(self.protocol, peer, packet_id, data) - } - - fn send_protocol( - &self, - protocol: ProtocolId, - peer: NodeIndex, - packet_id: PacketId, - data: Vec - ) { - debug_assert!(self.inner.protocols.has_protocol(protocol), - "invalid protocol id requested in the API of the libp2p networking"); - // TODO: could be "optimized" by building `message` only after checking the validity of - // the peer, but that's probably not worth the effort - let mut message = Bytes::with_capacity(1 + data.len()); - message.extend_from_slice(&[packet_id]); - message.extend_from_slice(&data); - if self.inner.network_state.send(peer, protocol, message).is_err() { - debug!(target: "sub-libp2p", "Sending to peer {} failed. Dropping.", peer); - self.inner.network_state.drop_peer(peer); - } - } - - fn respond(&self, packet_id: PacketId, data: Vec) { - if let Some(peer) = self.current_peer { - self.send_protocol(self.protocol, peer, packet_id, data) - } else { - panic!("respond() called outside of a received message"); - } - } - - fn report_peer(&self, peer: NodeIndex, reason: Severity) { - if let Some(info) = self.inner.network_state.peer_info(peer) { - if let Some(client_version) = info.client_version { - info!(target: "sub-libp2p", - "Peer {} ({:?} {}) reported by client: {}", - peer, - info.remote_address, - client_version, - reason - ); - } else { - info!(target: "sub-libp2p", "Peer {} reported by client: {}", peer, reason); - } - } - match reason { - Severity::Bad(reason) => self.inner.network_state.ban_peer(peer, reason), - Severity::Useless(_) => self.inner.network_state.drop_peer(peer), - Severity::Timeout => self.inner.network_state.drop_peer(peer), - } - } - - fn is_expired(&self) -> bool { - if let Some(current_peer) = self.current_peer { - !self.inner.network_state.is_peer_connected(current_peer) - } else { - // TODO: is this correct? - true - } - } - - fn register_timer(&self, token: usize, duration: Duration) - -> Result<(), Error> { - let handler = self.inner.protocols - .find_protocol(self.protocol) - .ok_or(ErrorKind::BadProtocol)? - .custom_data() - .clone(); - self.inner.timeouts_register_tx - .unbounded_send((duration, (handler, self.protocol, token))) - .map_err(|err| ErrorKind::Io(IoError::new(IoErrorKind::Other, err)))?; - Ok(()) - } - - fn peer_client_version(&self, peer: NodeIndex) -> String { - // Devp2p returns "unknown" on unknown peer ID, so we do the same. - self.inner.network_state.peer_client_version(peer, self.protocol) - .unwrap_or_else(|| "unknown".to_string()) - } - - fn session_info(&self, peer: NodeIndex) -> Option { - self.inner.network_state.session_info(peer, self.protocol) - } - - fn protocol_version(&self, protocol: ProtocolId, peer: NodeIndex) -> Option { - self.inner.network_state.protocol_version(peer, protocol) - } - - fn subprotocol_name(&self) -> ProtocolId { - self.protocol.clone() - } -} - -/// Builds the main `Future` for the network service. -/// -/// - `timeouts_register_rx` should receive newly-registered timeouts. -/// - `close_rx` should be triggered when we want to close the network. -fn init_thread( - shared: Arc, - timeouts_register_rx: mpsc::UnboundedReceiver< - (Duration, (Arc, ProtocolId, TimerToken)) - >, - close_rx: oneshot::Receiver<()> -) -> Result, Error> { - // Build the transport layer. - let transport = { - let base = transport::build_transport( - shared.network_state.local_private_key().clone() - ); - - let base = base.map_err_dial({ - let shared = shared.clone(); - move |err, addr| { - trace!(target: "sub-libp2p", "Failed to dial {}: {:?}", addr, err); - shared.network_state.report_failed_to_connect(&addr); - err - } - }); - - let shared = shared.clone(); - Transport::and_then(base, move |(peer_id, stream), endpoint, remote_addr| { - remote_addr.and_then(move |remote_addr| { - if &peer_id == shared.kad_system.local_peer_id() { - // TODO: this happens very frequently for now and floods the logs - //warn!(target: "sub-libp2p", "Refusing connection from our local peer id"); - return Err(IoErrorKind::ConnectionRefused.into()) - } - - // TODO: there's a possible race condition here if `cleanup_and_prepare_updates` is - // called between `assign_node_index` and one of `kad_connec`, `unique_connec`, - // etc. ; in practice though, it is very unlikely to happen - let node_index = shared.network_state.assign_node_index(&peer_id)?; - shared.network_state.report_connected(node_index, &remote_addr, endpoint)?; - let out = TransportOutput { - socket: stream, - node_index, - original_addr: remote_addr.clone(), - }; - Ok((out, future::ok(remote_addr))) - }) - }) - }; - - // Build the swarm. The swarm is the single entry point where successfully - // negotiated protocols arrive. - let (swarm_controller, swarm_events) = { - let upgraded_transport = transport.clone() - .and_then({ - let shared = shared.clone(); - move |out, endpoint, client_addr| { - let node_index = out.node_index; - let original_addr = out.original_addr; - let listener_upgrade = upgrade::or(upgrade::or(upgrade::or( - upgrade::map(shared.kad_upgrade.clone(), move |(c, f)| FinalUpgrade::Kad(node_index, c, f)), - upgrade::map(IdentifyProtocolConfig, move |id| FinalUpgrade::from((node_index, id, original_addr)))), - upgrade::map(ping::Ping, move |out| FinalUpgrade::from((node_index, out)))), - upgrade::map(DelayedProtosList(shared), move |c| FinalUpgrade::Custom(node_index, c))); - upgrade::apply(out.socket, listener_upgrade, endpoint, client_addr) - } - }); - let shared = shared.clone(); - - libp2p::core::swarm( - upgraded_transport, - move |upgrade, _client_addr| - listener_handle(shared.clone(), upgrade) - ) - }; - - // Listen on multiaddresses. - for addr in &shared.config.listen_addresses { - match swarm_controller.listen_on(addr.clone()) { - Ok(new_addr) => { - debug!(target: "sub-libp2p", "Libp2p listening on {}", new_addr); - shared.original_listened_addr.write().push(new_addr.clone()); - }, - Err(_) => { - warn!(target: "sub-libp2p", "Can't listen on {}, protocol not supported", addr); - return Err(ErrorKind::BadProtocol.into()) - }, - } - } - - // Explicitely connect to _all_ the boostrap nodes as a temporary measure. - for bootnode in shared.config.boot_nodes.iter() { - match shared.network_state.add_bootstrap_peer(bootnode) { - Ok((who, addr)) => { - trace!(target: "sub-libp2p", "Dialing bootnode {:?} through {}", who, addr); - for proto in shared.protocols.0.clone().into_iter() { - open_peer_custom_proto( - shared.clone(), - transport.clone(), - addr.clone(), - Some(who.clone()), - proto, - &swarm_controller - ) - } - }, - Err(Error(ErrorKind::AddressParse, _)) => { - // fallback: trying with IP:Port - let multi = match bootnode.parse::() { - Ok(SocketAddr::V4(socket)) => - format!("/ip4/{}/tcp/{}", socket.ip(), socket.port()).parse::(), - Ok(SocketAddr::V6(socket)) => - format!("/ip6/{}/tcp/{}", socket.ip(), socket.port()).parse::(), - _ => { - warn!(target: "sub-libp2p", "Not a valid Bootnode Address {:}", bootnode); - continue; - } - }; - - if let Ok(addr) = multi { - trace!(target: "sub-libp2p", "Missing NodeIndex for Bootnode {:}. Querying", bootnode); - for proto in shared.protocols.0.clone().into_iter() { - open_peer_custom_proto( - shared.clone(), - transport.clone(), - addr.clone(), - None, - proto, - &swarm_controller - ) - } - } else { - warn!(target: "sub-libp2p", "Not a valid Bootnode Address {:}", bootnode); - continue; - } - }, - Err(err) => warn!(target:"sub-libp2p", "Couldn't parse Bootnode Address: {}", err), - } - } - - let outgoing_connections = Interval::new(Instant::now(), Duration::from_secs(5)) - .map_err(|err| IoError::new(IoErrorKind::Other, err)) - .for_each({ - let shared = shared.clone(); - let transport = transport.clone(); - let swarm_controller = swarm_controller.clone(); - move |_| { - connect_to_nodes(shared.clone(), transport.clone(), &swarm_controller); - Ok(()) - } - }); - - // Build the timeouts system for the `register_timeout` function. - // (note: this has nothing to do with socket timeouts) - let timeouts = timeouts::build_timeouts_stream(timeouts_register_rx) - .for_each({ - let shared = shared.clone(); - move |(handler, protocol_id, timer_token)| { - handler.timeout(&NetworkContextImpl { - inner: shared.clone(), - protocol: protocol_id, - current_peer: None, - }, timer_token); - Ok(()) - } - }) - .then(|val| { - warn!(target: "sub-libp2p", "Timeouts stream closed unexpectedly: {:?}", val); - val - }); - - // Start the process of periodically discovering nodes to connect to. - let discovery = start_kademlia_discovery(shared.clone(), - transport.clone(), swarm_controller.clone()); - - // Start the process of pinging the active nodes on the network. - let periodic = start_periodic_updates(shared.clone(), transport, swarm_controller); - - let futures: Vec>> = vec![ - Box::new(swarm_events.for_each(|_| Ok(()))), - Box::new(discovery), - Box::new(periodic), - Box::new(outgoing_connections), - Box::new(timeouts), - Box::new(close_rx.map_err(|err| IoError::new(IoErrorKind::Other, err))), - ]; - - Ok( - select_all(futures) - .and_then(move |_| { - debug!(target: "sub-libp2p", "Networking ended ; disconnecting all peers"); - shared.network_state.disconnect_all(); - Ok(()) - }) - .map_err(|(r, _, _)| r) - ) -} - -/// Output of the common transport layer. -struct TransportOutput { - socket: S, - node_index: NodeIndex, - original_addr: Multiaddr, -} - -/// Enum of all the possible protocols our service handles. -enum FinalUpgrade { - Kad(NodeIndex, KadConnecController, Box + Send>), - /// The remote identification system, and the multiaddress we see the remote as. - IdentifyListener(NodeIndex, IdentifySender, Multiaddr), - /// The remote information about the address they see us as. - IdentifyDialer(NodeIndex, IdentifyInfo, Multiaddr), - PingDialer(NodeIndex, ping::Pinger, Box + Send>), - PingListener(NodeIndex, Box + Send>), - /// `Custom` means anything not in the core libp2p and is handled - /// by `CustomProtoConnectionUpgrade`. - Custom(NodeIndex, RegisteredProtocolOutput>), -} - -impl From<(NodeIndex, ping::PingOutput)> for FinalUpgrade { - fn from((node_index, out): (NodeIndex, ping::PingOutput)) -> FinalUpgrade { - match out { - ping::PingOutput::Ponger(processing) => - FinalUpgrade::PingListener(node_index, processing), - ping::PingOutput::Pinger { pinger, processing } => - FinalUpgrade::PingDialer(node_index, pinger, processing), - } - } -} - -impl From<(NodeIndex, IdentifyOutput, Multiaddr)> for FinalUpgrade { - fn from((node_index, out, addr): (NodeIndex, IdentifyOutput, Multiaddr)) -> FinalUpgrade { - match out { - IdentifyOutput::RemoteInfo { info, observed_addr } => - FinalUpgrade::IdentifyDialer(node_index, info, observed_addr), - IdentifyOutput::Sender { sender } => - FinalUpgrade::IdentifyListener(node_index, sender, addr), - } - } -} - -/// Called whenever we successfully open a multistream with a remote. -fn listener_handle<'a, C>( - shared: Arc, - upgrade: FinalUpgrade, -) -> Box + Send + 'a> - where C: AsyncRead + AsyncWrite + Send + 'a { - match upgrade { - FinalUpgrade::Kad(node_index, controller, kademlia_stream) => { - trace!(target: "sub-libp2p", "Opened kademlia substream with #{:?}", node_index); - match handle_kademlia_connection(shared, node_index, controller, kademlia_stream) { - Ok(fut) => Box::new(fut) as Box<_>, - Err(err) => Box::new(future::err(err)) as Box<_>, - } - }, - - FinalUpgrade::IdentifyListener(node_index, sender, original_addr) => { - trace!(target: "sub-libp2p", "Sending back identification info to #{}", node_index); - sender.send( - IdentifyInfo { - public_key: shared.network_state.local_public_key().clone(), - protocol_version: concat!("substrate/", - env!("CARGO_PKG_VERSION")).to_owned(), // TODO: ? - agent_version: concat!("substrate/", - env!("CARGO_PKG_VERSION")).to_owned(), - listen_addrs: shared.listened_addrs.read().clone(), - protocols: Vec::new(), // TODO: protocols_to_report, - }, - &original_addr - ) - }, - - FinalUpgrade::IdentifyDialer(node_index, info, observed_addr) => { - process_identify_info(&shared, node_index, &info, &observed_addr); - Box::new(future::ok(())) - }, - - FinalUpgrade::PingListener(node_index, future) => { - trace!(target: "sub-libp2p", "Received ping substream from #{}", node_index); - future - }, - - FinalUpgrade::PingDialer(node_index, pinger, future) => { - let ping_connec = match shared.network_state.ping_connection(node_index) { - Some(p) => p, - None => return Box::new(future::ok(())) as Box<_> - }; - trace!(target: "sub-libp2p", "Successfully opened ping substream with #{}", node_index); - let fut = ping_connec.tie_or_passthrough(pinger, future); - Box::new(fut) as Box<_> - }, - - FinalUpgrade::Custom(node_index, custom_proto_out) => { - // A "custom" protocol is one that is part of substrate and not part of libp2p. - let shared = shared.clone(); - let fut = handle_custom_connection(shared, node_index, custom_proto_out); - Box::new(fut) as Box<_> - }, - } -} - -/// Handles a newly-opened Kademlia connection. -fn handle_kademlia_connection( - shared: Arc, - node_index: NodeIndex, - controller: KadConnecController, - kademlia_stream: Box + Send> -) -> Result, IoError> { - let kad_connec = match shared.network_state.kad_connection(node_index) { - Some(kad) => kad, - None => return Err(IoError::new(IoErrorKind::Other, "node no longer exists")), - }; - - let node_id = match shared.network_state.node_id_from_index(node_index) { - Some(id) => id, - None => return Err(IoError::new(IoErrorKind::Other, "node no longer exists")), - }; - - let node_id2 = node_id.clone(); - let future = future::loop_fn(kademlia_stream, move |kademlia_stream| { - let shared = shared.clone(); - let node_id = node_id.clone(); - - let next = kademlia_stream - .into_future() - .map_err(|(err, _)| err); - - Timeout::new(next, Duration::from_secs(20)) - .map_err(|err| - // TODO: improve the error reporting here, but tokio-timer's API is bad - IoError::new(IoErrorKind::Other, err) - ) - .and_then(move |(req, rest)| { - shared.kad_system.update_kbuckets(node_id); - match req { - Some(KadIncomingRequest::FindNode { searched, responder }) => { - let resp = build_kademlia_response(&shared, &searched); - trace!(target: "sub-libp2p", "Responding to Kad {:?} with {:?}", searched, resp); - responder.respond(resp) - }, - Some(KadIncomingRequest::PingPong) => (), - None => return Ok(future::Loop::Break(())) - } - Ok(future::Loop::Continue(rest)) - }) - }).then(move |val| { - trace!(target: "sub-libp2p", "Closed Kademlia connection with #{} {:?} => {:?}", node_index, node_id2, val); - val - }); - - Ok(kad_connec.tie_or_passthrough(controller, future)) -} - -/// When a remote performs a `FIND_NODE` Kademlia request for `searched`, -/// this function builds the response to send back. -fn build_kademlia_response( - shared: &Arc, - searched: &PeerstorePeerId -) -> Vec { - shared.kad_system - .known_closest_peers(searched) - .map(move |who| { - if who == *shared.kad_system.local_peer_id() { - KadPeer { - node_id: who.clone(), - multiaddrs: shared.listened_addrs.read().clone(), - connection_ty: KadConnectionType::Connected, - } - } else { - let mut addrs = shared.network_state.addrs_of_peer(&who); - let connected = addrs.iter().any(|&(_, conn)| conn); - // The Kademlia protocol of libp2p doesn't allow specifying which address is valid - // and which is outdated, therefore in order to stay honest towards the network - // we only report the addresses we're connected to if we're connected to any. - if connected { - addrs = addrs.into_iter() - .filter_map(|(a, c)| if c { Some((a, c)) } else { None }) - .collect(); - } - - KadPeer { - node_id: who.clone(), - multiaddrs: addrs.into_iter().map(|(a, _)| a).collect(), - connection_ty: if connected { - KadConnectionType::Connected - } else { - KadConnectionType::NotConnected - }, - } - } - }) - // TODO: we really want to remove nodes with no multiaddress from - // the results, but a flaw in the Kad protocol of libp2p makes it - // impossible to return empty results ; therefore we must at least - // return ourselves - .filter(|p| p.node_id == *shared.kad_system.local_peer_id() || - !p.multiaddrs.is_empty()) - .take(20) - .collect::>() -} - -/// Handles a newly-opened connection to a remote with a custom protocol -/// (eg. `/substrate/dot/0`). -/// Returns a future that corresponds to when the handling is finished. -fn handle_custom_connection( - shared: Arc, - node_index: NodeIndex, - custom_proto_out: RegisteredProtocolOutput> -) -> Box + Send> { - let handler = custom_proto_out.custom_data; - let protocol_id = custom_proto_out.protocol_id; - - // Determine the ID of this peer, or drop the connection if the peer is disabled, - // if we reached `max_peers`, or a similar reason. - // TODO: is there a better way to refuse connections than to drop the - // newly-opened substream? should we refuse the connection - // beforehand? - let unique_connec = match shared.network_state.custom_proto( - node_index, - protocol_id, - ) { - Some(c) => c, - None => return Box::new(future::err(IoErrorKind::Other.into())) as Box<_>, - }; - - if let UniqueConnecState::Full = unique_connec.state() { - debug!(target: "sub-libp2p", - "Interrupting connection attempt to #{} with {:?} because we're already connected", - node_index, - custom_proto_out.protocol_id - ); - return Box::new(future::ok(())) as Box<_> - } - - struct ProtoDisconnectGuard { - inner: Arc, - who: NodeIndex, - handler: Arc, - protocol: ProtocolId, - print_log_message: bool, - } - - impl Drop for ProtoDisconnectGuard { - fn drop(&mut self) { - if self.print_log_message { - info!(target: "sub-libp2p", - "Node {:?} with peer ID {} through protocol {:?} disconnected", - self.inner.network_state.node_id_from_index(self.who), - self.who, - self.protocol - ); - } - self.handler.disconnected(&NetworkContextImpl { - inner: self.inner.clone(), - protocol: self.protocol, - current_peer: Some(self.who), - }, &self.who); - - // When any custom protocol drops, we drop the peer entirely. - // TODO: is this correct? - self.inner.network_state.drop_peer(self.who); - } - } - - let mut dc_guard = ProtoDisconnectGuard { - inner: shared.clone(), - who: node_index, - handler: handler.clone(), - protocol: protocol_id, - print_log_message: true, - }; - - let fut = custom_proto_out.incoming - .for_each({ - let shared = shared.clone(); - let handler = handler.clone(); - move |(packet_id, data)| { - if let Some(id) = shared.network_state.node_id_from_index(node_index) { - shared.kad_system.update_kbuckets(id); - } - handler.read(&NetworkContextImpl { - inner: shared.clone(), - protocol: protocol_id, - current_peer: Some(node_index.clone()), - }, &node_index, packet_id, &data); - Ok(()) - } - }); - - let val = (custom_proto_out.outgoing, custom_proto_out.protocol_version); - let final_fut = unique_connec.tie_or_stop(val, fut) - .then(move |val| { - info!(target: "sub-libp2p", "Finishing future for proto {:?} with {:?} => {:?}", - protocol_id, node_index, val); - // Makes sure that `dc_guard` is kept alive until here. - dc_guard.print_log_message = false; - drop(dc_guard); - val - }); - - debug!(target: "sub-libp2p", - "Successfully connected to {:?} (peer id #{}) with protocol {:?} version {}", - shared.network_state.node_id_from_index(node_index), - node_index, - protocol_id, - custom_proto_out.protocol_version - ); - - handler.connected(&NetworkContextImpl { - inner: shared.clone(), - protocol: protocol_id, - current_peer: Some(node_index), - }, &node_index); - - Box::new(final_fut) as Box<_> -} - -/// Randomly discovers peers to connect to. -/// This works by running a round at a regular interval, and skipping if we -/// reached `min_peers`. When we are over `min_peers`, we stop trying to dial -/// nodes and only accept incoming connections. -fn start_kademlia_discovery( - shared: Arc, - transport: T, - swarm_controller: SwarmController + Send>> -) -> Box + Send> - where T: MuxedTransport> + Clone + Send + 'static, - T::Dial: Send, - T::MultiaddrFuture: Send + 'static, - T::Listener: Send, - T::ListenerUpgrade: Send, - T::Incoming: Send, - T::IncomingUpgrade: Send, - To: AsyncRead + AsyncWrite + Send + 'static, - St: MuxedTransport> + Clone + Send + 'static, - St::Dial: Send, - St::MultiaddrFuture: Send, - St::Listener: Send, - St::ListenerUpgrade: Send, - St::Incoming: Send, - St::IncomingUpgrade: Send, - C: Send + 'static { - let kad_init = shared.kad_system.perform_initialization({ - let shared = shared.clone(); - let transport = transport.clone(); - let swarm_controller = swarm_controller.clone(); - move |who| - obtain_kad_connection( - shared.clone(), - who.clone(), - transport.clone(), - swarm_controller.clone() - ) - }); - - // We perform a random Kademlia query at a regular interval. - let discovery = Interval::new(Instant::now(), Duration::from_secs(32)) - // TODO: add a timeout to the lookups? - .map_err(|err| IoError::new(IoErrorKind::Other, err)) - .for_each({ - let shared = shared.clone(); - let transport = transport.clone(); - let swarm_controller = swarm_controller.clone(); - move |_| { - let _ = shared.network_state.flush_caches_to_disk(); - perform_kademlia_query(shared.clone(), transport.clone(), swarm_controller.clone()) - } - }); - - let final_future = kad_init - .select(discovery) - .map_err(|(err, _)| err) - .and_then(|(_, rest)| rest); - - // Note that we use a Box in order to speed compilation time. - Box::new(final_future) as Box + Send> -} - -/// Performs a kademlia request to a random node. -/// Note that we don't actually care about the results, so the future -/// produces `()`. -fn perform_kademlia_query( - shared: Arc, - transport: T, - swarm_controller: SwarmController + Send>> -) -> Box + Send> - where T: MuxedTransport> + Clone + Send + 'static, - T::MultiaddrFuture: Send + 'static, - T::Dial: Send, - T::Listener: Send, - T::ListenerUpgrade: Send, - T::Incoming: Send, - T::IncomingUpgrade: Send, - To: AsyncRead + AsyncWrite + Send + 'static, - St: MuxedTransport> + Send + Clone + 'static, - St::Dial: Send, - St::MultiaddrFuture: Send, - St::Listener: Send, - St::ListenerUpgrade: Send, - St::Incoming: Send, - St::IncomingUpgrade: Send, - C: Send + 'static { - // Query the node IDs that are closest to a random ID. - // Note that the randomness doesn't have to be secure, as this only - // influences which nodes we end up being connected to. - let random_key = PublicKey::Ed25519((0 .. 32) - .map(|_| -> u8 { rand::random() }).collect()); - let random_peer_id = random_key.into_peer_id(); - trace!(target: "sub-libp2p", "Start kademlia discovery for {:?}", random_peer_id); - - let future = shared.clone() - .kad_system - .find_node(random_peer_id, { - let shared = shared.clone(); - let transport = transport.clone(); - let swarm_controller = swarm_controller.clone(); - move |who| obtain_kad_connection(shared.clone(), who.clone(), - transport.clone(), swarm_controller.clone()) - }) - .filter_map(move |event| - match event { - KadQueryEvent::PeersReported(peers) => { - for peer in peers { - let connected = match peer.connection_ty { - KadConnectionType::NotConnected => false, - KadConnectionType::Connected => true, - KadConnectionType::CanConnect => true, - KadConnectionType::CannotConnect => continue, - }; - - for addr in peer.multiaddrs { - shared.network_state.add_kad_discovered_addr( - &peer.node_id, - addr, - connected - ); - } - } - None - }, - KadQueryEvent::Finished(_) => Some(()), - } - ) - .into_future() - .map_err(|(err, _)| err) - .map(|_| ()); - - // Note that we use a `Box` in order to speed up compilation. - Box::new(future) as Box + Send> -} - -/// Connects to additional nodes, if necessary. -fn connect_to_nodes( - shared: Arc, - base_transport: T, - swarm_controller: &SwarmController + Send>> -) - where T: MuxedTransport> + Clone + Send + 'static, - T::MultiaddrFuture: Send + 'static, - T::Dial: Send, - T::Listener: Send, - T::ListenerUpgrade: Send, - T::Incoming: Send, - T::IncomingUpgrade: Send, - To: AsyncRead + AsyncWrite + Send + 'static, - St: MuxedTransport> + Clone + Send + 'static, - St::Dial: Send, - St::MultiaddrFuture: Send, - St::Listener: Send, - St::ListenerUpgrade: Send, - St::Incoming: Send, - St::IncomingUpgrade: Send, - C: Send + 'static { - let (addrs, _will_change) = shared.network_state.outgoing_connections_to_attempt(); - - for (peer, addr) in addrs.into_iter() { - // Try to dial that node for each registered protocol. Since dialing - // upgrades the connection to use multiplexing, dialing multiple times - // should automatically open multiple substreams. - for proto in shared.protocols.0.clone().into_iter() { - open_peer_custom_proto( - shared.clone(), - base_transport.clone(), - addr.clone(), - Some(peer.clone()), - proto, - swarm_controller - ) - } - } -} - -/// Dials the given address for the given protocol and using the given `swarm_controller`. -/// -/// This function *always* performs a dial, and doesn't check whether we already have an existing -/// connection to the remote. This is expected to be checked by the caller. -/// -/// The dialing will fail if the obtained peer ID doesn't match the expected ID. This is an -/// opinionated decision, as we could just let the new connection through. But we decide not to. -/// If `None` is passed for the expected peer ID, we always accept the connection. -fn open_peer_custom_proto( - shared: Arc, - base_transport: T, - addr: Multiaddr, - expected_peer_id: Option, - proto: RegisteredProtocol>, - swarm_controller: &SwarmController + Send>> -) - where T: MuxedTransport> + Clone + Send + 'static, - T::MultiaddrFuture: Send + 'static, - T::Dial: Send, - T::Listener: Send, - T::ListenerUpgrade: Send, - T::Incoming: Send, - T::IncomingUpgrade: Send, - To: AsyncRead + AsyncWrite + Send + 'static, - St: MuxedTransport> + Clone + Send + 'static, - St::Dial: Send, - St::MultiaddrFuture: Send, - St::Listener: Send, - St::ListenerUpgrade: Send, - St::Incoming: Send, - St::IncomingUpgrade: Send, - C: Send + 'static, -{ - let proto_id = proto.id(); - - let with_proto = base_transport - .and_then(move |out, endpoint, client_addr| { - let node_index = out.node_index; - upgrade::apply(out.socket, proto, endpoint, client_addr) - .map(move |(custom, client_addr)| - ((node_index, FinalUpgrade::Custom(node_index, custom)), client_addr)) - }); - - let with_timeout = TransportTimeout::new(with_proto, Duration::from_secs(20)); - - if let Some(expected_peer_id) = expected_peer_id { - let expected_node_index = match shared.network_state.assign_node_index(&expected_peer_id) { - Ok(i) => i, - Err(_) => return, - }; - - let unique_connec = match shared.network_state.custom_proto(expected_node_index, proto_id) { - Some(uc) => uc, - None => return, - }; - - let with_peer_check = with_timeout - .and_then(move |(node_index, custom), _, client_addr| { - if node_index == expected_node_index { - future::ok((custom, client_addr)) - } else { - future::err(IoError::new(IoErrorKind::ConnectionRefused, "Peer id mismatch")) - } - }); - - trace!(target: "sub-libp2p", - "Opening connection to {:?} through {} with proto {:?}", - expected_peer_id, - addr, - proto_id - ); - - let _ = unique_connec.dial(swarm_controller, &addr, with_peer_check); - - } else { - let trans = with_timeout.map(|(_, out), _| out); - if let Err(addr) = swarm_controller.dial(addr, trans) { - debug!(target: "sub-libp2p", "Failed to dial {:?}", addr); - } - } -} - -/// Obtain a Kademlia connection to the given peer. -fn obtain_kad_connection( - shared: Arc, - who: PeerstorePeerId, - transport: T, - swarm_controller: SwarmController + Send>> -) -> Box + Send> - where T: MuxedTransport> + Clone + Send + 'static, - T::MultiaddrFuture: Send + 'static, - T::Dial: Send, - T::Listener: Send, - T::ListenerUpgrade: Send, - T::Incoming: Send, - T::IncomingUpgrade: Send, - To: AsyncRead + AsyncWrite + Send + 'static, - St: MuxedTransport> + Clone + Send + 'static, - St::Dial: Send, - St::MultiaddrFuture: Send, - St::Listener: Send, - St::ListenerUpgrade: Send, - St::Incoming: Send, - St::IncomingUpgrade: Send, - C: Send + 'static { - let kad_upgrade = shared.kad_upgrade.clone(); - let transport = transport - .and_then(move |out, endpoint, client_addr| { - let node_index = out.node_index; - upgrade::apply(out.socket, kad_upgrade.clone(), endpoint, client_addr) - .map(move |((ctrl, fut), addr)| (FinalUpgrade::Kad(node_index, ctrl, fut), addr)) - }); - - // This function consists in trying all the addresses we know one by one until we find - // one that works. - // - // This `future` returns a Kad controller, or an error if all dialing attempts failed. - let future = stream::iter_ok(shared.network_state.addrs_of_peer(&who)) - .and_then(move |addr| { - let node_index = shared.network_state.assign_node_index(&who)?; - let kad = match shared.network_state.kad_connection(node_index) { - Some(kad) => kad, - None => return Err(IoError::new(IoErrorKind::Other, "node no longer exists")), - }; - Ok((kad, addr)) - }) - .and_then(move |(unique_connec, addr)| { - unique_connec.dial(&swarm_controller, &addr.0, transport.clone()) - }) - .then(|result| -> Result<_, ()> { Ok(result.ok()) }) - .filter_map(|result| result) - .into_future() - .map_err(|_| -> IoError { unreachable!("all items always succeed") }) - .and_then(|(kad, _)| kad.ok_or_else(|| IoErrorKind::ConnectionRefused.into())); - - // Note that we use a Box in order to speed up compilation. - Box::new(future) as Box + Send> -} - -/// Processes the identification information that we received about a node. -fn process_identify_info( - shared: &Shared, - node_index: NodeIndex, - info: &IdentifyInfo, - observed_addr: &Multiaddr, -) { - trace!(target: "sub-libp2p", "Received identification info from #{}", node_index); - - shared.network_state.set_node_info(node_index, info.agent_version.clone()); - - for original_listened_addr in &*shared.original_listened_addr.read() { - // TODO: we're using a hack here ; ideally we would call `nat_traversal` on our - // `Transport` ; but that's complicated to pass around ; we could put it in a `Box` in - // `Shared`, but since our transport doesn't implement `Send` (libp2p doesn't implement - // `Send` on modifiers), we can't. Instead let's just recreate a transport locally every - // time. - let transport = libp2p::tcp::TcpConfig::new(); - if let Some(mut ext_addr) = transport.nat_traversal(original_listened_addr, &observed_addr) { - let mut listened_addrs = shared.listened_addrs.write(); - if !listened_addrs.iter().any(|a| a == &ext_addr) { - trace!(target: "sub-libp2p", - "NAT traversal: remote observes us as {}; registering {} as one of our own addresses", - observed_addr, - ext_addr - ); - listened_addrs.push(ext_addr.clone()); - ext_addr.append(AddrComponent::P2P(shared.kad_system - .local_peer_id().clone().into())); - info!(target: "sub-libp2p", "New external node address: {}", ext_addr); - } - } - } - - for addr in info.listen_addrs.iter() { - if let Some(node_id) = shared.network_state.node_id_from_index(node_index) { - shared.network_state.add_kad_discovered_addr(&node_id, addr.clone(), true); - } - } -} - -/// Returns a future that regularly pings every peer we're connected to. -/// If a peer doesn't respond after a while, we disconnect it. -fn start_periodic_updates( - shared: Arc, - transport: T, - swarm_controller: SwarmController + Send>> -) -> Box + Send> - where T: MuxedTransport> + Clone + Send + 'static, - T::MultiaddrFuture: Send + 'static, - T::Dial: Send, - T::Listener: Send, - T::ListenerUpgrade: Send, - T::Incoming: Send, - T::IncomingUpgrade: Send, - To: AsyncRead + AsyncWrite + Send + 'static, - St: MuxedTransport> + Clone + Send + 'static, - St::Dial: Send, - St::MultiaddrFuture: Send, - St::Listener: Send, - St::ListenerUpgrade: Send, - St::Incoming: Send, - St::IncomingUpgrade: Send, - C: Send + 'static { - let ping_transport = transport.clone() - .and_then(move |out, endpoint, client_addr| { - let node_index = out.node_index; - upgrade::apply(out.socket, ping::Ping, endpoint, client_addr) - .map(move |(stream, addr)| (FinalUpgrade::from((node_index, stream)), addr)) - }); - - let identify_transport = transport - .and_then(move |out, endpoint, client_addr| { - let node_index = out.node_index; - upgrade::apply(out.socket, IdentifyProtocolConfig, endpoint, client_addr) - .map(move |(id, addr)| { - let fin = match id { - IdentifyOutput::RemoteInfo { info, observed_addr } => - FinalUpgrade::IdentifyDialer(node_index, info, observed_addr), - IdentifyOutput::Sender { .. } => unreachable!("can't reach that on the dialer side"), - }; - (fin, addr) - }) - }); - - let fut = Interval::new(Instant::now() + Duration::from_secs(5), Duration::from_secs(30)) - .map_err(|err| IoError::new(IoErrorKind::Other, err)) - .for_each(move |_| periodic_updates( - shared.clone(), - ping_transport.clone(), - identify_transport.clone(), - &swarm_controller - )) - .then(|val| { - warn!(target: "sub-libp2p", "Periodic updates stream has stopped: {:?}", val); - val - }); - - // Note that we use a Box in order to speed compilation time. - Box::new(fut) as Box + Send> -} - -/// Pings all the nodes we're connected to and disconnects any node that -/// doesn't respond. Identifies nodes that need to be identified. Returns -/// a `Future` when all the pings have either suceeded or timed out. -fn periodic_updates( - shared: Arc, - ping_transport: Tp, - identify_transport: Tid, - swarm_controller: &SwarmController + Send>> -) -> Box + Send> - where Tp: MuxedTransport> + Clone + Send + 'static, - Tp::MultiaddrFuture: Send + 'static, - Tp::Dial: Send, - Tp::MultiaddrFuture: Send, - Tp::Listener: Send, - Tp::ListenerUpgrade: Send, - Tp::Incoming: Send, - Tp::IncomingUpgrade: Send, - Tid: MuxedTransport> + Clone + Send + 'static, - Tid::MultiaddrFuture: Send + 'static, - Tid::Dial: Send, - Tid::MultiaddrFuture: Send, - Tid::Listener: Send, - Tid::ListenerUpgrade: Send, - Tid::Incoming: Send, - Tid::IncomingUpgrade: Send, - St: MuxedTransport> + Clone + Send + 'static, - St::Dial: Send, - St::MultiaddrFuture: Send, - St::Listener: Send, - St::ListenerUpgrade: Send, - St::Incoming: Send, - St::IncomingUpgrade: Send, - C: Send + 'static { - trace!(target: "sub-libp2p", "Periodic update cycle"); - - let mut ping_futures = Vec::new(); - - for PeriodicUpdate { node_index, peer_id, address, pinger, identify } in - shared.network_state.cleanup_and_prepare_updates() { - let shared = shared.clone(); - - let fut = pinger - .dial(&swarm_controller, &address, ping_transport.clone()) - .and_then(move |mut p| { - trace!(target: "sub-libp2p", "Pinging peer #{} aka. {:?}", node_index, peer_id); - p.ping() - .map(move |()| peer_id) - .map_err(|err| IoError::new(IoErrorKind::Other, err)) - }); - let ping_start_time = Instant::now(); - let fut = Timeout::new_at(fut, ping_start_time + Duration::from_secs(30)) - .then(move |val| - match val { - Err(err) => { - trace!(target: "sub-libp2p", "Error while pinging #{:?} => {:?}", node_index, err); - shared.network_state.report_ping_failed(node_index); - // Return Ok, otherwise we would close the ping service - Ok(()) - }, - Ok(who) => { - let elapsed = ping_start_time.elapsed(); - trace!(target: "sub-libp2p", "Pong from #{:?} in {:?}", who, elapsed); - shared.network_state.report_ping_duration(node_index, elapsed); - shared.kad_system.update_kbuckets(who); - Ok(()) - } - } - ); - ping_futures.push(fut); - - if identify { - // Ignore dialing errors, as identifying is only about diagnostics. - trace!(target: "sub-libp2p", "Attempting to identify #{}", node_index); - let _ = swarm_controller.dial(address, identify_transport.clone()); - } - } - - let future = future::loop_fn(ping_futures, |ping_futures| { - if ping_futures.is_empty() { - let fut = future::ok(future::Loop::Break(())); - return future::Either::A(fut) - } - - let fut = future::select_all(ping_futures) - .map(|((), _, rest)| future::Loop::Continue(rest)) - .map_err(|(err, _, _)| err); - future::Either::B(fut) - }); - - // Note that we use a Box in order to speed up compilation. - Box::new(future) as Box + Send> -} - -/// Since new protocols are added after the networking starts, we have to load the protocols list -/// in a lazy way. This is what this wrapper does. -#[derive(Clone)] -struct DelayedProtosList(Arc); -// `Maf` is short for `MultiaddressFuture` -impl ConnectionUpgrade for DelayedProtosList -where C: AsyncRead + AsyncWrite + Send + 'static, // TODO: 'static :-/ - Maf: Future + Send + 'static, // TODO: 'static :( -{ - type NamesIter = > as ConnectionUpgrade>::NamesIter; - type UpgradeIdentifier = > as ConnectionUpgrade>::UpgradeIdentifier; - - fn protocol_names(&self) -> Self::NamesIter { - ConnectionUpgrade::::protocol_names(&self.0.protocols) - } - - type Output = > as ConnectionUpgrade>::Output; - type MultiaddrFuture = > as ConnectionUpgrade>::MultiaddrFuture; - type Future = > as ConnectionUpgrade>::Future; - - #[inline] - fn upgrade(self, socket: C, id: Self::UpgradeIdentifier, endpoint: Endpoint, - remote_addr: Maf) -> Self::Future - { - self.0.protocols - .clone() - .upgrade(socket, id, endpoint, remote_addr) - } -} - -#[cfg(test)] -mod tests { - use super::NetworkService; - - #[test] - fn builds_and_finishes_in_finite_time() { - // Checks that merely starting the network doesn't end up in an infinite loop. - let _service = NetworkService::new(Default::default(), vec![]).unwrap(); - } -} diff --git a/substrate/network-libp2p/src/timeouts.rs b/substrate/network-libp2p/src/timeouts.rs deleted file mode 100644 index 9b5615b0d54ae..0000000000000 --- a/substrate/network-libp2p/src/timeouts.rs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -use futures::{Async, future, Future, Poll, stream, Stream, sync::mpsc}; -use std::io::{Error as IoError, ErrorKind as IoErrorKind}; -use std::marker::PhantomData; -use std::time::{Duration, Instant}; -use tokio_timer::{self, Delay}; - -/// Builds the timeouts system. -/// -/// The `timeouts_rx` should be a stream receiving newly-created timeout -/// requests. Returns a stream that produces items as their timeout elapses. -/// `T` can be anything you want, as it is transparently passed from the input -/// to the output. Timeouts continue to fire forever, as there is no way to -/// unregister them. -pub fn build_timeouts_stream<'a, T>( - timeouts_rx: mpsc::UnboundedReceiver<(Duration, T)> -) -> Box + 'a> - where T: Clone + 'a { - let next_timeout = next_in_timeouts_stream(timeouts_rx); - - // The `unfold` function is essentially a loop turned into a stream. The - // first parameter is the initial state, and the closure returns the new - // state and an item. - let stream = stream::unfold(vec![future::Either::A(next_timeout)], move |timeouts| { - // `timeouts` is a `Vec` of futures that produce an `Out`. - - // `select_ok` panics if `timeouts` is empty anyway. - if timeouts.is_empty() { - return None - } - - Some(future::select_ok(timeouts.into_iter()) - .and_then(move |(item, mut timeouts)| - match item { - Out::NewTimeout((Some((duration, item)), next_timeouts)) => { - // Received a new timeout request on the channel. - let next_timeout = next_in_timeouts_stream(next_timeouts); - let timeout = Delay::new(Instant::now() + duration); - let timeout = TimeoutWrapper(timeout, duration, Some(item), PhantomData); - timeouts.push(future::Either::B(timeout)); - timeouts.push(future::Either::A(next_timeout)); - Ok((None, timeouts)) - }, - Out::NewTimeout((None, _)) => - // The channel has been closed. - Ok((None, timeouts)), - Out::Timeout(duration, item) => { - // A timeout has happened. - let returned = item.clone(); - let timeout = Delay::new(Instant::now() + duration); - let timeout = TimeoutWrapper(timeout, duration, Some(item), PhantomData); - timeouts.push(future::Either::B(timeout)); - Ok((Some(returned), timeouts)) - }, - } - ) - ) - }).filter_map(|item| item); - - // Note that we use a `Box` in order to speed up compilation time. - Box::new(stream) as Box> -} - -/// Local enum representing the output of the selection. -enum Out { - NewTimeout(A), - Timeout(Duration, B), -} - -/// Convenience function that calls `.into_future()` on the timeouts stream, -/// and applies some modifiers. -/// This function is necessary. Otherwise if we copy-paste its content we run -/// into errors because the type of the copy-pasted closures differs. -fn next_in_timeouts_stream( - stream: mpsc::UnboundedReceiver -) -> impl Future, mpsc::UnboundedReceiver), B>, Error = IoError> { - stream - .into_future() - .map(Out::NewTimeout) - .map_err(|_| unreachable!("an UnboundedReceiver can never error")) -} - -/// Does the equivalent to `future.map(move |()| (duration, item)).map_err(|err| to_io_err(err))`. -struct TimeoutWrapper(F, Duration, Option, PhantomData); -impl Future for TimeoutWrapper - where F: Future { - type Item = Out; - type Error = IoError; - - fn poll(&mut self) -> Poll { - match self.0.poll() { - Ok(Async::Ready(())) => (), - Ok(Async::NotReady) => return Ok(Async::NotReady), - Err(err) => return Err(IoError::new(IoErrorKind::Other, err.to_string())), - } - - let out = Out::Timeout(self.1, self.2.take().expect("poll() called again after success")); - Ok(Async::Ready(out)) - } -} diff --git a/substrate/network-libp2p/src/topology.rs b/substrate/network-libp2p/src/topology.rs deleted file mode 100644 index d36eb07624bca..0000000000000 --- a/substrate/network-libp2p/src/topology.rs +++ /dev/null @@ -1,655 +0,0 @@ -// Copyright 2018 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 fnv::FnvHashMap; -use parking_lot::Mutex; -use libp2p::{Multiaddr, PeerId}; -use serde_json; -use std::{cmp, fs}; -use std::io::{Read, Cursor, Error as IoError, ErrorKind as IoErrorKind, Write, BufReader, BufWriter}; -use std::path::{Path, PathBuf}; -use std::time::{Duration, Instant, SystemTime}; - -/// For each address we're connected to, a period of this duration increases the score by 1. -const CONNEC_DURATION_PER_SCORE: Duration = Duration::from_secs(10); -/// Maximum value for the score. -const MAX_SCORE: u32 = 100; -/// When we successfully connect to a node, raises its score to the given minimum value. -const CONNECTED_MINIMUM_SCORE: u32 = 20; -/// Initial score that a node discovered through Kademlia receives, where we have a hint that the -/// node is reachable. -const KADEMLIA_DISCOVERY_INITIAL_SCORE_CONNECTABLE: u32 = 15; -/// Initial score that a node discovered through Kademlia receives, without any hint. -const KADEMLIA_DISCOVERY_INITIAL_SCORE: u32 = 10; -/// Score adjustement when we fail to connect to an address. -const SCORE_DIFF_ON_FAILED_TO_CONNECT: i32 = -1; -/// Default time-to-live for addresses discovered through Kademlia. -/// After this time has elapsed and no connection has succeeded, the address will be removed. -const KADEMLIA_DISCOVERY_EXPIRATION: Duration = Duration::from_secs(2 * 3600); -/// After a successful connection, the TTL is set to a minimum at this amount. -const EXPIRATION_PUSH_BACK_CONNEC: Duration = Duration::from_secs(2 * 3600); -/// Initial score that a bootstrap node receives when registered. -const BOOTSTRAP_NODE_SCORE: u32 = 100; -/// Time to live of a boostrap node. This only applies if you start the node later *without* -/// that bootstrap node configured anymore. -const BOOTSTRAP_NODE_EXPIRATION: Duration = Duration::from_secs(24 * 3600); -/// The first time we fail to connect to an address, wait this duration before trying again. -const FIRST_CONNECT_FAIL_BACKOFF: Duration = Duration::from_secs(2); -/// Every time we fail to connect to an address, multiply the backoff by this constant. -const FAIL_BACKOFF_MULTIPLIER: u32 = 2; -/// We need a maximum value for the backoff, overwise we risk an overflow. -const MAX_BACKOFF: Duration = Duration::from_secs(30 * 60); - -// TODO: should be merged with the Kademlia k-buckets - -/// Stores information about the topology of the network. -#[derive(Debug)] -pub struct NetTopology { - store: FnvHashMap, - cache_path: Option, -} - -impl Default for NetTopology { - #[inline] - fn default() -> NetTopology { - NetTopology::memory() - } -} - -impl NetTopology { - /// Initializes a new `NetTopology` that isn't tied to any file. - /// - /// `flush_to_disk()` will be a no-op. - #[inline] - pub fn memory() -> NetTopology { - NetTopology { - store: Default::default(), - cache_path: None, - } - } - - /// Builds a `NetTopology` that will use `path` as a cache. - /// - /// This function tries to load a known topology from the file. If the file doesn't exist - /// or contains garbage data, the execution still continues. - /// - /// Calling `flush_to_disk()` in the future writes to the given path. - pub fn from_file>(path: P) -> NetTopology { - let path = path.as_ref(); - debug!(target: "sub-libp2p", "Initializing peer store for JSON file {:?}", path); - NetTopology { - store: try_load(path), - cache_path: Some(path.to_owned()), - } - } - - /// Writes the topology into the path passed to `from_file`. - /// - /// No-op if the object was created with `memory()`. - pub fn flush_to_disk(&self) -> Result<(), IoError> { - let path = match self.cache_path { - Some(ref p) => p, - None => return Ok(()) - }; - - let file = fs::File::create(path)?; - // TODO: the capacity of the BufWriter is kind of arbitrary ; decide better - serialize(BufWriter::with_capacity(1024 * 1024, file), &self.store) - } - - /// Perform a cleanup pass, removing all obsolete addresses and peers. - /// - /// This should be done from time to time. - pub fn cleanup(&mut self) { - let now_systime = SystemTime::now(); - self.store.retain(|_, peer| { - peer.addrs.retain(|a| { - a.expires > now_systime || a.is_connected() - }); - !peer.addrs.is_empty() - }); - } - - /// Returns the known potential addresses of a peer, ordered by score. - /// - /// The boolean associated to each address indicates whether we're connected to it. - // TODO: filter out backed off ones? - pub fn addrs_of_peer(&self, peer: &PeerId) -> impl Iterator { - let peer = if let Some(peer) = self.store.get(peer) { - peer - } else { - // TODO: use an EitherIterator or something - return Vec::new().into_iter(); - }; - - let now = SystemTime::now(); - let mut list = peer.addrs.iter().filter_map(move |addr| { - let (score, connected) = addr.score_and_is_connected(); - if (addr.expires >= now && score > 0) || connected { - Some((score, connected, &addr.addr)) - } else { - None - } - }).collect::>(); - list.sort_by(|a, b| a.0.cmp(&b.0)); - // TODO: meh, optimize - let l = list.into_iter().map(|(_, connec, addr)| (addr, connec)).collect::>(); - l.into_iter() - } - - /// Returns a list of all the known addresses of peers, ordered by the - /// order in which we should attempt to connect to them. - /// - /// Because of expiration and back-off mechanisms, this list can grow - /// by itself over time. The `Instant` that is returned corresponds to - /// the earlier known time when a new entry will be added automatically to - /// the list. - pub fn addrs_to_attempt(&self) -> (impl Iterator, Instant) { - // TODO: optimize - let now = Instant::now(); - let now_systime = SystemTime::now(); - let mut instant = now + Duration::from_secs(3600); - let mut addrs_out = Vec::new(); - - for (peer, info) in &self.store { - for addr in &info.addrs { - let (score, is_connected) = addr.score_and_is_connected(); - if score == 0 || addr.expires < now_systime { - continue; - } - if !is_connected && addr.back_off_until > now { - instant = cmp::min(instant, addr.back_off_until); - continue; - } - - addrs_out.push(((peer, &addr.addr), score)); - } - } - - addrs_out.sort_by(|a, b| b.1.cmp(&a.1)); - (addrs_out.into_iter().map(|a| a.0), instant) - } - - /// Adds an address corresponding to a boostrap node. - /// - /// We assume that the address is valid, so its score starts very high. - pub fn add_bootstrap_addr(&mut self, peer: &PeerId, addr: Multiaddr) { - let now_systime = SystemTime::now(); - let now = Instant::now(); - - let peer = peer_access(&mut self.store, peer); - - let mut found = false; - peer.addrs.retain(|a| { - if a.expires < now_systime && !a.is_connected() { - return false; - } - if a.addr == addr { - found = true; - } - true - }); - - if !found { - peer.addrs.push(Addr { - addr, - expires: now_systime + BOOTSTRAP_NODE_EXPIRATION, - back_off_until: now, - next_back_off: FIRST_CONNECT_FAIL_BACKOFF, - score: Mutex::new(AddrScore { - connected_since: None, - score: BOOTSTRAP_NODE_SCORE, - latest_score_update: now, - }), - }); - } - } - - /// Adds an address discovered through the Kademlia DHT. - /// - /// This address is not necessarily valid and should expire after a TTL. - /// - /// If `connectable` is true, that means we have some sort of hint that this node can - /// be reached. - pub fn add_kademlia_discovered_addr( - &mut self, - peer_id: &PeerId, - addr: Multiaddr, - connectable: bool - ) { - let now_systime = SystemTime::now(); - let now = Instant::now(); - - let peer = peer_access(&mut self.store, peer_id); - - let mut found = false; - peer.addrs.retain(|a| { - if a.expires < now_systime && !a.is_connected() { - return false; - } - if a.addr == addr { - found = true; - } - true - }); - - if !found { - trace!( - target: "sub-libp2p", - "Peer store: adding address {} for {:?} (connectable hint: {:?})", - addr, - peer_id, - connectable - ); - - let initial_score = if connectable { - KADEMLIA_DISCOVERY_INITIAL_SCORE_CONNECTABLE - } else { - KADEMLIA_DISCOVERY_INITIAL_SCORE - }; - - peer.addrs.push(Addr { - addr, - expires: now_systime + KADEMLIA_DISCOVERY_EXPIRATION, - back_off_until: now, - next_back_off: FIRST_CONNECT_FAIL_BACKOFF, - score: Mutex::new(AddrScore { - connected_since: None, - score: initial_score, - latest_score_update: now, - }), - }); - } - } - - /// Indicates the peer store that we're connected to this given address. - /// - /// This increases the score of the address that we connected to. Since we assume that only - /// one peer can be reached with any specific address, we also remove all addresses from other - /// peers that match the one we connected to. - pub fn report_connected(&mut self, addr: &Multiaddr, peer: &PeerId) { - let now = Instant::now(); - - // Just making sure that we have an entry for this peer in `store`, but don't use it. - let _ = peer_access(&mut self.store, peer); - - for (peer_in_store, info_in_store) in self.store.iter_mut() { - if peer == peer_in_store { - if let Some(addr) = info_in_store.addrs.iter_mut().find(|a| &a.addr == addr) { - addr.connected_now(CONNECTED_MINIMUM_SCORE); - addr.back_off_until = now; - addr.next_back_off = FIRST_CONNECT_FAIL_BACKOFF; - continue; - } - - // TODO: a else block would be better, but we get borrowck errors - info_in_store.addrs.push(Addr { - addr: addr.clone(), - expires: SystemTime::now() + EXPIRATION_PUSH_BACK_CONNEC, - back_off_until: now, - next_back_off: FIRST_CONNECT_FAIL_BACKOFF, - score: Mutex::new(AddrScore { - connected_since: Some(now), - latest_score_update: now, - score: CONNECTED_MINIMUM_SCORE, - }), - }); - - } else { - // Set the score to 0 for any address that matches the one we connected to. - for addr_in_store in &mut info_in_store.addrs { - if &addr_in_store.addr == addr { - addr_in_store.adjust_score(-(MAX_SCORE as i32)); - } - } - } - } - } - - /// Indicates the peer store that we're disconnected from an address. - /// - /// There's no need to indicate a peer ID, as each address can only have one peer ID. - /// If we were indeed connected to this addr, then we can find out which peer ID it is. - pub fn report_disconnected(&mut self, addr: &Multiaddr, reason: DisconnectReason) { - let score_diff = match reason { - DisconnectReason::ClosedGracefully => -1, - }; - - for info in self.store.values_mut() { - for a in info.addrs.iter_mut() { - if &a.addr == addr { - a.disconnected_now(score_diff); - a.back_off_until = Instant::now() + a.next_back_off; - a.next_back_off = cmp::min(a.next_back_off * FAIL_BACKOFF_MULTIPLIER, MAX_BACKOFF); - let expires_push_back = SystemTime::now() + EXPIRATION_PUSH_BACK_CONNEC; - if a.expires < expires_push_back { - a.expires = expires_push_back; - } - return; - } - } - } - } - - /// Indicates the peer store that we failed to connect to an address. - /// - /// We don't care about which peer is supposed to be behind that address. If we failed to dial - /// it for a specific peer, we would also fail to dial it for all peers that have this - /// address. - pub fn report_failed_to_connect(&mut self, addr: &Multiaddr) { - for info in self.store.values_mut() { - for a in info.addrs.iter_mut() { - if &a.addr == addr { - a.adjust_score(SCORE_DIFF_ON_FAILED_TO_CONNECT); - a.back_off_until = Instant::now() + a.next_back_off; - a.next_back_off = cmp::min(a.next_back_off * FAIL_BACKOFF_MULTIPLIER, MAX_BACKOFF); - } - } - } - } -} - -/// Reason why we disconnected from a peer. -pub enum DisconnectReason { - /// The disconnection was graceful. - ClosedGracefully, -} - -fn peer_access<'a>(store: &'a mut FnvHashMap, peer: &PeerId) -> &'a mut PeerInfo { - // TODO: should be optimizable if HashMap gets a better API - store.entry(peer.clone()).or_insert_with(Default::default) -} - -#[derive(Debug, Clone, Default)] -struct PeerInfo { - /// Addresses of that peer. - addrs: Vec, -} - -#[derive(Debug)] -struct Addr { - /// The multiaddress. - addr: Multiaddr, - /// When the address expires. - expires: SystemTime, - next_back_off: Duration, - /// Don't try to connect to this node until `Instant`. - back_off_until: Instant, - score: Mutex, -} - -impl Clone for Addr { - fn clone(&self) -> Addr { - Addr { - addr: self.addr.clone(), - expires: self.expires.clone(), - next_back_off: self.next_back_off.clone(), - back_off_until: self.back_off_until.clone(), - score: Mutex::new(self.score.lock().clone()), - } - } -} - -#[derive(Debug, Clone)] -struct AddrScore { - /// If connected, contains the moment when we connected. `None` if we're not connected. - connected_since: Option, - /// Score of this address. Potentially needs to be updated based on `latest_score_update`. - score: u32, - /// When we last updated the score. - latest_score_update: Instant, -} - -impl Addr { - /// Sets the addr to connected. If the score is lower than the given value, raises it to this - /// value. - fn connected_now(&self, raise_to_min: u32) { - let mut score = self.score.lock(); - let now = Instant::now(); - Addr::flush(&mut score, now); - score.connected_since = Some(now); - if score.score < raise_to_min { - score.score = raise_to_min; - } - } - - /// Applies a modification to the score. - fn adjust_score(&self, score_diff: i32) { - let mut score = self.score.lock(); - Addr::flush(&mut score, Instant::now()); - if score_diff >= 0 { - score.score = cmp::min(MAX_SCORE, score.score + score_diff as u32); - } else { - score.score = score.score.saturating_sub(-score_diff as u32); - } - } - - /// Sets the addr to disconnected and applies a modification to the score. - fn disconnected_now(&self, score_diff: i32) { - let mut score = self.score.lock(); - Addr::flush(&mut score, Instant::now()); - score.connected_since = None; - if score_diff >= 0 { - score.score = cmp::min(MAX_SCORE, score.score + score_diff as u32); - } else { - score.score = score.score.saturating_sub(-score_diff as u32); - } - } - - /// Returns true if we are connected to this addr. - fn is_connected(&self) -> bool { - let score = self.score.lock(); - score.connected_since.is_some() - } - - /// Returns the score, and true if we are connected to this addr. - fn score_and_is_connected(&self) -> (u32, bool) { - let mut score = self.score.lock(); - Addr::flush(&mut score, Instant::now()); - let is_connected = score.connected_since.is_some(); - (score.score, is_connected) - } - - /// Updates `score` and `latest_score_update`, and returns the score. - fn score(&self) -> u32 { - let mut score = self.score.lock(); - Addr::flush(&mut score, Instant::now()); - score.score - } - - fn flush(score: &mut AddrScore, now: Instant) { - if let Some(connected_since) = score.connected_since { - let potential_score: u32 = div_dur_with_dur(now - connected_since, CONNEC_DURATION_PER_SCORE); - // We flush when we connect to an address. - debug_assert!(score.latest_score_update >= connected_since); - let effective_score: u32 = - div_dur_with_dur(score.latest_score_update - connected_since, CONNEC_DURATION_PER_SCORE); - let to_add = potential_score.saturating_sub(effective_score); - score.score = cmp::min(MAX_SCORE, score.score + to_add); - } - - score.latest_score_update = now; - } -} - -/// Divides a `Duration` with a `Duration`. This exists in the stdlib but isn't stable yet. -// TODO: remove this function once stable -fn div_dur_with_dur(a: Duration, b: Duration) -> u32 { - let a_ms = a.as_secs() * 1_000_000 + (a.subsec_nanos() / 1_000) as u64; - let b_ms = b.as_secs() * 1_000_000 + (b.subsec_nanos() / 1_000) as u64; - (a_ms / b_ms) as u32 -} - -/// Serialized version of a `PeerInfo`. Suitable for storage in the cache file. -#[derive(Debug, Clone, Serialize, Deserialize)] -struct SerializedPeerInfo { - addrs: Vec, -} - -/// Serialized version of an `Addr`. Suitable for storage in the cache file. -#[derive(Debug, Clone, Serialize, Deserialize)] -struct SerializedAddr { - addr: String, - expires: SystemTime, - score: u32, -} - -impl<'a> From<&'a Addr> for SerializedAddr { - fn from(addr: &'a Addr) -> SerializedAddr { - SerializedAddr { - addr: addr.addr.to_string(), - expires: addr.expires, - score: addr.score(), - } - } -} - -/// Attempts to load storage from a file. -/// Deletes the file and returns an empty map if the file doesn't exist, cannot be opened -/// or is corrupted. -fn try_load(path: impl AsRef) -> FnvHashMap { - let path = path.as_ref(); - if !path.exists() { - debug!(target: "sub-libp2p", "Peer storage file {:?} doesn't exist", path); - return Default::default() - } - - let mut file = match fs::File::open(path) { - // TODO: the capacity of the BufReader is kind of arbitrary ; decide better - Ok(f) => BufReader::with_capacity(1024 * 1024, f), - Err(err) => { - warn!(target: "sub-libp2p", "Failed to open peer storage file: {:?}", err); - info!(target: "sub-libp2p", "Deleting peer storage file {:?}", path); - let _ = fs::remove_file(path); - return Default::default() - } - }; - - // We want to support empty files (and treat them as an empty recordset). Unfortunately - // `serde_json` will always produce an error if we do this ("unexpected EOF at line 0 - // column 0"). Therefore we start by reading one byte from the file in order to check - // for EOF. - - let mut first_byte = [0]; - let num_read = match file.read(&mut first_byte) { - Ok(f) => f, - Err(err) => { - // TODO: DRY - warn!(target: "sub-libp2p", "Failed to read peer storage file: {:?}", err); - info!(target: "sub-libp2p", "Deleting peer storage file {:?}", path); - let _ = fs::remove_file(path); - return Default::default() - } - }; - - if num_read == 0 { - // File is empty. - debug!(target: "sub-libp2p", "Peer storage file {:?} is empty", path); - Default::default() - - } else { - let data = Cursor::new(first_byte).chain(file); - match serde_json::from_reader::<_, serde_json::Value>(data) { - Ok(serde_json::Value::Null) => Default::default(), - Ok(serde_json::Value::Object(map)) => deserialize_tolerant(map.into_iter()), - Ok(_) | Err(_) => { - // The `Ok(_)` case means that the file doesn't contain a map. - let _ = fs::remove_file(path); - Default::default() - }, - } - } -} - -/// Attempts to turn a deserialized version of the storage into the final version. -/// -/// Skips entries that are invalid. -fn deserialize_tolerant( - iter: impl Iterator -) -> FnvHashMap { - let now = Instant::now(); - let now_systime = SystemTime::now(); - - let mut out = FnvHashMap::default(); - for (peer, info) in iter { - let peer: PeerId = match peer.parse() { - Ok(p) => p, - Err(_) => continue, - }; - - let info: SerializedPeerInfo = match serde_json::from_value(info) { - Ok(i) => i, - Err(_) => continue, - }; - - let mut addrs = Vec::with_capacity(info.addrs.len()); - for addr in info.addrs { - let multiaddr = match addr.addr.parse() { - Ok(a) => a, - Err(_) => continue, - }; - - if addr.expires < now_systime { - continue - } - - addrs.push(Addr { - addr: multiaddr, - expires: addr.expires, - next_back_off: FIRST_CONNECT_FAIL_BACKOFF, - back_off_until: now, - score: Mutex::new(AddrScore { - connected_since: None, - score: addr.score, - latest_score_update: now, - }), - }); - } - - if addrs.is_empty() { - continue; - } - - out.insert(peer, PeerInfo { addrs }); - } - - out -} - -/// Attempts to turn a deserialized version of the storage into the final version. -/// -/// Skips entries that are invalid or expired. -fn serialize(out: W, map: &FnvHashMap) -> Result<(), IoError> { - let now = SystemTime::now(); - let array: FnvHashMap<_, _> = map.iter().filter_map(|(peer, info)| { - if info.addrs.is_empty() { - return None - } - - let peer = peer.to_base58(); - let info = SerializedPeerInfo { - addrs: info.addrs.iter() - .filter(|a| a.expires > now || a.is_connected()) - .map(Into::into) - .collect(), - }; - - Some((peer, info)) - }).collect(); - - serde_json::to_writer_pretty(out, &array) - .map_err(|err| IoError::new(IoErrorKind::Other, err)) -} diff --git a/substrate/network-libp2p/src/traits.rs b/substrate/network-libp2p/src/traits.rs deleted file mode 100644 index de18f083c559d..0000000000000 --- a/substrate/network-libp2p/src/traits.rs +++ /dev/null @@ -1,299 +0,0 @@ -// Copyright 2015-2018 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::fmt; -use std::cmp::Ordering; -use std::iter; -use std::net::Ipv4Addr; -use std::str; -use std::time::Duration; -use io::TimerToken; -use libp2p::{multiaddr::AddrComponent, Multiaddr}; -use error::Error; -use ethkey::Secret; -use ethereum_types::H512; - -/// Protocol handler level packet id -pub type PacketId = u8; -/// Protocol / handler id -pub type ProtocolId = [u8; 3]; - -/// Node public key -pub type NodeId = H512; - -/// Local (temporary) peer session ID. -pub type NodeIndex = usize; - -/// Shared session information -#[derive(Debug, Clone)] -pub struct SessionInfo { - /// Peer public key - pub id: Option, - /// Peer client ID - pub client_version: String, - /// Peer RLPx protocol version - pub protocol_version: u32, - /// Session protocol capabilities - pub capabilities: Vec, - /// Peer protocol capabilities - pub peer_capabilities: Vec, - /// Peer ping delay - pub ping: Option, - /// True if this session was originated by us. - pub originated: bool, - /// Remote endpoint address of the session - pub remote_address: String, - /// Local endpoint address of the session - pub local_address: String, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct PeerCapabilityInfo { - pub protocol: ProtocolId, - pub version: u8, -} - -impl ToString for PeerCapabilityInfo { - fn to_string(&self) -> String { - format!("{}/{}", str::from_utf8(&self.protocol[..]).unwrap_or("???"), self.version) - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct SessionCapabilityInfo { - pub protocol: [u8; 3], - pub version: u8, - pub packet_count: u8, - pub id_offset: u8, -} - -impl PartialOrd for SessionCapabilityInfo { - fn partial_cmp(&self, other: &SessionCapabilityInfo) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for SessionCapabilityInfo { - fn cmp(&self, b: &SessionCapabilityInfo) -> Ordering { - // By protocol id first - if self.protocol != b.protocol { - return self.protocol.cmp(&b.protocol); - } - // By version - self.version.cmp(&b.version) - } -} - -/// Network service configuration -#[derive(Debug, PartialEq, Clone)] -pub struct NetworkConfiguration { - /// Directory path to store general network configuration. None means nothing will be saved - pub config_path: Option, - /// Directory path to store network-specific configuration. None means nothing will be saved - pub net_config_path: Option, - /// Multiaddresses to listen for incoming connections. - pub listen_addresses: Vec, - /// Multiaddresses to advertise. Detected automatically if empty. - pub public_addresses: Vec, - /// List of initial node addresses - pub boot_nodes: Vec, - /// Use provided node key instead of default - pub use_secret: Option, - /// Minimum number of connected peers to maintain - pub min_peers: u32, - /// Maximum allowed number of peers - pub max_peers: u32, - /// List of reserved node addresses. - pub reserved_nodes: Vec, - /// The non-reserved peer mode. - pub non_reserved_mode: NonReservedPeerMode, - /// Client identifier - pub client_version: String, -} - -impl Default for NetworkConfiguration { - fn default() -> Self { - NetworkConfiguration::new() - } -} - -impl NetworkConfiguration { - /// Create a new instance of default settings. - pub fn new() -> Self { - NetworkConfiguration { - config_path: None, - net_config_path: None, - listen_addresses: vec![ - iter::once(AddrComponent::IP4(Ipv4Addr::new(0, 0, 0, 0))) - .chain(iter::once(AddrComponent::TCP(30333))) - .collect() - ], - public_addresses: Vec::new(), - boot_nodes: Vec::new(), - use_secret: None, - min_peers: 25, - max_peers: 50, - reserved_nodes: Vec::new(), - non_reserved_mode: NonReservedPeerMode::Accept, - client_version: "Parity-network".into(), - } - } - - /// Create new default configuration for localhost-only connection with random port (useful for testing) - pub fn new_local() -> NetworkConfiguration { - let mut config = NetworkConfiguration::new(); - config.listen_addresses = vec![ - iter::once(AddrComponent::IP4(Ipv4Addr::new(127, 0, 0, 1))) - .chain(iter::once(AddrComponent::TCP(0))) - .collect() - ]; - config - } -} - -/// The severity of misbehaviour of a peer that is reported. -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum Severity<'a> { - /// Peer is timing out. Could be bad connectivity of overload of work on either of our sides. - Timeout, - /// Peer has been notably useless. E.g. unable to answer a request that we might reasonably consider - /// it could answer. - Useless(&'a str), - /// Peer has behaved in an invalid manner. This doesn't necessarily need to be Byzantine, but peer - /// must have taken concrete action in order to behave in such a way which is wantanly invalid. - Bad(&'a str), -} - -impl<'a> fmt::Display for Severity<'a> { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - match *self { - Severity::Timeout => write!(fmt, "Timeout"), - Severity::Useless(r) => write!(fmt, "Useless ({})", r), - Severity::Bad(r) => write!(fmt, "Bad ({})", r), - } - } -} - -/// IO access point. This is passed to all IO handlers and provides an interface to the IO subsystem. -pub trait NetworkContext { - /// Send a packet over the network to another peer. - fn send(&self, peer: NodeIndex, packet_id: PacketId, data: Vec); - - /// Send a packet over the network to another peer using specified protocol. - fn send_protocol(&self, protocol: ProtocolId, peer: NodeIndex, packet_id: PacketId, data: Vec); - - /// Respond to a current network message. Panics if no there is no packet in the context. If the session is expired returns nothing. - fn respond(&self, packet_id: PacketId, data: Vec); - - /// Report peer. Depending on the report, peer may be disconnected and possibly banned. - fn report_peer(&self, peer: NodeIndex, reason: Severity); - - /// Check if the session is still active. - fn is_expired(&self) -> bool; - - /// Register a new IO timer. 'IoHandler::timeout' will be called with the token. - fn register_timer(&self, token: TimerToken, delay: Duration) -> Result<(), Error>; - - /// Returns peer identification string - fn peer_client_version(&self, peer: NodeIndex) -> String; - - /// Returns information on p2p session - fn session_info(&self, peer: NodeIndex) -> Option; - - /// Returns max version for a given protocol. - fn protocol_version(&self, protocol: ProtocolId, peer: NodeIndex) -> Option; - - /// Returns this object's subprotocol name. - fn subprotocol_name(&self) -> ProtocolId; -} - -impl<'a, T> NetworkContext for &'a T where T: ?Sized + NetworkContext { - fn send(&self, peer: NodeIndex, packet_id: PacketId, data: Vec) { - (**self).send(peer, packet_id, data) - } - - fn send_protocol(&self, protocol: ProtocolId, peer: NodeIndex, packet_id: PacketId, data: Vec) { - (**self).send_protocol(protocol, peer, packet_id, data) - } - - fn respond(&self, packet_id: PacketId, data: Vec) { - (**self).respond(packet_id, data) - } - - fn report_peer(&self, peer: NodeIndex, reason: Severity) { - (**self).report_peer(peer, reason) - } - - fn is_expired(&self) -> bool { - (**self).is_expired() - } - - fn register_timer(&self, token: TimerToken, delay: Duration) -> Result<(), Error> { - (**self).register_timer(token, delay) - } - - fn peer_client_version(&self, peer: NodeIndex) -> String { - (**self).peer_client_version(peer) - } - - fn session_info(&self, peer: NodeIndex) -> Option { - (**self).session_info(peer) - } - - fn protocol_version(&self, protocol: ProtocolId, peer: NodeIndex) -> Option { - (**self).protocol_version(protocol, peer) - } - - fn subprotocol_name(&self) -> ProtocolId { - (**self).subprotocol_name() - } -} - -/// Network IO protocol handler. This needs to be implemented for each new subprotocol. -/// All the handler function are called from within IO event loop. -/// `Message` is the type for message data. -pub trait NetworkProtocolHandler: Sync + Send { - /// Initialize the handler - fn initialize(&self, _io: &NetworkContext) {} - /// Called when new network packet received. - fn read(&self, io: &NetworkContext, peer: &NodeIndex, packet_id: u8, data: &[u8]); - /// Called when new peer is connected. Only called when peer supports the same protocol. - fn connected(&self, io: &NetworkContext, peer: &NodeIndex); - /// Called when a previously connected peer disconnects. - fn disconnected(&self, io: &NetworkContext, peer: &NodeIndex); - /// Timer function called after a timeout created with `NetworkContext::timeout`. - fn timeout(&self, _io: &NetworkContext, _timer: TimerToken) {} -} - -/// Non-reserved peer modes. -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum NonReservedPeerMode { - /// Accept them. This is the default. - Accept, - /// Deny them. - Deny, -} - -impl NonReservedPeerMode { - /// Attempt to parse the peer mode from a string. - pub fn parse(s: &str) -> Option { - match s { - "accept" => Some(NonReservedPeerMode::Accept), - "deny" => Some(NonReservedPeerMode::Deny), - _ => None, - } - } -} diff --git a/substrate/network-libp2p/src/transport.rs b/substrate/network-libp2p/src/transport.rs deleted file mode 100644 index 8acb9ecab0828..0000000000000 --- a/substrate/network-libp2p/src/transport.rs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2018 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 libp2p::{self, PeerId, Transport, mplex, secio, yamux}; -use libp2p::core::{either, upgrade, transport::BoxedMuxed}; -use libp2p::transport_timeout::TransportTimeout; -use std::time::Duration; -use std::usize; -use tokio_io::{AsyncRead, AsyncWrite}; - -/// Builds the transport that serves as a common ground for all connections. -pub fn build_transport( - local_private_key: secio::SecioKeyPair -) -> BoxedMuxed<(PeerId, impl AsyncRead + AsyncWrite)> { - let mut mplex_config = mplex::MplexConfig::new(); - mplex_config.max_buffer_len_behaviour(mplex::MaxBufferBehaviour::Block); - mplex_config.max_buffer_len(usize::MAX); - - let base = libp2p::CommonTransport::new() - .with_upgrade(secio::SecioConfig { - key: local_private_key, - }) - .and_then(move |out, endpoint, client_addr| { - let upgrade = upgrade::or( - upgrade::map(mplex_config, either::EitherOutput::First), - upgrade::map(yamux::Config::default(), either::EitherOutput::Second), - ); - let key = out.remote_key; - let upgrade = upgrade::map(upgrade, move |muxer| (key, muxer)); - upgrade::apply(out.stream, upgrade, endpoint, client_addr) - }) - .into_connection_reuse() - .map(|(key, substream), _| (key.into_peer_id(), substream)); - - TransportTimeout::new(base, Duration::from_secs(20)) - .boxed_muxed() -} diff --git a/substrate/network-libp2p/tests/tests.rs b/substrate/network-libp2p/tests/tests.rs deleted file mode 100644 index a74772171950c..0000000000000 --- a/substrate/network-libp2p/tests/tests.rs +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Parity. - -// Parity 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. - -// Parity 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 Parity. If not, see . - -extern crate parking_lot; -extern crate parity_bytes; -extern crate ethcore_io as io; -extern crate ethcore_logger; -extern crate substrate_network_libp2p; -extern crate ethkey; - -use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering}; -use std::sync::Arc; -use std::thread; -use std::time::*; -use parking_lot::Mutex; -use parity_bytes::Bytes; -use substrate_network_libp2p::*; -use ethkey::{Random, Generator}; -use io::TimerToken; - -pub struct TestProtocol { - drop_session: bool, - pub packet: Mutex, - pub got_timeout: AtomicBool, - pub got_disconnect: AtomicBool, -} - -impl TestProtocol { - pub fn new(drop_session: bool) -> Self { - TestProtocol { - packet: Mutex::new(Vec::new()), - got_timeout: AtomicBool::new(false), - got_disconnect: AtomicBool::new(false), - drop_session: drop_session, - } - } - - pub fn got_packet(&self) -> bool { - self.packet.lock()[..] == b"hello"[..] - } - - pub fn got_timeout(&self) -> bool { - self.got_timeout.load(AtomicOrdering::Relaxed) - } - - pub fn got_disconnect(&self) -> bool { - self.got_disconnect.load(AtomicOrdering::Relaxed) - } -} - -impl NetworkProtocolHandler for TestProtocol { - fn initialize(&self, io: &NetworkContext) { - io.register_timer(0, Duration::from_millis(10)).unwrap(); - } - - fn read(&self, _io: &NetworkContext, _peer: &NodeIndex, packet_id: u8, data: &[u8]) { - assert_eq!(packet_id, 33); - self.packet.lock().extend(data); - } - - fn connected(&self, io: &NetworkContext, peer: &NodeIndex) { - if self.drop_session { - io.report_peer(*peer, Severity::Bad("We are evil and just want to drop")) - } else { - io.respond(33, "hello".to_owned().into_bytes()); - } - } - - fn disconnected(&self, _io: &NetworkContext, _peer: &NodeIndex) { - self.got_disconnect.store(true, AtomicOrdering::Relaxed); - } - - /// Timer function called after a timeout created with `NetworkContext::timeout`. - fn timeout(&self, _io: &NetworkContext, timer: TimerToken) { - assert_eq!(timer, 0); - self.got_timeout.store(true, AtomicOrdering::Relaxed); - } -} - - -#[test] -fn net_service() { - let _service = NetworkService::new( - NetworkConfiguration::new_local(), - vec![(Arc::new(TestProtocol::new(false)), *b"myp", &[(1u8, 1)])] - ).expect("Error creating network service"); -} - -#[test] -#[ignore] // TODO: how is this test even supposed to work? -fn net_disconnect() { - let key1 = Random.generate().unwrap(); - let mut config1 = NetworkConfiguration::new_local(); - config1.use_secret = Some(key1.secret().clone()); - config1.boot_nodes = vec![ ]; - let handler1 = Arc::new(TestProtocol::new(false)); - let service1 = NetworkService::new(config1, vec![(handler1.clone(), *b"tst", &[(42u8, 1), (43u8, 1)])]).unwrap(); - let mut config2 = NetworkConfiguration::new_local(); - config2.boot_nodes = vec![ service1.external_url().unwrap() ]; - let handler2 = Arc::new(TestProtocol::new(true)); - let _service2 = NetworkService::new(config2, vec![(handler2.clone(), *b"tst", &[(42u8, 1), (43u8, 1)])]).unwrap(); - while !(handler1.got_disconnect() && handler2.got_disconnect()) { - thread::sleep(Duration::from_millis(50)); - } - assert!(handler1.got_disconnect()); - assert!(handler2.got_disconnect()); -} - -#[test] -fn net_timeout() { - let config = NetworkConfiguration::new_local(); - let handler = Arc::new(TestProtocol::new(false)); - let _service = NetworkService::new(config, vec![(handler.clone(), *b"tst", &[(42u8, 1), (43u8, 1)])]).unwrap(); - while !handler.got_timeout() { - thread::sleep(Duration::from_millis(50)); - } -} diff --git a/substrate/network/Cargo.toml b/substrate/network/Cargo.toml deleted file mode 100644 index 17b16e74d3558..0000000000000 --- a/substrate/network/Cargo.toml +++ /dev/null @@ -1,30 +0,0 @@ -[package] -description = "Polkadot network protocol" -name = "substrate-network" -version = "0.1.0" -license = "GPL-3.0" -authors = ["Parity Technologies "] - -[lib] - -[dependencies] -log = "0.3" -parking_lot = "0.4" -error-chain = "0.12" -bitflags = "1.0" -futures = "0.1.17" -linked-hash-map = "0.5" -rustc-hex = "1.0" -ethcore-io = { git = "https://github.com/paritytech/parity.git" } -ed25519 = { path = "../../substrate/ed25519" } -substrate-primitives = { path = "../../substrate/primitives" } -substrate-client = { path = "../../substrate/client" } -substrate-runtime-primitives = { path = "../../substrate/runtime/primitives" } -substrate-codec = { path = "../../substrate/codec" } -substrate-codec-derive = { path = "../../substrate/codec/derive" } -substrate-network-libp2p = { path = "../../substrate/network-libp2p" } - -[dev-dependencies] -env_logger = "0.4" -substrate-keyring = { path = "../../substrate/keyring" } -substrate-test-client = { path = "../../substrate/test-client" } diff --git a/substrate/network/README.adoc b/substrate/network/README.adoc deleted file mode 100644 index ac29b0cd0bfbc..0000000000000 --- a/substrate/network/README.adoc +++ /dev/null @@ -1,13 +0,0 @@ - -= Network - -.Summary -[source, toml] ----- -include::Cargo.toml[lines=2..5] ----- - -.Description ----- -include::src/lib.rs[tag=description] ----- diff --git a/substrate/network/src/blocks.rs b/substrate/network/src/blocks.rs deleted file mode 100644 index b078b9200b311..0000000000000 --- a/substrate/network/src/blocks.rs +++ /dev/null @@ -1,282 +0,0 @@ -// Copyright 2017 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::mem; -use std::cmp; -use std::ops::Range; -use std::collections::{HashMap, BTreeMap}; -use std::collections::hash_map::Entry; -use network_libp2p::NodeIndex; -use runtime_primitives::traits::{Block as BlockT, NumberFor, As}; -use message; - -const MAX_PARALLEL_DOWNLOADS: u32 = 1; - -/// Block data with origin. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct BlockData { - pub block: message::BlockData, - pub origin: NodeIndex, -} - -#[derive(Debug)] -enum BlockRangeState { - Downloading { - len: NumberFor, - downloading: u32, - }, - Complete(Vec>), -} - -impl BlockRangeState { - pub fn len(&self) -> NumberFor { - match *self { - BlockRangeState::Downloading { len, .. } => len, - BlockRangeState::Complete(ref blocks) => As::sa(blocks.len() as u64), - } - } -} - -/// A collection of blocks being downloaded. -#[derive(Default)] -pub struct BlockCollection { - /// Downloaded blocks. - blocks: BTreeMap, BlockRangeState>, - peer_requests: HashMap>, -} - -impl BlockCollection { - /// Create a new instance. - pub fn new() -> Self { - BlockCollection { - blocks: BTreeMap::new(), - peer_requests: HashMap::new(), - } - } - - /// Clear everything. - pub fn clear(&mut self) { - self.blocks.clear(); - self.peer_requests.clear(); - } - - /// Insert a set of blocks into collection. - pub fn insert(&mut self, start: NumberFor, blocks: Vec>, who: NodeIndex) { - if blocks.is_empty() { - return; - } - - match self.blocks.get(&start) { - Some(&BlockRangeState::Downloading { .. }) => { - trace!(target: "sync", "Ignored block data still marked as being downloaded: {}", start); - debug_assert!(false); - return; - }, - Some(&BlockRangeState::Complete(ref existing)) if existing.len() >= blocks.len() => { - trace!(target: "sync", "Ignored block data already downloaded: {}", start); - return; - }, - _ => (), - } - - self.blocks.insert(start, BlockRangeState::Complete(blocks.into_iter().map(|b| BlockData { origin: who, block: b }).collect())); - } - - /// Returns a set of block hashes that require a header download. The returned set is marked as being downloaded. - pub fn needed_blocks(&mut self, who: NodeIndex, count: usize, peer_best: NumberFor, common: NumberFor) -> Option>> { - // First block number that we need to download - let first_different = common + As::sa(1); - let count = As::sa(count as u64); - let (mut range, downloading) = { - let mut downloading_iter = self.blocks.iter().peekable(); - let mut prev: Option<(&NumberFor, &BlockRangeState)> = None; - loop { - let next = downloading_iter.next(); - break match &(prev, next) { - &(Some((start, &BlockRangeState::Downloading { ref len, downloading })), _) if downloading < MAX_PARALLEL_DOWNLOADS => - (*start .. *start + *len, downloading), - &(Some((start, r)), Some((next_start, _))) if *start + r.len() < *next_start => - (*start + r.len() .. cmp::min(*next_start, *start + r.len() + count), 0), // gap - &(Some((start, r)), None) => - (*start + r.len() .. *start + r.len() + count, 0), // last range - &(None, None) => - (first_different .. first_different + count, 0), // empty - &(None, Some((start, _))) if *start > first_different => - (first_different .. cmp::min(first_different + count, *start), 0), // gap at the start - _ => { - prev = next; - continue - }, - } - } - }; - // crop to peers best - if range.start > peer_best { - trace!(target: "sync", "Out of range for peer {} ({} vs {})", who, range.start, peer_best); - return None; - } - range.end = cmp::min(peer_best + As::sa(1), range.end); - self.peer_requests.insert(who, range.start); - self.blocks.insert(range.start, BlockRangeState::Downloading { len: range.end - range.start, downloading: downloading + 1 }); - if range.end <= range.start { - panic!("Empty range {:?}, count={}, peer_best={}, common={}, blocks={:?}", range, count, peer_best, common, self.blocks); - } - Some(range) - } - - /// Get a valid chain of blocks ordered in descending order and ready for importing into blockchain. - pub fn drain(&mut self, from: NumberFor) -> Vec> { - let mut drained = Vec::new(); - let mut ranges = Vec::new(); - { - let mut prev = from; - for (start, range_data) in &mut self.blocks { - match range_data { - &mut BlockRangeState::Complete(ref mut blocks) if *start <= prev => { - prev = *start + As::sa(blocks.len() as u64); - let mut blocks = mem::replace(blocks, Vec::new()); - drained.append(&mut blocks); - ranges.push(*start); - }, - _ => break, - } - } - } - for r in ranges { - self.blocks.remove(&r); - } - trace!(target: "sync", "Drained {} blocks", drained.len()); - drained - } - - pub fn clear_peer_download(&mut self, who: NodeIndex) { - match self.peer_requests.entry(who) { - Entry::Occupied(entry) => { - let start = entry.remove(); - let remove = match self.blocks.get_mut(&start) { - Some(&mut BlockRangeState::Downloading { ref mut downloading, .. }) if *downloading > 1 => { - *downloading = *downloading - 1; - false - }, - Some(&mut BlockRangeState::Downloading { .. }) => { - true - }, - _ => { - debug_assert!(false); - false - } - }; - if remove { - self.blocks.remove(&start); - } - }, - _ => (), - } - } -} - -#[cfg(test)] -mod test { - use super::{BlockCollection, BlockData, BlockRangeState}; - use message; - use runtime_primitives::testing::Block as RawBlock; - use primitives::H256; - - type Block = RawBlock; - - fn is_empty(bc: &BlockCollection) -> bool { - bc.blocks.is_empty() && - bc.peer_requests.is_empty() - } - - fn generate_blocks(n: usize) -> Vec> { - (0 .. n).map(|_| message::generic::BlockData { - hash: H256::random(), - header: None, - body: None, - message_queue: None, - receipt: None, - justification: None, - }).collect() - } - - #[test] - fn create_clear() { - let mut bc = BlockCollection::new(); - assert!(is_empty(&bc)); - bc.insert(1, generate_blocks(100), 0); - assert!(!is_empty(&bc)); - bc.clear(); - assert!(is_empty(&bc)); - } - - #[test] - fn insert_blocks() { - let mut bc = BlockCollection::new(); - assert!(is_empty(&bc)); - let peer0 = 0; - let peer1 = 1; - let peer2 = 2; - - let blocks = generate_blocks(150); - assert_eq!(bc.needed_blocks(peer0, 40, 150, 0), Some(1 .. 41)); - assert_eq!(bc.needed_blocks(peer1, 40, 150, 0), Some(41 .. 81)); - assert_eq!(bc.needed_blocks(peer2, 40, 150, 0), Some(81 .. 121)); - - bc.clear_peer_download(peer1); - bc.insert(41, blocks[41..81].to_vec(), peer1); - assert_eq!(bc.drain(1), vec![]); - assert_eq!(bc.needed_blocks(peer1, 40, 150, 0), Some(121 .. 151)); - bc.clear_peer_download(peer0); - bc.insert(1, blocks[1..11].to_vec(), peer0); - - assert_eq!(bc.needed_blocks(peer0, 40, 150, 0), Some(11 .. 41)); - assert_eq!(bc.drain(1), blocks[1..11].iter().map(|b| BlockData { block: b.clone(), origin: 0 }).collect::>()); - - bc.clear_peer_download(peer0); - bc.insert(11, blocks[11..41].to_vec(), peer0); - - let drained = bc.drain(12); - assert_eq!(drained[..30], blocks[11..41].iter().map(|b| BlockData { block: b.clone(), origin: 0 }).collect::>()[..]); - assert_eq!(drained[30..], blocks[41..81].iter().map(|b| BlockData { block: b.clone(), origin: 1 }).collect::>()[..]); - - bc.clear_peer_download(peer2); - assert_eq!(bc.needed_blocks(peer2, 40, 150, 80), Some(81 .. 121)); - bc.clear_peer_download(peer2); - bc.insert(81, blocks[81..121].to_vec(), peer2); - bc.clear_peer_download(peer1); - bc.insert(121, blocks[121..150].to_vec(), peer1); - - assert_eq!(bc.drain(80), vec![]); - let drained = bc.drain(81); - assert_eq!(drained[..40], blocks[81..121].iter().map(|b| BlockData { block: b.clone(), origin: 2 }).collect::>()[..]); - assert_eq!(drained[40..], blocks[121..150].iter().map(|b| BlockData { block: b.clone(), origin: 1 }).collect::>()[..]); - } - - #[test] - fn large_gap() { - let mut bc: BlockCollection = BlockCollection::new(); - bc.blocks.insert(100, BlockRangeState::Downloading { - len: 128, - downloading: 1, - }); - let blocks = generate_blocks(10).into_iter().map(|b| BlockData { block: b, origin: 0 }).collect(); - bc.blocks.insert(114305, BlockRangeState::Complete(blocks)); - - assert_eq!(bc.needed_blocks(0, 128, 10000, 000), Some(1 .. 100)); - assert_eq!(bc.needed_blocks(0, 128, 10000, 600), Some(100 + 128 .. 100 + 128 + 128)); - } -} diff --git a/substrate/network/src/chain.rs b/substrate/network/src/chain.rs deleted file mode 100644 index 704e32d55d117..0000000000000 --- a/substrate/network/src/chain.rs +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2017 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 . - -//! Blockchain access trait - -use client::{self, Client as SubstrateClient, ImportResult, ClientInfo, BlockStatus, BlockOrigin, CallExecutor}; -use client::error::Error; -use runtime_primitives::traits::{Block as BlockT, Header as HeaderT}; -use runtime_primitives::generic::BlockId; -use runtime_primitives::bft::Justification; -use primitives::{Blake2Hasher, RlpCodec}; - -/// Local client abstraction for the network. -pub trait Client: Send + Sync { - /// Import a new block. Parent is supposed to be existing in the blockchain. - fn import(&self, origin: BlockOrigin, header: Block::Header, justification: Justification, body: Option>) -> Result; - - /// Get blockchain info. - fn info(&self) -> Result, Error>; - - /// Get block status. - fn block_status(&self, id: &BlockId) -> Result; - - /// Get block hash by number. - fn block_hash(&self, block_number: ::Number) -> Result, Error>; - - /// Get block header. - fn header(&self, id: &BlockId) -> Result, Error>; - - /// Get block body. - fn body(&self, id: &BlockId) -> Result>, Error>; - - /// Get block justification. - fn justification(&self, id: &BlockId) -> Result>, Error>; - - /// Get block header proof. - fn header_proof(&self, block_number: ::Number) -> Result<(Block::Header, Vec>), Error>; - - /// Get storage read execution proof. - fn read_proof(&self, block: &Block::Hash, key: &[u8]) -> Result>, Error>; - - /// Get method execution proof. - fn execution_proof(&self, block: &Block::Hash, method: &str, data: &[u8]) -> Result<(Vec, Vec>), Error>; -} - -impl Client for SubstrateClient where - B: client::backend::Backend + Send + Sync + 'static, - E: CallExecutor + Send + Sync + 'static, - Block: BlockT, -{ - fn import(&self, origin: BlockOrigin, header: Block::Header, justification: Justification, body: Option>) -> Result { - // TODO: defer justification check. - let justified_header = self.check_justification(header, justification.into())?; - (self as &SubstrateClient).import_block(origin, justified_header, body) - } - - fn info(&self) -> Result, Error> { - (self as &SubstrateClient).info() - } - - fn block_status(&self, id: &BlockId) -> Result { - (self as &SubstrateClient).block_status(id) - } - - fn block_hash(&self, block_number: ::Number) -> Result, Error> { - (self as &SubstrateClient).block_hash(block_number) - } - - fn header(&self, id: &BlockId) -> Result, Error> { - (self as &SubstrateClient).header(id) - } - - fn body(&self, id: &BlockId) -> Result>, Error> { - (self as &SubstrateClient).body(id) - } - - fn justification(&self, id: &BlockId) -> Result>, Error> { - (self as &SubstrateClient).justification(id) - } - - fn header_proof(&self, block_number: ::Number) -> Result<(Block::Header, Vec>), Error> { - (self as &SubstrateClient).header_proof(&BlockId::Number(block_number)) - } - - fn read_proof(&self, block: &Block::Hash, key: &[u8]) -> Result>, Error> { - (self as &SubstrateClient).read_proof(&BlockId::Hash(block.clone()), key) - } - - fn execution_proof(&self, block: &Block::Hash, method: &str, data: &[u8]) -> Result<(Vec, Vec>), Error> { - (self as &SubstrateClient).execution_proof(&BlockId::Hash(block.clone()), method, data) - } -} diff --git a/substrate/network/src/config.rs b/substrate/network/src/config.rs deleted file mode 100644 index ad5de3957def3..0000000000000 --- a/substrate/network/src/config.rs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -pub use service::Roles; - -/// Protocol configuration -#[derive(Clone)] -pub struct ProtocolConfig { - /// Assigned roles. - pub roles: Roles, -} - -impl Default for ProtocolConfig { - fn default() -> ProtocolConfig { - ProtocolConfig { - roles: Roles::FULL, - } - } -} diff --git a/substrate/network/src/consensus_gossip.rs b/substrate/network/src/consensus_gossip.rs deleted file mode 100644 index 7b2f72bd66923..0000000000000 --- a/substrate/network/src/consensus_gossip.rs +++ /dev/null @@ -1,382 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! Utility for gossip of network messages between authorities. -//! Handles chain-specific and standard BFT messages. - -use std::collections::{HashMap, HashSet}; -use futures::sync::mpsc; -use std::time::{Instant, Duration}; -use network_libp2p::NodeIndex; -use runtime_primitives::traits::{Block as BlockT, Header as HeaderT}; -use runtime_primitives::generic::BlockId; -use message::{self, generic::Message as GenericMessage}; -use protocol::Context; -use service::Roles; - -// TODO: Add additional spam/DoS attack protection. -const MESSAGE_LIFETIME: Duration = Duration::from_secs(600); - -struct PeerConsensus { - known_messages: HashSet, -} - -/// Consensus messages. -#[derive(Debug, Clone, PartialEq)] -pub enum ConsensusMessage { - /// A message concerning BFT agreement - Bft(message::LocalizedBftMessage), - /// A message concerning some chain-specific aspect of consensus - ChainSpecific(Vec, B::Hash), -} - -struct MessageEntry { - hash: B::Hash, - message: ConsensusMessage, - instant: Instant, -} - -/// Consensus network protocol handler. Manages statements and candidate requests. -pub struct ConsensusGossip { - peers: HashMap>, - message_sink: Option<(mpsc::UnboundedSender>, B::Hash)>, - messages: Vec>, - message_hashes: HashSet, -} - -impl ConsensusGossip where B::Header: HeaderT { - /// Create a new instance. - pub fn new() -> Self { - ConsensusGossip { - peers: HashMap::new(), - message_sink: None, - messages: Default::default(), - message_hashes: Default::default(), - } - } - - /// Closes all notification streams. - pub fn abort(&mut self) { - self.message_sink = None; - } - - /// Handle new connected peer. - pub fn new_peer(&mut self, protocol: &mut Context, who: NodeIndex, roles: Roles) { - if roles.intersects(Roles::AUTHORITY | Roles::FULL) { - trace!(target:"gossip", "Registering {:?} {}", roles, who); - // Send out all known messages. - // TODO: limit by size - let mut known_messages = HashSet::new(); - for entry in self.messages.iter() { - known_messages.insert(entry.hash); - let message = match entry.message { - ConsensusMessage::Bft(ref bft) => GenericMessage::BftMessage(bft.clone()), - ConsensusMessage::ChainSpecific(ref msg, _) => GenericMessage::ChainSpecific(msg.clone()), - }; - - protocol.send_message(who, message); - } - self.peers.insert(who, PeerConsensus { - known_messages, - }); - } - } - - fn propagate(&mut self, protocol: &mut Context, message: message::Message, hash: B::Hash) { - for (id, ref mut peer) in self.peers.iter_mut() { - if peer.known_messages.insert(hash.clone()) { - trace!(target:"gossip", "Propagating to {}: {:?}", id, message); - protocol.send_message(*id, message.clone()); - } - } - } - - fn register_message(&mut self, hash: B::Hash, message: ConsensusMessage) { - if self.message_hashes.insert(hash) { - self.messages.push(MessageEntry { - hash, - instant: Instant::now(), - message, - }); - } - } - - /// Handles incoming BFT message, passing to stream and repropagating. - pub fn on_bft_message(&mut self, protocol: &mut Context, who: NodeIndex, message: message::LocalizedBftMessage) { - if let Some((hash, message)) = self.handle_incoming(protocol, who, ConsensusMessage::Bft(message)) { - // propagate to other peers. - self.multicast(protocol, message, Some(hash)); - } - } - - /// Handles incoming chain-specific message and repropagates - pub fn on_chain_specific(&mut self, protocol: &mut Context, who: NodeIndex, message: Vec, parent_hash: B::Hash) { - debug!(target: "gossip", "received chain-specific gossip message"); - if let Some((hash, message)) = self.handle_incoming(protocol, who, ConsensusMessage::ChainSpecific(message, parent_hash)) { - debug!(target: "gossip", "handled incoming chain-specific message"); - // propagate to other peers. - self.multicast(protocol, message, Some(hash)); - } - } - - /// Get a stream of messages relevant to consensus on top of a given parent hash. - pub fn messages_for(&mut self, parent_hash: B::Hash) -> mpsc::UnboundedReceiver> { - let (sink, stream) = mpsc::unbounded(); - - for entry in self.messages.iter() { - let message_matches = match entry.message { - ConsensusMessage::Bft(ref msg) => msg.parent_hash == parent_hash, - ConsensusMessage::ChainSpecific(_, ref h) => h == &parent_hash, - }; - - if message_matches { - sink.unbounded_send(entry.message.clone()).expect("receiving end known to be open; qed"); - } - } - - self.message_sink = Some((sink, parent_hash)); - stream - } - - /// Multicast a chain-specific message to other authorities. - pub fn multicast_chain_specific(&mut self, protocol: &mut Context, message: Vec, parent_hash: B::Hash) { - trace!(target:"gossip", "sending chain-specific message"); - self.multicast(protocol, ConsensusMessage::ChainSpecific(message, parent_hash), None); - } - - /// Multicast a BFT message to other authorities - pub fn multicast_bft_message(&mut self, protocol: &mut Context, message: message::LocalizedBftMessage) { - // Broadcast message to all authorities. - trace!(target:"gossip", "Broadcasting BFT message {:?}", message); - self.multicast(protocol, ConsensusMessage::Bft(message), None); - } - - /// Call when a peer has been disconnected to stop tracking gossip status. - pub fn peer_disconnected(&mut self, _protocol: &mut Context, who: NodeIndex) { - self.peers.remove(&who); - } - - /// Prune old or no longer relevant consensus messages. - /// Supply an optional block hash where consensus is known to have concluded. - pub fn collect_garbage(&mut self, best_hash: Option<&B::Hash>) { - let hashes = &mut self.message_hashes; - let before = self.messages.len(); - let now = Instant::now(); - self.messages.retain(|entry| { - if entry.instant + MESSAGE_LIFETIME >= now && - best_hash.map_or(true, |parent_hash| - match entry.message { - ConsensusMessage::Bft(ref msg) => &msg.parent_hash != parent_hash, - ConsensusMessage::ChainSpecific(_, ref h) => h != parent_hash, - }) - { - true - } else { - hashes.remove(&entry.hash); - false - } - }); - if self.messages.len() != before { - trace!(target:"gossip", "Cleaned up {} stale messages", before - self.messages.len()); - } - for (_, ref mut peer) in self.peers.iter_mut() { - peer.known_messages.retain(|h| hashes.contains(h)); - } - } - - fn handle_incoming(&mut self, protocol: &mut Context, who: NodeIndex, message: ConsensusMessage) -> Option<(B::Hash, ConsensusMessage)> { - let (hash, parent, message) = match message { - ConsensusMessage::Bft(msg) => { - let parent = msg.parent_hash; - let generic = GenericMessage::BftMessage(msg); - ( - ::protocol::hash_message(&generic), - parent, - match generic { - GenericMessage::BftMessage(msg) => ConsensusMessage::Bft(msg), - _ => panic!("`generic` is known to be the `BftMessage` variant; qed"), - } - ) - } - ConsensusMessage::ChainSpecific(msg, parent) => { - let generic = GenericMessage::ChainSpecific(msg); - ( - ::protocol::hash_message::(&generic), - parent, - match generic { - GenericMessage::ChainSpecific(msg) => ConsensusMessage::ChainSpecific(msg, parent), - _ => panic!("`generic` is known to be the `ChainSpecific` variant; qed"), - } - ) - } - }; - - if self.message_hashes.contains(&hash) { - trace!(target:"gossip", "Ignored already known message from {}", who); - return None; - } - - match (protocol.client().info(), protocol.client().header(&BlockId::Hash(parent))) { - (_, Err(e)) | (Err(e), _) => { - debug!(target:"gossip", "Error reading blockchain: {:?}", e); - return None; - }, - (Ok(info), Ok(Some(header))) => { - if header.number() < &info.chain.best_number { - trace!(target:"gossip", "Ignored ancient message from {}, hash={}", who, parent); - return None; - } - }, - (Ok(_), Ok(None)) => {}, - } - - if let Some(ref mut peer) = self.peers.get_mut(&who) { - peer.known_messages.insert(hash); - if let Some((sink, parent_hash)) = self.message_sink.take() { - if parent == parent_hash { - debug!(target: "gossip", "Pushing relevant consensus message to sink."); - if let Err(e) = sink.unbounded_send(message.clone()) { - trace!(target:"gossip", "Error broadcasting message notification: {:?}", e); - } - } - - self.message_sink = Some((sink, parent_hash)); - } - } else { - trace!(target:"gossip", "Ignored statement from unregistered peer {}", who); - return None; - } - - Some((hash, message)) - } - - fn multicast(&mut self, protocol: &mut Context, message: ConsensusMessage, hash: Option) { - let generic = match message { - ConsensusMessage::Bft(ref message) => GenericMessage::BftMessage(message.clone()), - ConsensusMessage::ChainSpecific(ref message, _) => GenericMessage::ChainSpecific(message.clone()), - }; - - let hash = hash.unwrap_or_else(|| ::protocol::hash_message(&generic)); - self.register_message(hash, message); - self.propagate(protocol, generic, hash); - } -} - -#[cfg(test)] -mod tests { - use runtime_primitives::bft::Justification; - use runtime_primitives::testing::{H256, Header, Block as RawBlock}; - use std::time::Instant; - use message::{self, generic}; - use super::*; - - type Block = RawBlock; - - #[test] - fn collects_garbage() { - let prev_hash = H256::random(); - let best_hash = H256::random(); - let mut consensus = ConsensusGossip::::new(); - let now = Instant::now(); - let m1_hash = H256::random(); - let m2_hash = H256::random(); - let m1 = ConsensusMessage::Bft(message::LocalizedBftMessage { - parent_hash: prev_hash, - message: message::generic::BftMessage::Auxiliary(Justification { - round_number: 0, - hash: Default::default(), - signatures: Default::default(), - }), - }); - let m2 = ConsensusMessage::ChainSpecific(vec![1, 2, 3], best_hash); - - macro_rules! push_msg { - ($hash:expr, $now: expr, $m:expr) => { - consensus.messages.push(MessageEntry { - hash: $hash, - instant: $now, - message: $m, - }) - } - } - - push_msg!(m1_hash, now, m1); - push_msg!(m2_hash, now, m2.clone()); - consensus.message_hashes.insert(m1_hash); - consensus.message_hashes.insert(m2_hash); - - // nothing to collect - consensus.collect_garbage(None); - assert_eq!(consensus.messages.len(), 2); - assert_eq!(consensus.message_hashes.len(), 2); - - // random header, nothing should be cleared - let mut header = Header { - parent_hash: H256::default(), - number: 0, - state_root: H256::default(), - extrinsics_root: H256::default(), - digest: Default::default(), - }; - - consensus.collect_garbage(Some(&H256::default())); - assert_eq!(consensus.messages.len(), 2); - assert_eq!(consensus.message_hashes.len(), 2); - - // header that matches one of the messages - header.parent_hash = prev_hash; - consensus.collect_garbage(Some(&prev_hash)); - assert_eq!(consensus.messages.len(), 1); - assert_eq!(consensus.message_hashes.len(), 1); - assert!(consensus.message_hashes.contains(&m2_hash)); - - // make timestamp expired - consensus.messages.clear(); - push_msg!(m2_hash, now - MESSAGE_LIFETIME, m2); - consensus.collect_garbage(None); - assert!(consensus.messages.is_empty()); - assert!(consensus.message_hashes.is_empty()); - } - - #[test] - fn message_stream_include_those_sent_before_asking_for_stream() { - use futures::Stream; - - let mut consensus = ConsensusGossip::new(); - - let bft_message = generic::BftMessage::Consensus(generic::SignedConsensusMessage::Vote(generic::SignedConsensusVote { - vote: generic::ConsensusVote::AdvanceRound(0), - sender: [0; 32].into(), - signature: Default::default(), - })); - - let parent_hash = [1; 32].into(); - - let localized = ::message::LocalizedBftMessage:: { - message: bft_message, - parent_hash: parent_hash, - }; - - let message = generic::Message::BftMessage(localized.clone()); - let message_hash = ::protocol::hash_message::(&message); - - let message = ConsensusMessage::Bft(localized); - consensus.register_message(message_hash, message.clone()); - let stream = consensus.messages_for(parent_hash); - - assert_eq!(stream.wait().next(), Some(Ok(message))); - } -} diff --git a/substrate/network/src/error.rs b/substrate/network/src/error.rs deleted file mode 100644 index 5ad888eb573ca..0000000000000 --- a/substrate/network/src/error.rs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2017 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 . - -//! Polkadot service possible errors. - -use std::io::Error as IoError; -use network_libp2p::Error as NetworkError; -use client; - -error_chain! { - foreign_links { - Network(NetworkError) #[doc = "Devp2p error."]; - Io(IoError) #[doc = "IO error."]; - } - - links { - Client(client::error::Error, client::error::ErrorKind) #[doc="Client error"]; - } - - errors { - } -} diff --git a/substrate/network/src/import_queue.rs b/substrate/network/src/import_queue.rs deleted file mode 100644 index 647070383b4ce..0000000000000 --- a/substrate/network/src/import_queue.rs +++ /dev/null @@ -1,593 +0,0 @@ -// Copyright 2017 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 . - -//! Blocks import queue. - -use std::collections::{HashSet, VecDeque}; -use std::sync::{Arc, Weak}; -use std::sync::atomic::{AtomicBool, Ordering}; -use parking_lot::{Condvar, Mutex, RwLock}; - -use client::{BlockOrigin, ImportResult}; -use network_libp2p::{NodeIndex, Severity}; - -use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero}; - -use blocks::BlockData; -use chain::Client; -use error::{ErrorKind, Error}; -use protocol::Context; -use service::ExecuteInContext; -use sync::ChainSync; - -/// Blocks import queue API. -pub trait ImportQueue: Send + Sync { - /// Clear the queue when sync is restarting. - fn clear(&self); - /// Clears the import queue and stops importing. - fn stop(&self); - /// Get queue status. - fn status(&self) -> ImportQueueStatus; - /// Is block with given hash currently in the queue. - fn is_importing(&self, hash: &B::Hash) -> bool; - /// Import bunch of blocks. - fn import_blocks(&self, sync: &mut ChainSync, protocol: &mut Context, blocks: (BlockOrigin, Vec>)); -} - -/// Import queue status. It isn't completely accurate. -pub struct ImportQueueStatus { - /// Number of blocks that are currently in the queue. - pub importing_count: usize, - /// The number of the best block that was ever in the queue since start/last failure. - pub best_importing_number: <::Header as HeaderT>::Number, -} - -/// Blocks import queue that is importing blocks in the separate thread. -pub struct AsyncImportQueue { - handle: Mutex>>, - data: Arc>, -} - -/// Locks order: queue, queue_blocks, best_importing_number -struct AsyncImportQueueData { - signal: Condvar, - queue: Mutex>)>>, - queue_blocks: RwLock>, - best_importing_number: RwLock<<::Header as HeaderT>::Number>, - is_stopping: AtomicBool, -} - -impl AsyncImportQueue { - pub fn new() -> Self { - Self { - handle: Mutex::new(None), - data: Arc::new(AsyncImportQueueData::new()), - } - } - - pub fn start>(&self, sync: Weak>>, service: Weak, chain: Weak>) -> Result<(), Error> { - debug_assert!(self.handle.lock().is_none()); - - let qdata = self.data.clone(); - *self.handle.lock() = Some(::std::thread::Builder::new().name("ImportQueue".into()).spawn(move || { - import_thread(sync, service, chain, qdata) - }).map_err(|err| Error::from(ErrorKind::Io(err)))?); - Ok(()) - } -} - -impl AsyncImportQueueData { - pub fn new() -> Self { - Self { - signal: Default::default(), - queue: Mutex::new(VecDeque::new()), - queue_blocks: RwLock::new(HashSet::new()), - best_importing_number: RwLock::new(Zero::zero()), - is_stopping: Default::default(), - } - } -} - -impl ImportQueue for AsyncImportQueue { - fn clear(&self) { - let mut queue = self.data.queue.lock(); - let mut queue_blocks = self.data.queue_blocks.write(); - let mut best_importing_number = self.data.best_importing_number.write(); - queue_blocks.clear(); - queue.clear(); - *best_importing_number = Zero::zero(); - } - - fn stop(&self) { - self.clear(); - if let Some(handle) = self.handle.lock().take() { - self.data.is_stopping.store(true, Ordering::SeqCst); - self.data.signal.notify_one(); - - let _ = handle.join(); - } - } - - fn status(&self) -> ImportQueueStatus { - ImportQueueStatus { - importing_count: self.data.queue_blocks.read().len(), - best_importing_number: *self.data.best_importing_number.read(), - } - } - - fn is_importing(&self, hash: &B::Hash) -> bool { - self.data.queue_blocks.read().contains(hash) - } - - fn import_blocks(&self, _sync: &mut ChainSync, _protocol: &mut Context, blocks: (BlockOrigin, Vec>)) { - if blocks.1.is_empty() { - return; - } - - trace!(target:"sync", "Scheduling {} blocks for import", blocks.1.len()); - - let mut queue = self.data.queue.lock(); - let mut queue_blocks = self.data.queue_blocks.write(); - let mut best_importing_number = self.data.best_importing_number.write(); - let new_best_importing_number = blocks.1.last().and_then(|b| b.block.header.as_ref().map(|h| h.number().clone())).unwrap_or_else(|| Zero::zero()); - queue_blocks.extend(blocks.1.iter().map(|b| b.block.hash.clone())); - if new_best_importing_number > *best_importing_number { - *best_importing_number = new_best_importing_number; - } - queue.push_back(blocks); - self.data.signal.notify_one(); - } -} - -impl Drop for AsyncImportQueue { - fn drop(&mut self) { - self.stop(); - } -} - -/// Blocks import thread. -fn import_thread>(sync: Weak>>, service: Weak, chain: Weak>, qdata: Arc>) { - trace!(target: "sync", "Starting import thread"); - loop { - if qdata.is_stopping.load(Ordering::SeqCst) { - break; - } - - let new_blocks = { - let mut queue_lock = qdata.queue.lock(); - if queue_lock.is_empty() { - qdata.signal.wait(&mut queue_lock); - } - - match queue_lock.pop_front() { - Some(new_blocks) => new_blocks, - None => break, - } - }; - - match (sync.upgrade(), service.upgrade(), chain.upgrade()) { - (Some(sync), Some(service), Some(chain)) => { - let blocks_hashes: Vec = new_blocks.1.iter().map(|b| b.block.hash.clone()).collect(); - if !import_many_blocks(&mut SyncLink::Indirect(&sync, &*chain, &*service), Some(&*qdata), new_blocks) { - break; - } - - let mut queue_blocks = qdata.queue_blocks.write(); - for blocks_hash in blocks_hashes { - queue_blocks.remove(&blocks_hash); - } - }, - _ => break, - } - } - - trace!(target: "sync", "Stopping import thread"); -} - -/// ChainSync link trait. -trait SyncLinkApi { - /// Get chain reference. - fn chain(&self) -> &Client; - /// Block imported. - fn block_imported(&mut self, hash: &B::Hash, number: NumberFor); - /// Maintain sync. - fn maintain_sync(&mut self); - /// Disconnect from peer. - fn useless_peer(&mut self, who: NodeIndex, reason: &str); - /// Disconnect from peer and restart sync. - fn note_useless_and_restart_sync(&mut self, who: NodeIndex, reason: &str); - /// Restart sync. - fn restart(&mut self); -} - -/// Link with the ChainSync service. -enum SyncLink<'a, B: 'a + BlockT, E: 'a + ExecuteInContext> { - /// Indirect link (through service). - Indirect(&'a RwLock>, &'a Client, &'a E), - /// Direct references are given. - #[cfg(test)] - Direct(&'a mut ChainSync, &'a mut Context), -} - -/// Block import successful result. -#[derive(Debug, PartialEq)] -enum BlockImportResult { - /// Imported known block. - ImportedKnown(H, N), - /// Imported unknown block. - ImportedUnknown(H, N), -} - -/// Block import error. -#[derive(Debug, PartialEq)] -enum BlockImportError { - /// Disconnect from peer and continue import of next bunch of blocks. - Disconnect(NodeIndex), - /// Disconnect from peer and restart sync. - DisconnectAndRestart(NodeIndex), - /// Restart sync. - Restart, -} - -/// Import a bunch of blocks. -fn import_many_blocks<'a, B: BlockT>( - link: &mut SyncLinkApi, - qdata: Option<&AsyncImportQueueData>, - blocks: (BlockOrigin, Vec>) -) -> bool -{ - let (blocks_origin, blocks) = blocks; - let count = blocks.len(); - let mut imported = 0; - - let blocks_range = match ( - blocks.first().and_then(|b| b.block.header.as_ref().map(|h| h.number())), - blocks.last().and_then(|b| b.block.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); - - // Blocks in the response/drain should be in ascending order. - for block in blocks { - let import_result = import_single_block(link.chain(), blocks_origin.clone(), block); - let is_import_failed = import_result.is_err(); - imported += process_import_result(link, import_result); - if is_import_failed { - qdata.map(|qdata| *qdata.best_importing_number.write() = Zero::zero()); - return true; - } - - if qdata.map(|qdata| qdata.is_stopping.load(Ordering::SeqCst)).unwrap_or_default() { - return false; - } - } - - trace!(target: "sync", "Imported {} of {}", imported, count); - link.maintain_sync(); - true -} - -/// Single block import function. -fn import_single_block( - chain: &Client, - block_origin: BlockOrigin, - block: BlockData -) -> Result::Header as HeaderT>::Number>, BlockImportError> -{ - let origin = block.origin; - let block = block.block; - match (block.header, block.justification) { - (Some(header), Some(justification)) => { - let number = header.number().clone(); - let hash = header.hash(); - let parent = header.parent_hash().clone(); - - let result = chain.import( - block_origin, - header, - justification, - block.body, - ); - match result { - Ok(ImportResult::AlreadyInChain) => { - trace!(target: "sync", "Block already in chain {}: {:?}", number, hash); - Ok(BlockImportResult::ImportedKnown(hash, number)) - }, - Ok(ImportResult::AlreadyQueued) => { - trace!(target: "sync", "Block already queued {}: {:?}", number, hash); - Ok(BlockImportResult::ImportedKnown(hash, number)) - }, - Ok(ImportResult::Queued) => { - trace!(target: "sync", "Block queued {}: {:?}", number, hash); - Ok(BlockImportResult::ImportedUnknown(hash, number)) - }, - Ok(ImportResult::UnknownParent) => { - debug!(target: "sync", "Block with unknown parent {}: {:?}, parent: {:?}", number, hash, parent); - Err(BlockImportError::Restart) - }, - Ok(ImportResult::KnownBad) => { - debug!(target: "sync", "Peer gave us a bad block {}: {:?}", number, hash); - Err(BlockImportError::DisconnectAndRestart(origin)) //TODO: use persistent ID - } - Err(e) => { - debug!(target: "sync", "Error importing block {}: {:?}: {:?}", number, hash, e); - Err(BlockImportError::Restart) - } - } - }, - (None, _) => { - debug!(target: "sync", "Header {} was not provided by {} ", block.hash, origin); - Err(BlockImportError::Disconnect(origin)) //TODO: use persistent ID - }, - (_, None) => { - debug!(target: "sync", "Justification set for block {} was not provided by {} ", block.hash, origin); - Err(BlockImportError::Disconnect(origin)) //TODO: use persistent ID - } - } -} - -/// Process single block import result. -fn process_import_result<'a, B: BlockT>( - link: &mut SyncLinkApi, - result: Result::Header as HeaderT>::Number>, BlockImportError> -) -> usize -{ - match result { - Ok(BlockImportResult::ImportedKnown(hash, number)) => { - link.block_imported(&hash, number); - 1 - }, - Ok(BlockImportResult::ImportedUnknown(hash, number)) => { - link.block_imported(&hash, number); - 1 - }, - Err(BlockImportError::Disconnect(who)) => { - // TODO: FIXME: @arkpar BlockImport shouldn't be trying to manage the peer set. - // This should contain an actual reason. - link.useless_peer(who, "Import result was stated Disconnect"); - 0 - }, - Err(BlockImportError::DisconnectAndRestart(who)) => { - // TODO: FIXME: @arkpar BlockImport shouldn't be trying to manage the peer set. - // This should contain an actual reason. - link.note_useless_and_restart_sync(who, "Import result was stated DisconnectAndRestart"); - 0 - }, - Err(BlockImportError::Restart) => { - link.restart(); - 0 - }, - } -} - -impl<'a, B: 'static + BlockT, E: 'a + ExecuteInContext> SyncLink<'a, B, E> { - /// Execute closure with locked ChainSync. - fn with_sync, &mut Context)>(&mut self, closure: F) { - match *self { - #[cfg(test)] - SyncLink::Direct(ref mut sync, ref mut protocol) => - closure(*sync, *protocol), - SyncLink::Indirect(ref sync, _, ref service) => - service.execute_in_context(move |protocol| { - let mut sync = sync.write(); - closure(&mut *sync, protocol) - }), - } - } -} - -impl<'a, B: 'static + BlockT, E: ExecuteInContext> SyncLinkApi for SyncLink<'a, B, E> { - fn chain(&self) -> &Client { - match *self { - #[cfg(test)] - SyncLink::Direct(_, ref protocol) => protocol.client(), - SyncLink::Indirect(_, ref chain, _) => *chain, - } - } - - fn block_imported(&mut self, hash: &B::Hash, number: NumberFor) { - self.with_sync(|sync, _| sync.block_imported(&hash, number)) - } - - fn maintain_sync(&mut self) { - self.with_sync(|sync, protocol| sync.maintain_sync(protocol)) - } - - fn useless_peer(&mut self, who: NodeIndex, reason: &str) { - self.with_sync(|_, protocol| protocol.report_peer(who, Severity::Useless(reason))) - } - - fn note_useless_and_restart_sync(&mut self, who: NodeIndex, reason: &str) { - self.with_sync(|sync, protocol| { - protocol.report_peer(who, Severity::Useless(reason)); // is this actually malign or just useless? - sync.restart(protocol); - }) - } - - fn restart(&mut self) { - self.with_sync(|sync, protocol| sync.restart(protocol)) - } -} - -#[cfg(test)] -pub mod tests { - use client; - use message; - use test_client::{self, TestClient}; - use test_client::runtime::{Block, Hash}; - use on_demand::tests::DummyExecutor; - use runtime_primitives::generic::BlockId; - use super::*; - - /// Blocks import queue that is importing blocks in the same thread. - pub struct SyncImportQueue; - struct DummyExecuteInContext; - - impl ExecuteInContext for DummyExecuteInContext { - fn execute_in_context)>(&self, _closure: F) { } - } - - impl ImportQueue for SyncImportQueue { - fn clear(&self) { } - - fn stop(&self) { } - - fn status(&self) -> ImportQueueStatus { - ImportQueueStatus { - importing_count: 0, - best_importing_number: Zero::zero(), - } - } - - fn is_importing(&self, _hash: &B::Hash) -> bool { - false - } - - fn import_blocks(&self, sync: &mut ChainSync, protocol: &mut Context, blocks: (BlockOrigin, Vec>)) { - import_many_blocks(&mut SyncLink::Direct::<_, DummyExecuteInContext>(sync, protocol), None, blocks); - } - } - - struct TestLink { - chain: Arc>, - imported: usize, - maintains: usize, - disconnects: usize, - restarts: usize, - } - - impl TestLink { - fn new() -> TestLink { - TestLink { - chain: Arc::new(test_client::new()), - imported: 0, - maintains: 0, - disconnects: 0, - restarts: 0, - } - } - - fn total(&self) -> usize { - self.imported + self.maintains + self.disconnects + self.restarts - } - } - - impl SyncLinkApi for TestLink { - fn chain(&self) -> &Client { &*self.chain } - fn block_imported(&mut self, _hash: &Hash, _number: NumberFor) { self.imported += 1; } - fn maintain_sync(&mut self) { self.maintains += 1; } - fn useless_peer(&mut self, _: NodeIndex, _: &str) { self.disconnects += 1; } - fn note_useless_and_restart_sync(&mut self, _: NodeIndex, _: &str) { self.disconnects += 1; self.restarts += 1; } - fn restart(&mut self) { self.restarts += 1; } - } - - fn prepare_good_block() -> (client::Client, Hash, u64, BlockData) { - let client = test_client::new(); - let block = client.new_block().unwrap().bake().unwrap(); - client.justify_and_import(BlockOrigin::File, block).unwrap(); - - let (hash, number) = (client.block_hash(1).unwrap().unwrap(), 1); - let block = message::BlockData:: { - hash: client.block_hash(1).unwrap().unwrap(), - header: client.header(&BlockId::Number(1)).unwrap(), - body: None, - receipt: None, - message_queue: None, - justification: client.justification(&BlockId::Number(1)).unwrap(), - }; - - (client, hash, number, BlockData { block, origin: 0 }) - } - - #[test] - fn import_single_good_block_works() { - let (_, hash, number, block) = prepare_good_block(); - assert_eq!(import_single_block(&test_client::new(), BlockOrigin::File, block), Ok(BlockImportResult::ImportedUnknown(hash, number))); - } - - #[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), Ok(BlockImportResult::ImportedKnown(hash, number))); - } - - #[test] - fn import_single_good_block_without_header_fails() { - let (_, _, _, mut block) = prepare_good_block(); - block.block.header = None; - assert_eq!(import_single_block(&test_client::new(), BlockOrigin::File, block), Err(BlockImportError::Disconnect(0))); - } - - #[test] - fn import_single_good_block_without_justification_fails() { - let (_, _, _, mut block) = prepare_good_block(); - block.block.justification = None; - assert_eq!(import_single_block(&test_client::new(), BlockOrigin::File, block), Err(BlockImportError::Disconnect(0))); - } - - #[test] - fn process_import_result_works() { - let mut link = TestLink::new(); - assert_eq!(process_import_result::(&mut link, Ok(BlockImportResult::ImportedKnown(Default::default(), 0))), 1); - assert_eq!(link.total(), 1); - - let mut link = TestLink::new(); - assert_eq!(process_import_result::(&mut link, Ok(BlockImportResult::ImportedKnown(Default::default(), 0))), 1); - assert_eq!(link.total(), 1); - assert_eq!(link.imported, 1); - - let mut link = TestLink::new(); - assert_eq!(process_import_result::(&mut link, Ok(BlockImportResult::ImportedUnknown(Default::default(), 0))), 1); - assert_eq!(link.total(), 1); - assert_eq!(link.imported, 1); - - let mut link = TestLink::new(); - assert_eq!(process_import_result::(&mut link, Err(BlockImportError::Disconnect(0))), 0); - assert_eq!(link.total(), 1); - assert_eq!(link.disconnects, 1); - - let mut link = TestLink::new(); - assert_eq!(process_import_result::(&mut link, Err(BlockImportError::DisconnectAndRestart(0))), 0); - assert_eq!(link.total(), 2); - assert_eq!(link.disconnects, 1); - assert_eq!(link.restarts, 1); - - let mut link = TestLink::new(); - assert_eq!(process_import_result::(&mut link, Err(BlockImportError::Restart)), 0); - assert_eq!(link.total(), 1); - assert_eq!(link.restarts, 1); - } - - #[test] - fn import_many_blocks_stops_when_stopping() { - let (_, _, _, block) = prepare_good_block(); - let qdata = AsyncImportQueueData::new(); - qdata.is_stopping.store(true, Ordering::SeqCst); - assert!(!import_many_blocks(&mut TestLink::new(), Some(&qdata), (BlockOrigin::File, vec![block.clone(), block]))); - } - - #[test] - fn async_import_queue_drops() { - let queue = AsyncImportQueue::new(); - let service = Arc::new(DummyExecutor); - let chain = Arc::new(test_client::new()); - queue.start(Weak::new(), Arc::downgrade(&service), Arc::downgrade(&chain) as Weak>).unwrap(); - drop(queue); - } -} diff --git a/substrate/network/src/io.rs b/substrate/network/src/io.rs deleted file mode 100644 index 4a7d57b158955..0000000000000 --- a/substrate/network/src/io.rs +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2017 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 network_libp2p::{NetworkContext, Severity, NodeIndex, SessionInfo}; - -/// IO interface for the syncing handler. -/// Provides peer connection management and an interface to the blockchain client. -pub trait SyncIo { - /// Report a peer for misbehaviour. - fn report_peer(&mut self, who: NodeIndex, reason: Severity); - /// Send a packet to a peer. - fn send(&mut self, who: NodeIndex, data: Vec); - /// Returns peer identifier string - fn peer_info(&self, who: NodeIndex) -> String { - who.to_string() - } - /// Returns information on p2p session - fn peer_session_info(&self, who: NodeIndex) -> Option; - /// Check if the session is expired - fn is_expired(&self) -> bool; -} - -/// Wraps `NetworkContext` and the blockchain client -pub struct NetSyncIo<'s> { - network: &'s NetworkContext, -} - -impl<'s> NetSyncIo<'s> { - /// Creates a new instance from the `NetworkContext` and the blockchain client reference. - pub fn new(network: &'s NetworkContext) -> NetSyncIo<'s> { - NetSyncIo { - network: network, - } - } -} - -impl<'s> SyncIo for NetSyncIo<'s> { - fn report_peer(&mut self, who: NodeIndex, reason: Severity) { - self.network.report_peer(who, reason); - } - - fn send(&mut self, who: NodeIndex, data: Vec) { - self.network.send(who, 0, data) - } - - fn peer_session_info(&self, who: NodeIndex) -> Option { - self.network.session_info(who) - } - - fn is_expired(&self) -> bool { - self.network.is_expired() - } - - fn peer_info(&self, who: NodeIndex) -> String { - self.network.peer_client_version(who) - } -} - - diff --git a/substrate/network/src/lib.rs b/substrate/network/src/lib.rs deleted file mode 100644 index 14d3d82da5dbc..0000000000000 --- a/substrate/network/src/lib.rs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2017 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 . - -#![warn(unused_extern_crates)] -#![warn(missing_docs)] - -// tag::description[] -//! Substrate-specific P2P networking: synchronizing blocks, propagating BFT messages. -//! Allows attachment of an optional subprotocol for chain-specific requests. -// end::description[] - -extern crate ethcore_io as core_io; -extern crate linked_hash_map; -extern crate parking_lot; -extern crate substrate_primitives as primitives; -extern crate substrate_client as client; -extern crate substrate_runtime_primitives as runtime_primitives; -extern crate substrate_network_libp2p as network_libp2p; -extern crate substrate_codec as codec; -extern crate futures; -extern crate ed25519; -extern crate rustc_hex; -#[macro_use] extern crate log; -#[macro_use] extern crate bitflags; -#[macro_use] extern crate error_chain; -#[macro_use] extern crate substrate_codec_derive; - -#[cfg(test)] extern crate env_logger; -#[cfg(test)] extern crate substrate_keyring as keyring; -#[cfg(test)] extern crate substrate_test_client as test_client; - -mod service; -mod sync; -mod protocol; -mod io; -mod config; -mod chain; -mod blocks; -mod on_demand; -mod import_queue; -pub mod consensus_gossip; -pub mod error; -pub mod message; -pub mod specialization; - -#[cfg(test)] mod test; - -pub use chain::Client as ClientHandle; -pub use service::{Service, FetchFuture, ConsensusService, BftMessageStream, - TransactionPool, Params, ManageNetwork, SyncProvider}; -pub use protocol::{ProtocolStatus, PeerInfo, Context}; -pub use sync::{Status as SyncStatus, SyncState}; -pub use network_libp2p::{NonReservedPeerMode, NetworkConfiguration, NodeIndex, ProtocolId, ConnectionFilter, ConnectionDirection, Severity}; -pub use message::{generic as generic_message, RequestId, BftMessage, LocalizedBftMessage, ConsensusVote, SignedConsensusVote, SignedConsensusMessage, SignedConsensusProposal, Status as StatusMessage}; -pub use error::Error; -pub use config::{Roles, ProtocolConfig}; -pub use on_demand::{OnDemand, OnDemandService, RemoteResponse}; diff --git a/substrate/network/src/message.rs b/substrate/network/src/message.rs deleted file mode 100644 index 875529f0fe923..0000000000000 --- a/substrate/network/src/message.rs +++ /dev/null @@ -1,376 +0,0 @@ -// Copyright 2017 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 packet message types. These get serialized and put into the lower level protocol payload. - -use runtime_primitives::traits::{Block as BlockT, Header as HeaderT}; -use codec::{Encode, Decode, Input, Output}; -pub use self::generic::{ - BlockAnnounce, RemoteCallRequest, RemoteReadRequest, - RemoteHeaderRequest, RemoteHeaderResponse, ConsensusVote, - SignedConsensusVote, FromBlock -}; - -/// A unique ID of a request. -pub type RequestId = u64; - -/// Type alias for using the message type using block type parameters. -pub type Message = generic::Message< - B, - ::Header, - ::Hash, - <::Header as HeaderT>::Number, - ::Extrinsic, ->; - -/// Type alias for using the status type using block type parameters. -pub type Status = generic::Status< - ::Hash, - <::Header as HeaderT>::Number, ->; - -/// Type alias for using the block request type using block type parameters. -pub type BlockRequest = generic::BlockRequest< - ::Hash, - <::Header as HeaderT>::Number, ->; - -/// Type alias for using the localized bft message type using block type parameters. -pub type LocalizedBftMessage = generic::LocalizedBftMessage< - B, - ::Hash, ->; - -/// Type alias for using the BlockData type using block type parameters. -pub type BlockData = generic::BlockData< - ::Header, - ::Hash, - ::Extrinsic, ->; - -/// Type alias for using the BlockResponse type using block type parameters. -pub type BlockResponse = generic::BlockResponse< - ::Header, - ::Hash, - ::Extrinsic, ->; - -/// Type alias for using the BftMessage type using block type parameters. -pub type BftMessage = generic::BftMessage< - B, - ::Hash, ->; - -/// Type alias for using the SignedConsensusProposal type using block type parameters. -pub type SignedConsensusProposal = generic::SignedConsensusProposal< - B, - ::Hash, ->; - -/// Type alias for using the SignedConsensusProposal type using block type parameters. -pub type SignedConsensusMessage = generic::SignedConsensusProposal< - B, - ::Hash, ->; - -/// A set of transactions. -pub type Transactions = Vec; - -/// Bits of block data and associated artefacts to request. -bitflags! { - /// Node roles bitmask. - pub struct BlockAttributes: u8 { - /// Include block header. - const HEADER = 0b00000001; - /// Include block body. - const BODY = 0b00000010; - /// Include block receipt. - const RECEIPT = 0b00000100; - /// Include block message queue. - const MESSAGE_QUEUE = 0b00001000; - /// Include a justification for the block. - const JUSTIFICATION = 0b00010000; - } -} - -impl Encode for BlockAttributes { - fn encode_to(&self, dest: &mut T) { - dest.push_byte(self.bits()) - } -} - -impl Decode for BlockAttributes { - fn decode(input: &mut I) -> Option { - Self::from_bits(input.read_byte()?) - } -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Encode, Decode)] -/// Block enumeration direction. -pub enum Direction { - /// Enumerate in ascending order (from child to parent). - Ascending = 0, - /// Enumerate in descendfing order (from parent to canonical child). - Descending = 1, -} - -/// Remote call response. -#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] -pub struct RemoteCallResponse { - /// Id of a request this response was made for. - pub id: RequestId, - /// Execution proof. - pub proof: Vec>, -} - -#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] -/// Remote read response. -pub struct RemoteReadResponse { - /// Id of a request this response was made for. - pub id: RequestId, - /// Read proof. - pub proof: Vec>, -} - -/// Generic types. -pub mod generic { - use primitives::AuthorityId; - use runtime_primitives::bft::Justification; - use ed25519; - use service::Roles; - use super::{ - BlockAttributes, RemoteCallResponse, RemoteReadResponse, - RequestId, Transactions, Direction - }; - - /// Block data sent in the response. - #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] - pub struct BlockData { - /// Block header hash. - pub hash: Hash, - /// Block header if requested. - pub header: Option

, - /// Block body if requested. - pub body: Option>, - /// Block receipt if requested. - pub receipt: Option>, - /// Block message queue if requested. - pub message_queue: Option>, - /// Justification if requested. - pub justification: Option>, - } - - /// Identifies starting point of a block sequence. - #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] - pub enum FromBlock { - /// Start with given hash. - Hash(Hash), - /// Start with given block number. - Number(Number), - } - - /// Communication that can occur between participants in consensus. - #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] - pub enum BftMessage { - /// A consensus message (proposal or vote) - Consensus(SignedConsensusMessage), - /// Auxiliary communication (just proof-of-lock for now). - Auxiliary(Justification), - } - - /// BFT Consensus message with parent header hash attached to it. - #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] - pub struct LocalizedBftMessage { - /// Consensus message. - pub message: BftMessage, - /// Parent header hash. - pub parent_hash: Hash, - } - - /// A localized proposal message. Contains two signed pieces of data. - #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] - pub struct SignedConsensusProposal { - /// The round number. - pub round_number: u32, - /// The proposal sent. - pub proposal: Block, - /// The digest of the proposal. - pub digest: Hash, - /// The sender of the proposal - pub sender: AuthorityId, - /// The signature on the message (propose, round number, digest) - pub digest_signature: ed25519::Signature, - /// The signature on the message (propose, round number, proposal) - pub full_signature: ed25519::Signature, - } - - /// A localized vote message, including the sender. - #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] - pub struct SignedConsensusVote { - /// The message sent. - pub vote: ConsensusVote, - /// The sender of the message - pub sender: AuthorityId, - /// The signature of the message. - pub signature: ed25519::Signature, - } - - /// Votes during a consensus round. - #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] - pub enum ConsensusVote { - /// Prepare to vote for proposal with digest D. - Prepare(u32, H), - /// Commit to proposal with digest D.. - Commit(u32, H), - /// Propose advancement to a new round. - AdvanceRound(u32), - } - - /// A localized message. - #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] - pub enum SignedConsensusMessage { - /// A proposal. - Propose(SignedConsensusProposal), - /// A vote. - Vote(SignedConsensusVote), - } - - /// A network message. - #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] - pub enum Message { - /// Status packet. - Status(Status), - /// Block request. - BlockRequest(BlockRequest), - /// Block response. - BlockResponse(BlockResponse), - /// Block announce. - BlockAnnounce(BlockAnnounce
), - /// Transactions. - Transactions(Transactions), - /// BFT Consensus statement. - BftMessage(LocalizedBftMessage), - /// Remote method call request. - RemoteCallRequest(RemoteCallRequest), - /// Remote method call response. - RemoteCallResponse(RemoteCallResponse), - /// Remote storage read request. - RemoteReadRequest(RemoteReadRequest), - /// Remote storage read response. - RemoteReadResponse(RemoteReadResponse), - /// Remote header request. - RemoteHeaderRequest(RemoteHeaderRequest), - /// Remote header response. - RemoteHeaderResponse(RemoteHeaderResponse
), - /// Chain-specific message - #[codec(index = "255")] - ChainSpecific(Vec), - } - - /// Status sent on connection. - #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] - pub struct Status { - /// Protocol version. - pub version: u32, - /// Supported roles. - pub roles: Roles, - /// Best block number. - pub best_number: Number, - /// Best block hash. - pub best_hash: Hash, - /// Genesis block hash. - pub genesis_hash: Hash, - /// Chain-specific status. - pub chain_status: Vec, - } - - /// Request block data from a peer. - #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] - pub struct BlockRequest { - /// Unique request id. - pub id: RequestId, - /// Bits of block data to request. - pub fields: BlockAttributes, - /// Start from this block. - pub from: FromBlock, - /// End at this block. An implementation defined maximum is used when unspecified. - pub to: Option, - /// Sequence direction. - pub direction: Direction, - /// Maximum number of blocks to return. An implementation defined maximum is used when unspecified. - pub max: Option, - } - - /// Response to `BlockRequest` - #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] - pub struct BlockResponse { - /// Id of a request this response was made for. - pub id: RequestId, - /// Block data for the requested sequence. - pub blocks: Vec>, - } - - /// Announce a new complete relay chain block on the network. - #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] - pub struct BlockAnnounce { - /// New block header. - pub header: H, - } - - #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] - /// Remote call request. - pub struct RemoteCallRequest { - /// Unique request id. - pub id: RequestId, - /// Block at which to perform call. - pub block: H, - /// Method name. - pub method: String, - /// Call data. - pub data: Vec, - } - - #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] - /// Remote storage read request. - pub struct RemoteReadRequest { - /// Unique request id. - pub id: RequestId, - /// Block at which to perform call. - pub block: H, - /// Storage key. - pub key: Vec, - } - - #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] - /// Remote header request. - pub struct RemoteHeaderRequest { - /// Unique request id. - pub id: RequestId, - /// Block number to request header for. - pub block: N, - } - - #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] - /// Remote header response. - pub struct RemoteHeaderResponse
{ - /// Id of a request this response was made for. - pub id: RequestId, - /// Header. None if proof generation has failed (e.g. header is unknown). - pub header: Option
, - /// Header proof. - pub proof: Vec>, - } -} diff --git a/substrate/network/src/on_demand.rs b/substrate/network/src/on_demand.rs deleted file mode 100644 index 7883b5fa14360..0000000000000 --- a/substrate/network/src/on_demand.rs +++ /dev/null @@ -1,736 +0,0 @@ -// Copyright 2017 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 . - -//! On-demand requests service. - -use std::collections::VecDeque; -use std::sync::{Arc, Weak}; -use std::time::{Instant, Duration}; -use futures::{Async, Future, Poll}; -use futures::sync::oneshot::{channel, Receiver, Sender}; -use linked_hash_map::LinkedHashMap; -use linked_hash_map::Entry; -use parking_lot::Mutex; -use client; -use client::light::fetcher::{Fetcher, FetchChecker, RemoteHeaderRequest, - RemoteCallRequest, RemoteReadRequest}; -use io::SyncIo; -use message; -use network_libp2p::{Severity, NodeIndex}; -use service; -use runtime_primitives::traits::{Block as BlockT, Header as HeaderT}; - -/// Remote request timeout. -const REQUEST_TIMEOUT: Duration = Duration::from_secs(15); -/// Default request retry count. -const RETRY_COUNT: usize = 1; - -/// On-demand service API. -pub trait OnDemandService: Send + Sync { - /// When new node is connected. - fn on_connect(&self, peer: NodeIndex, role: service::Roles); - - /// When node is disconnected. - fn on_disconnect(&self, peer: NodeIndex); - - /// Maintain peers requests. - fn maintain_peers(&self, io: &mut SyncIo); - - /// When header response is received from remote node. - fn on_remote_header_response( - &self, - io: &mut SyncIo, - peer: NodeIndex, - response: message::RemoteHeaderResponse - ); - - /// When read response is received from remote node. - fn on_remote_read_response(&self, io: &mut SyncIo, peer: NodeIndex, response: message::RemoteReadResponse); - - /// When call response is received from remote node. - fn on_remote_call_response(&self, io: &mut SyncIo, peer: NodeIndex, response: message::RemoteCallResponse); -} - -/// On-demand requests service. Dispatches requests to appropriate peers. -pub struct OnDemand> { - core: Mutex>, - checker: Arc>, -} - -/// On-demand remote call response. -pub struct RemoteResponse { - receiver: Receiver>, -} - -#[derive(Default)] -struct OnDemandCore> { - service: Weak, - next_request_id: u64, - pending_requests: VecDeque>, - active_peers: LinkedHashMap>, - idle_peers: VecDeque, -} - -struct Request { - id: u64, - timestamp: Instant, - retry_count: usize, - data: RequestData, -} - -enum RequestData { - RemoteHeader(RemoteHeaderRequest, Sender>), - RemoteRead(RemoteReadRequest, Sender>, client::error::Error>>), - RemoteCall(RemoteCallRequest, Sender>), -} - -enum Accept { - Ok, - CheckFailed(client::error::Error, RequestData), - Unexpected(RequestData), -} - -impl Future for RemoteResponse { - type Item = T; - type Error = client::error::Error; - - fn poll(&mut self) -> Poll { - self.receiver.poll() - .map_err(|_| client::error::ErrorKind::RemoteFetchCancelled.into()) - .and_then(|r| match r { - Async::Ready(Ok(ready)) => Ok(Async::Ready(ready)), - Async::Ready(Err(error)) => Err(error), - Async::NotReady => Ok(Async::NotReady), - }) - } -} - -impl OnDemand where - E: service::ExecuteInContext, - B::Header: HeaderT, -{ - /// Creates new on-demand service. - pub fn new(checker: Arc>) -> Self { - OnDemand { - checker, - core: Mutex::new(OnDemandCore { - service: Weak::new(), - next_request_id: 0, - pending_requests: VecDeque::new(), - active_peers: LinkedHashMap::new(), - idle_peers: VecDeque::new(), - }) - } - } - - /// Sets weak reference to network service. - pub fn set_service_link(&self, service: Weak) { - self.core.lock().service = service; - } - - /// Schedule && dispatch all scheduled requests. - fn schedule_request(&self, retry_count: Option, data: RequestData, result: R) -> R { - let mut core = self.core.lock(); - core.insert(retry_count.unwrap_or(RETRY_COUNT), data); - core.dispatch(); - result - } - - /// Try to accept response from given peer. - fn accept_response) -> Accept>(&self, rtype: &str, io: &mut SyncIo, peer: NodeIndex, request_id: u64, try_accept: F) { - let mut core = self.core.lock(); - let request = match core.remove(peer, request_id) { - Some(request) => request, - None => { - io.report_peer(peer, Severity::Bad(&format!("Invalid remote {} response from peer", rtype))); - core.remove_peer(peer); - return; - }, - }; - - let retry_count = request.retry_count; - let (retry_count, retry_request_data) = match try_accept(request) { - Accept::Ok => (retry_count, None), - Accept::CheckFailed(error, retry_request_data) => { - io.report_peer(peer, Severity::Bad(&format!("Failed to check remote {} response from peer: {}", rtype, error))); - core.remove_peer(peer); - - if retry_count > 0 { - (retry_count - 1, Some(retry_request_data)) - } else { - trace!(target: "sync", "Failed to get remote {} response for given number of retries", rtype); - retry_request_data.fail(client::error::ErrorKind::RemoteFetchFailed.into()); - (0, None) - } - }, - Accept::Unexpected(retry_request_data) => { - io.report_peer(peer, Severity::Bad(&format!("Unexpected response to remote {} from peer", rtype))); - core.remove_peer(peer); - - (retry_count, Some(retry_request_data)) - }, - }; - - if let Some(request_data) = retry_request_data { - core.insert(retry_count, request_data); - } - - core.dispatch(); - } -} - -impl OnDemandService for OnDemand where - B: BlockT, - E: service::ExecuteInContext, - B::Header: HeaderT, -{ - fn on_connect(&self, peer: NodeIndex, role: service::Roles) { - if !role.intersects(service::Roles::FULL | service::Roles::AUTHORITY) { // TODO: correct? - return; - } - - let mut core = self.core.lock(); - core.add_peer(peer); - core.dispatch(); - } - - fn on_disconnect(&self, peer: NodeIndex) { - let mut core = self.core.lock(); - core.remove_peer(peer); - core.dispatch(); - } - - fn maintain_peers(&self, io: &mut SyncIo) { - let mut core = self.core.lock(); - for bad_peer in core.maintain_peers() { - io.report_peer(bad_peer, Severity::Timeout); - } - core.dispatch(); - } - - fn on_remote_header_response(&self, io: &mut SyncIo, peer: NodeIndex, response: message::RemoteHeaderResponse) { - self.accept_response("header", io, peer, response.id, |request| match request.data { - RequestData::RemoteHeader(request, sender) => match self.checker.check_header_proof(&request, response.header, response.proof) { - Ok(response) => { - // we do not bother if receiver has been dropped already - let _ = sender.send(Ok(response)); - Accept::Ok - }, - Err(error) => Accept::CheckFailed(error, RequestData::RemoteHeader(request, sender)), - }, - data @ _ => Accept::Unexpected(data), - }) - } - - fn on_remote_read_response(&self, io: &mut SyncIo, peer: NodeIndex, response: message::RemoteReadResponse) { - self.accept_response("read", io, peer, response.id, |request| match request.data { - RequestData::RemoteRead(request, sender) => match self.checker.check_read_proof(&request, response.proof) { - Ok(response) => { - // we do not bother if receiver has been dropped already - let _ = sender.send(Ok(response)); - Accept::Ok - }, - Err(error) => Accept::CheckFailed(error, RequestData::RemoteRead(request, sender)), - }, - data @ _ => Accept::Unexpected(data), - }) - } - - fn on_remote_call_response(&self, io: &mut SyncIo, peer: NodeIndex, response: message::RemoteCallResponse) { - self.accept_response("call", io, peer, response.id, |request| match request.data { - RequestData::RemoteCall(request, sender) => match self.checker.check_execution_proof(&request, response.proof) { - Ok(response) => { - // we do not bother if receiver has been dropped already - let _ = sender.send(Ok(response)); - Accept::Ok - }, - Err(error) => Accept::CheckFailed(error, RequestData::RemoteCall(request, sender)), - }, - data @ _ => Accept::Unexpected(data), - }) - } -} - -impl Fetcher for OnDemand where - B: BlockT, - E: service::ExecuteInContext, - B::Header: HeaderT, -{ - type RemoteHeaderResult = RemoteResponse; - type RemoteReadResult = RemoteResponse>>; - type RemoteCallResult = RemoteResponse; - - fn remote_header(&self, request: RemoteHeaderRequest) -> Self::RemoteHeaderResult { - let (sender, receiver) = channel(); - self.schedule_request(request.retry_count.clone(), RequestData::RemoteHeader(request, sender), - RemoteResponse { receiver }) - } - - fn remote_read(&self, request: RemoteReadRequest) -> Self::RemoteReadResult { - let (sender, receiver) = channel(); - self.schedule_request(request.retry_count.clone(), RequestData::RemoteRead(request, sender), - RemoteResponse { receiver }) - } - - fn remote_call(&self, request: RemoteCallRequest) -> Self::RemoteCallResult { - let (sender, receiver) = channel(); - self.schedule_request(request.retry_count.clone(), RequestData::RemoteCall(request, sender), - RemoteResponse { receiver }) - } -} - -impl OnDemandCore where - B: BlockT, - E: service::ExecuteInContext, - B::Header: HeaderT, -{ - pub fn add_peer(&mut self, peer: NodeIndex) { - self.idle_peers.push_back(peer); - } - - pub fn remove_peer(&mut self, peer: NodeIndex) { - if let Some(request) = self.active_peers.remove(&peer) { - self.pending_requests.push_front(request); - return; - } - - if let Some(idle_index) = self.idle_peers.iter().position(|i| *i == peer) { - self.idle_peers.swap_remove_back(idle_index); - } - } - - pub fn maintain_peers(&mut self) -> Vec { - let now = Instant::now(); - let mut bad_peers = Vec::new(); - loop { - match self.active_peers.front() { - Some((_, request)) if now - request.timestamp >= REQUEST_TIMEOUT => (), - _ => return bad_peers, - } - - let (bad_peer, request) = self.active_peers.pop_front().expect("front() is Some as checked above"); - self.pending_requests.push_front(request); - bad_peers.push(bad_peer); - } - } - - pub fn insert(&mut self, retry_count: usize, data: RequestData) { - let request_id = self.next_request_id; - self.next_request_id += 1; - - self.pending_requests.push_back(Request { - id: request_id, - timestamp: Instant::now(), - retry_count, - data, - }); - } - - pub fn remove(&mut self, peer: NodeIndex, id: u64) -> Option> { - match self.active_peers.entry(peer) { - Entry::Occupied(entry) => match entry.get().id == id { - true => { - self.idle_peers.push_back(peer); - Some(entry.remove()) - }, - false => None, - }, - Entry::Vacant(_) => None, - } - } - - pub fn dispatch(&mut self) { - let service = match self.service.upgrade() { - Some(service) => service, - None => return, - }; - - while !self.pending_requests.is_empty() { - let peer = match self.idle_peers.pop_front() { - Some(peer) => peer, - None => return, - }; - - let mut request = self.pending_requests.pop_front().expect("checked in loop condition; qed"); - request.timestamp = Instant::now(); - trace!(target: "sync", "Dispatching remote request {} to peer {}", request.id, peer); - - service.execute_in_context(|ctx| ctx.send_message(peer, request.message())); - self.active_peers.insert(peer, request); - } - } -} - -impl Request { - pub fn message(&self) -> message::Message { - match self.data { - RequestData::RemoteHeader(ref data, _) => message::generic::Message::RemoteHeaderRequest( - message::RemoteHeaderRequest { - id: self.id, - block: data.block, - }), - RequestData::RemoteRead(ref data, _) => message::generic::Message::RemoteReadRequest( - message::RemoteReadRequest { - id: self.id, - block: data.block, - key: data.key.clone(), - }), - RequestData::RemoteCall(ref data, _) => message::generic::Message::RemoteCallRequest( - message::RemoteCallRequest { - id: self.id, - block: data.block, - method: data.method.clone(), - data: data.call_data.clone(), - }), - } - } -} - -impl RequestData { - pub fn fail(self, error: client::error::Error) { - // don't care if anyone is listening - match self { - RequestData::RemoteHeader(_, sender) => { let _ = sender.send(Err(error)); }, - RequestData::RemoteCall(_, sender) => { let _ = sender.send(Err(error)); }, - RequestData::RemoteRead(_, sender) => { let _ = sender.send(Err(error)); }, - } - } -} - -#[cfg(test)] -pub mod tests { - use std::collections::VecDeque; - use std::sync::Arc; - use std::time::Instant; - use futures::Future; - use parking_lot::RwLock; - use client; - use client::light::fetcher::{Fetcher, FetchChecker, RemoteHeaderRequest, - RemoteCallRequest, RemoteReadRequest}; - use message; - use network_libp2p::NodeIndex; - use service::{Roles, ExecuteInContext}; - use test::TestIo; - use super::{REQUEST_TIMEOUT, OnDemand, OnDemandService}; - use test_client::runtime::{Block, Header}; - - pub struct DummyExecutor; - struct DummyFetchChecker { ok: bool } - - impl ExecuteInContext for DummyExecutor { - fn execute_in_context)>(&self, _closure: F) {} - } - - impl FetchChecker for DummyFetchChecker { - fn check_header_proof( - &self, - _request: &RemoteHeaderRequest
, - header: Option
, - _remote_proof: Vec> - ) -> client::error::Result
{ - match self.ok { - true if header.is_some() => Ok(header.unwrap()), - _ => Err(client::error::ErrorKind::Backend("Test error".into()).into()), - } - } - - fn check_read_proof(&self, _request: &RemoteReadRequest
, _remote_proof: Vec>) -> client::error::Result>> { - match self.ok { - true => Ok(Some(vec![42])), - false => Err(client::error::ErrorKind::Backend("Test error".into()).into()), - } - } - - fn check_execution_proof(&self, _request: &RemoteCallRequest
, _remote_proof: Vec>) -> client::error::Result { - match self.ok { - true => Ok(client::CallResult { - return_data: vec![42], - changes: Default::default(), - }), - false => Err(client::error::ErrorKind::Backend("Test error".into()).into()), - } - } - } - - fn dummy(ok: bool) -> (Arc, Arc>) { - let executor = Arc::new(DummyExecutor); - let service = Arc::new(OnDemand::new(Arc::new(DummyFetchChecker { ok }))); - service.set_service_link(Arc::downgrade(&executor)); - (executor, service) - } - - fn total_peers(on_demand: &OnDemand) -> usize { - let core = on_demand.core.lock(); - core.idle_peers.len() + core.active_peers.len() - } - - fn receive_call_response(on_demand: &OnDemand, network: &mut TestIo, peer: NodeIndex, id: message::RequestId) { - on_demand.on_remote_call_response(network, peer, message::RemoteCallResponse { - id: id, - proof: vec![vec![2]], - }); - } - - fn dummy_header() -> Header { - Header { - parent_hash: Default::default(), - number: 0, - state_root: Default::default(), - extrinsics_root: Default::default(), - digest: Default::default(), - } - } - - #[test] - fn knows_about_peers_roles() { - let (_, on_demand) = dummy(true); - on_demand.on_connect(0, Roles::LIGHT); - on_demand.on_connect(1, Roles::FULL); - on_demand.on_connect(2, Roles::AUTHORITY); - assert_eq!(vec![1, 2], on_demand.core.lock().idle_peers.iter().cloned().collect::>()); - } - - #[test] - fn disconnects_from_idle_peer() { - let (_, on_demand) = dummy(true); - on_demand.on_connect(0, Roles::FULL); - assert_eq!(1, total_peers(&*on_demand)); - on_demand.on_disconnect(0); - assert_eq!(0, total_peers(&*on_demand)); - } - - #[test] - fn disconnects_from_timeouted_peer() { - let (_x, on_demand) = dummy(true); - let queue = RwLock::new(VecDeque::new()); - let mut network = TestIo::new(&queue, None); - - on_demand.on_connect(0, Roles::FULL); - on_demand.on_connect(1, Roles::FULL); - assert_eq!(vec![0, 1], on_demand.core.lock().idle_peers.iter().cloned().collect::>()); - assert!(on_demand.core.lock().active_peers.is_empty()); - - on_demand.remote_call(RemoteCallRequest { - block: Default::default(), - header: dummy_header(), - method: "test".into(), - call_data: vec![], - retry_count: None, - }); - assert_eq!(vec![1], on_demand.core.lock().idle_peers.iter().cloned().collect::>()); - assert_eq!(vec![0], on_demand.core.lock().active_peers.keys().cloned().collect::>()); - - on_demand.core.lock().active_peers[&0].timestamp = Instant::now() - REQUEST_TIMEOUT - REQUEST_TIMEOUT; - on_demand.maintain_peers(&mut network); - assert!(on_demand.core.lock().idle_peers.is_empty()); - assert_eq!(vec![1], on_demand.core.lock().active_peers.keys().cloned().collect::>()); - assert!(network.to_disconnect.contains(&0)); - } - - #[test] - fn disconnects_from_peer_on_response_with_wrong_id() { - let (_x, on_demand) = dummy(true); - let queue = RwLock::new(VecDeque::new()); - let mut network = TestIo::new(&queue, None); - on_demand.on_connect(0, Roles::FULL); - - on_demand.remote_call(RemoteCallRequest { - block: Default::default(), - header: dummy_header(), - method: "test".into(), - call_data: vec![], - retry_count: None, - }); - receive_call_response(&*on_demand, &mut network, 0, 1); - assert!(network.to_disconnect.contains(&0)); - assert_eq!(on_demand.core.lock().pending_requests.len(), 1); - } - - #[test] - fn disconnects_from_peer_on_incorrect_response() { - let (_x, on_demand) = dummy(false); - let queue = RwLock::new(VecDeque::new()); - let mut network = TestIo::new(&queue, None); - on_demand.remote_call(RemoteCallRequest { - block: Default::default(), - header: dummy_header(), - method: "test".into(), - call_data: vec![], - retry_count: Some(1), - }); - - on_demand.on_connect(0, Roles::FULL); - receive_call_response(&*on_demand, &mut network, 0, 0); - assert!(network.to_disconnect.contains(&0)); - assert_eq!(on_demand.core.lock().pending_requests.len(), 1); - } - - #[test] - fn disconnects_from_peer_on_unexpected_response() { - let (_x, on_demand) = dummy(true); - let queue = RwLock::new(VecDeque::new()); - let mut network = TestIo::new(&queue, None); - on_demand.on_connect(0, Roles::FULL); - - receive_call_response(&*on_demand, &mut network, 0, 0); - assert!(network.to_disconnect.contains(&0)); - } - - #[test] - fn disconnects_from_peer_on_wrong_response_type() { - let (_x, on_demand) = dummy(false); - let queue = RwLock::new(VecDeque::new()); - let mut network = TestIo::new(&queue, None); - on_demand.on_connect(0, Roles::FULL); - - on_demand.remote_call(RemoteCallRequest { - block: Default::default(), - header: dummy_header(), - method: "test".into(), - call_data: vec![], - retry_count: Some(1), - }); - - on_demand.on_remote_read_response(&mut network, 0, message::RemoteReadResponse { - id: 0, - proof: vec![vec![2]], - }); - assert!(network.to_disconnect.contains(&0)); - assert_eq!(on_demand.core.lock().pending_requests.len(), 1); - } - - #[test] - fn receives_remote_failure_after_retry_count_failures() { - use parking_lot::{Condvar, Mutex}; - - let retry_count = 2; - let (_x, on_demand) = dummy(false); - let queue = RwLock::new(VecDeque::new()); - let mut network = TestIo::new(&queue, None); - for i in 0..retry_count+1 { - on_demand.on_connect(i, Roles::FULL); - } - - let sync = Arc::new((Mutex::new(0), Mutex::new(0), Condvar::new())); - let thread_sync = sync.clone(); - - let response = on_demand.remote_call(RemoteCallRequest { - block: Default::default(), - header: dummy_header(), - method: "test".into(), - call_data: vec![], - retry_count: Some(retry_count) - }); - let thread = ::std::thread::spawn(move || { - let &(ref current, ref finished_at, ref finished) = &*thread_sync; - let _ = response.wait().unwrap_err(); - *finished_at.lock() = *current.lock(); - finished.notify_one(); - }); - - let &(ref current, ref finished_at, ref finished) = &*sync; - for i in 0..retry_count+1 { - let mut current = current.lock(); - *current = *current + 1; - receive_call_response(&*on_demand, &mut network, i, i as u64); - } - - let mut finished_at = finished_at.lock(); - assert!(!finished.wait_for(&mut finished_at, ::std::time::Duration::from_millis(1000)).timed_out()); - assert_eq!(*finished_at, retry_count + 1); - - thread.join().unwrap(); - } - - #[test] - fn receives_remote_call_response() { - let (_x, on_demand) = dummy(true); - let queue = RwLock::new(VecDeque::new()); - let mut network = TestIo::new(&queue, None); - on_demand.on_connect(0, Roles::FULL); - - let response = on_demand.remote_call(RemoteCallRequest { - block: Default::default(), - header: dummy_header(), - method: "test".into(), - call_data: vec![], - retry_count: None, - }); - let thread = ::std::thread::spawn(move || { - let result = response.wait().unwrap(); - assert_eq!(result.return_data, vec![42]); - }); - - receive_call_response(&*on_demand, &mut network, 0, 0); - thread.join().unwrap(); - } - - #[test] - fn receives_remote_read_response() { - let (_x, on_demand) = dummy(true); - let queue = RwLock::new(VecDeque::new()); - let mut network = TestIo::new(&queue, None); - on_demand.on_connect(0, Roles::FULL); - - let response = on_demand.remote_read(RemoteReadRequest { - header: dummy_header(), - block: Default::default(), - key: b":key".to_vec(), - retry_count: None, - }); - let thread = ::std::thread::spawn(move || { - let result = response.wait().unwrap(); - assert_eq!(result, Some(vec![42])); - }); - - on_demand.on_remote_read_response(&mut network, 0, message::RemoteReadResponse { - id: 0, - proof: vec![vec![2]], - }); - thread.join().unwrap(); - } - - #[test] - fn receives_remote_header_response() { - let (_x, on_demand) = dummy(true); - let queue = RwLock::new(VecDeque::new()); - let mut network = TestIo::new(&queue, None); - on_demand.on_connect(0, Roles::FULL); - - let response = on_demand.remote_header(RemoteHeaderRequest { - cht_root: Default::default(), - block: 1, - retry_count: None, - }); - let thread = ::std::thread::spawn(move || { - let result = response.wait().unwrap(); - assert_eq!(result.hash(), "80729accb7bb10ff9c637a10e8bb59f21c52571aa7b46544c5885ca89ed190f4".into()); - }); - - on_demand.on_remote_header_response(&mut network, 0, message::RemoteHeaderResponse { - id: 0, - header: Some(Header { - parent_hash: Default::default(), - number: 1, - state_root: Default::default(), - extrinsics_root: Default::default(), - digest: Default::default(), - }), - proof: vec![vec![2]], - }); - thread.join().unwrap(); - } -} diff --git a/substrate/network/src/protocol.rs b/substrate/network/src/protocol.rs deleted file mode 100644 index 8866a167a2855..0000000000000 --- a/substrate/network/src/protocol.rs +++ /dev/null @@ -1,679 +0,0 @@ -// Copyright 2017 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::collections::{HashMap, HashSet}; -use std::{mem, cmp}; -use std::sync::Arc; -use std::time; -use parking_lot::RwLock; -use rustc_hex::ToHex; -use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, Hash, HashFor, NumberFor, As}; -use runtime_primitives::generic::BlockId; -use network_libp2p::{NodeIndex, Severity}; -use codec::{Encode, Decode}; - -use message::{self, Message}; -use message::generic::Message as GenericMessage; -use specialization::Specialization; -use sync::{ChainSync, Status as SyncStatus, SyncState}; -use service::{Roles, TransactionPool, ExHashT}; -use import_queue::ImportQueue; -use config::ProtocolConfig; -use chain::Client; -use on_demand::OnDemandService; -use io::SyncIo; -use error; - -const REQUEST_TIMEOUT_SEC: u64 = 40; - -/// Current protocol version. -pub (crate) const CURRENT_VERSION: u32 = 1; -/// Current packet count. -pub (crate) const CURRENT_PACKET_COUNT: u8 = 1; - -// Maximum allowed entries in `BlockResponse` -const MAX_BLOCK_DATA_RESPONSE: u32 = 128; - -// Lock must always be taken in order declared here. -pub struct Protocol, H: ExHashT> { - config: ProtocolConfig, - on_demand: Option>>, - genesis_hash: B::Hash, - sync: Arc>>, - specialization: RwLock, - context_data: ContextData, - // Connected peers pending Status message. - handshaking_peers: RwLock>, - transaction_pool: Arc>, -} -/// Syncing status and statistics -#[derive(Clone)] -pub struct ProtocolStatus { - /// Sync status. - pub sync: SyncStatus, - /// Total number of connected peers - pub num_peers: usize, - /// Total number of active peers. - pub num_active_peers: usize, -} - -/// Peer information -struct Peer { - /// Protocol version - protocol_version: u32, - /// Roles - roles: Roles, - /// Peer best block hash - best_hash: B::Hash, - /// Peer best block number - best_number: ::Number, - /// Pending block request if any - block_request: Option>, - /// Request timestamp - request_timestamp: Option, - /// Holds a set of transactions known to this peer. - known_extrinsics: HashSet, - /// Holds a set of blocks known to this peer. - known_blocks: HashSet, - /// Request counter, - next_request_id: message::RequestId, -} - -/// Info about a peer's known state. -#[derive(Debug)] -pub struct PeerInfo { - /// Roles - pub roles: Roles, - /// Protocol version - pub protocol_version: u32, - /// Peer best block hash - pub best_hash: B::Hash, - /// Peer best block number - pub best_number: ::Number, -} - -/// Context for a network-specific handler. -pub trait Context { - /// Get a reference to the client. - fn client(&self) -> &::chain::Client; - - /// Point out that a peer has been malign or irresponsible or appeared lazy. - fn report_peer(&mut self, who: NodeIndex, reason: Severity); - - /// Get peer info. - fn peer_info(&self, peer: NodeIndex) -> Option>; - - /// Send a message to a peer. - fn send_message(&mut self, who: NodeIndex, data: ::message::Message); -} - -/// Protocol context. -pub(crate) struct ProtocolContext<'a, B: 'a + BlockT, H: 'a + ExHashT> { - io: &'a mut SyncIo, - context_data: &'a ContextData, -} - -impl<'a, B: BlockT + 'a, H: 'a + ExHashT> ProtocolContext<'a, B, H> { - pub(crate) fn new(context_data: &'a ContextData, io: &'a mut SyncIo) -> Self { - ProtocolContext { - io, - context_data, - } - } - - /// Send a message to a peer. - pub fn send_message(&mut self, who: NodeIndex, message: Message) { - send_message(&self.context_data.peers, self.io, who, message) - } - - /// Point out that a peer has been malign or irresponsible or appeared lazy. - pub fn report_peer(&mut self, who: NodeIndex, reason: Severity) { - self.io.report_peer(who, reason); - } - - /// Get peer info. - pub fn peer_info(&self, peer: NodeIndex) -> Option> { - self.context_data.peers.read().get(&peer).map(|p| { - PeerInfo { - roles: p.roles, - protocol_version: p.protocol_version, - best_hash: p.best_hash, - best_number: p.best_number, - } - }) - } -} - -impl<'a, B: BlockT + 'a, H: ExHashT + 'a> Context for ProtocolContext<'a, B, H> { - fn send_message(&mut self, who: NodeIndex, message: Message) { - ProtocolContext::send_message(self, who, message); - } - - fn report_peer(&mut self, who: NodeIndex, reason: Severity) { - ProtocolContext::report_peer(self, who, reason); - } - - fn peer_info(&self, who: NodeIndex) -> Option> { - ProtocolContext::peer_info(self, who) - } - - fn client(&self) -> &Client { - &*self.context_data.chain - } -} - -/// Data necessary to create a context. -pub(crate) struct ContextData { - // All connected peers - peers: RwLock>>, - chain: Arc>, -} - -impl, H: ExHashT> Protocol { - /// Create a new instance. - pub fn new( - config: ProtocolConfig, - chain: Arc>, - import_queue: Arc>, - on_demand: Option>>, - transaction_pool: Arc>, - specialization: S, - ) -> error::Result { - let info = chain.info()?; - let sync = ChainSync::new(config.roles, &info, import_queue); - let protocol = Protocol { - config: config, - context_data: ContextData { - peers: RwLock::new(HashMap::new()), - chain, - }, - on_demand, - genesis_hash: info.chain.genesis_hash, - sync: Arc::new(RwLock::new(sync)), - specialization: RwLock::new(specialization), - handshaking_peers: RwLock::new(HashMap::new()), - transaction_pool: transaction_pool, - }; - Ok(protocol) - } - - pub(crate) fn context_data(&self) -> &ContextData { - &self.context_data - } - - pub(crate) fn sync(&self) -> &Arc>> { - &self.sync - } - - /// Returns protocol status - pub fn status(&self) -> ProtocolStatus { - let sync = self.sync.read(); - let peers = self.context_data.peers.read(); - ProtocolStatus { - sync: sync.status(), - num_peers: peers.values().count(), - num_active_peers: peers.values().filter(|p| p.block_request.is_some()).count(), - } - } - - pub fn handle_packet(&self, io: &mut SyncIo, who: NodeIndex, mut data: &[u8]) { - let message: Message = match Decode::decode(&mut data) { - Some(m) => m, - None => { - trace!(target: "sync", "Invalid packet from {}", who); - io.report_peer(who, Severity::Bad("Peer sent us a packet with invalid format")); - return; - } - }; - - match message { - GenericMessage::Status(s) => self.on_status_message(io, who, s), - GenericMessage::BlockRequest(r) => self.on_block_request(io, who, r), - GenericMessage::BlockResponse(r) => { - let request = { - let mut peers = self.context_data.peers.write(); - if let Some(ref mut peer) = peers.get_mut(&who) { - peer.request_timestamp = None; - match mem::replace(&mut peer.block_request, None) { - Some(r) => r, - None => { - io.report_peer(who, Severity::Bad("Unexpected response packet received from peer")); - return; - } - } - } else { - io.report_peer(who, Severity::Bad("Unexpected packet received from peer")); - return; - } - }; - if request.id != r.id { - trace!(target: "sync", "Ignoring mismatched response packet from {} (expected {} got {})", who, request.id, r.id); - return; - } - self.on_block_response(io, who, request, r); - }, - GenericMessage::BlockAnnounce(announce) => self.on_block_announce(io, who, announce), - GenericMessage::Transactions(m) => self.on_extrinsics(io, who, m), - GenericMessage::RemoteCallRequest(request) => self.on_remote_call_request(io, who, request), - GenericMessage::RemoteCallResponse(response) => self.on_remote_call_response(io, who, response), - GenericMessage::RemoteReadRequest(request) => self.on_remote_read_request(io, who, request), - GenericMessage::RemoteReadResponse(response) => self.on_remote_read_response(io, who, response), - GenericMessage::RemoteHeaderRequest(request) => self.on_remote_header_request(io, who, request), - GenericMessage::RemoteHeaderResponse(response) => self.on_remote_header_response(io, who, response), - other => self.specialization.write().on_message(&mut ProtocolContext::new(&self.context_data, io), who, other), - } - } - - pub fn send_message(&self, io: &mut SyncIo, who: NodeIndex, message: Message) { - send_message::(&self.context_data.peers, io, who, message) - } - - /// Called when a new peer is connected - pub fn on_peer_connected(&self, io: &mut SyncIo, who: NodeIndex) { - trace!(target: "sync", "Connected {}: {}", who, io.peer_info(who)); - self.handshaking_peers.write().insert(who, time::Instant::now()); - self.send_status(io, who); - } - - /// Called by peer when it is disconnecting - pub fn on_peer_disconnected(&self, io: &mut SyncIo, peer: NodeIndex) { - trace!(target: "sync", "Disconnecting {}: {}", peer, io.peer_info(peer)); - - // lock all the the peer lists so that add/remove peer events are in order - let mut sync = self.sync.write(); - let mut spec = self.specialization.write(); - - let removed = { - let mut peers = self.context_data.peers.write(); - let mut handshaking_peers = self.handshaking_peers.write(); - handshaking_peers.remove(&peer); - peers.remove(&peer).is_some() - }; - if removed { - let mut context = ProtocolContext::new(&self.context_data, io); - sync.peer_disconnected(&mut context, peer); - spec.on_disconnect(&mut context, peer); - self.on_demand.as_ref().map(|s| s.on_disconnect(peer)); - } - } - - fn on_block_request(&self, io: &mut SyncIo, peer: NodeIndex, request: message::BlockRequest) { - trace!(target: "sync", "BlockRequest {} from {}: from {:?} to {:?} max {:?}", request.id, peer, request.from, request.to, request.max); - let mut blocks = Vec::new(); - let mut id = match request.from { - message::FromBlock::Hash(h) => BlockId::Hash(h), - message::FromBlock::Number(n) => BlockId::Number(n), - }; - let max = cmp::min(request.max.unwrap_or(u32::max_value()), MAX_BLOCK_DATA_RESPONSE) as usize; - // TODO: receipts, etc. - let get_header = request.fields.contains(message::BlockAttributes::HEADER); - let get_body = request.fields.contains(message::BlockAttributes::BODY); - let get_justification = request.fields.contains(message::BlockAttributes::JUSTIFICATION); - while let Some(header) = self.context_data.chain.header(&id).unwrap_or(None) { - if blocks.len() >= max { - break; - } - let number = header.number().clone(); - let hash = header.hash(); - let justification = if get_justification { self.context_data.chain.justification(&BlockId::Hash(hash)).unwrap_or(None) } else { None }; - let block_data = message::generic::BlockData { - hash: hash, - header: if get_header { Some(header) } else { None }, - body: if get_body { self.context_data.chain.body(&BlockId::Hash(hash)).unwrap_or(None) } else { None }, - receipt: None, - message_queue: None, - justification, - }; - blocks.push(block_data); - match request.direction { - message::Direction::Ascending => id = BlockId::Number(number + As::sa(1)), - message::Direction::Descending => { - if number == As::sa(0) { - break; - } - id = BlockId::Number(number - As::sa(1)) - } - } - } - let response = message::generic::BlockResponse { - id: request.id, - blocks: blocks, - }; - trace!(target: "sync", "Sending BlockResponse with {} blocks", response.blocks.len()); - self.send_message(io, peer, GenericMessage::BlockResponse(response)) - } - - fn on_block_response(&self, io: &mut SyncIo, peer: NodeIndex, request: message::BlockRequest, response: message::BlockResponse) { - // TODO: validate response - let blocks_range = match ( - response.blocks.first().and_then(|b| b.header.as_ref().map(|h| h.number())), - response.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", "BlockResponse {} from {} with {} blocks{}", - response.id, peer, response.blocks.len(), blocks_range); - - self.sync.write().on_block_data(&mut ProtocolContext::new(&self.context_data, io), peer, request, response); - } - - /// Perform time based maintenance. - pub fn tick(&self, io: &mut SyncIo) { - self.maintain_peers(io); - self.on_demand.as_ref().map(|s| s.maintain_peers(io)); - } - - fn maintain_peers(&self, io: &mut SyncIo) { - let tick = time::Instant::now(); - let mut aborting = Vec::new(); - { - let peers = self.context_data.peers.read(); - let handshaking_peers = self.handshaking_peers.read(); - for (who, timestamp) in peers.iter() - .filter_map(|(id, peer)| peer.request_timestamp.as_ref().map(|r| (id, r))) - .chain(handshaking_peers.iter()) { - if (tick - *timestamp).as_secs() > REQUEST_TIMEOUT_SEC { - trace!(target: "sync", "Timeout {}", who); - aborting.push(*who); - } - } - } - - self.specialization.write().maintain_peers(&mut ProtocolContext::new(&self.context_data, io)); - for p in aborting { - io.report_peer(p, Severity::Timeout); - } - } - - pub fn peer_info(&self, peer: NodeIndex) -> Option> { - self.context_data.peers.read().get(&peer).map(|p| { - PeerInfo { - roles: p.roles, - protocol_version: p.protocol_version, - best_hash: p.best_hash, - best_number: p.best_number, - } - }) - } - - /// Called by peer to report status - fn on_status_message(&self, io: &mut SyncIo, who: NodeIndex, status: message::Status) { - trace!(target: "sync", "New peer {} {:?}", who, status); - if io.is_expired() { - trace!(target: "sync", "Status packet from expired session {}:{}", who, io.peer_info(who)); - return; - } - - { - let mut peers = self.context_data.peers.write(); - let mut handshaking_peers = self.handshaking_peers.write(); - if peers.contains_key(&who) { - debug!(target: "sync", "Unexpected status packet from {}:{}", who, io.peer_info(who)); - return; - } - if status.genesis_hash != self.genesis_hash { - io.report_peer(who, Severity::Bad(&format!("Peer is on different chain (our genesis: {} theirs: {})", self.genesis_hash, status.genesis_hash))); - return; - } - if status.version != CURRENT_VERSION { - io.report_peer(who, Severity::Bad(&format!("Peer using unsupported protocol version {}", status.version))); - return; - } - - let peer = Peer { - protocol_version: status.version, - roles: status.roles, - best_hash: status.best_hash, - best_number: status.best_number, - block_request: None, - request_timestamp: None, - known_extrinsics: HashSet::new(), - known_blocks: HashSet::new(), - next_request_id: 0, - }; - peers.insert(who.clone(), peer); - handshaking_peers.remove(&who); - debug!(target: "sync", "Connected {} {}", who, io.peer_info(who)); - } - - let mut context = ProtocolContext::new(&self.context_data, io); - self.sync.write().new_peer(&mut context, who); - self.specialization.write().on_connect(&mut context, who, status.clone()); - self.on_demand.as_ref().map(|s| s.on_connect(who, status.roles)); - } - - /// Called when peer sends us new extrinsics - fn on_extrinsics(&self, _io: &mut SyncIo, who: NodeIndex, extrinsics: message::Transactions) { - // Accept extrinsics only when fully synced - if self.sync.read().status().state != SyncState::Idle { - trace!(target: "sync", "{} Ignoring extrinsics while syncing", who); - return; - } - trace!(target: "sync", "Received {} extrinsics from {}", extrinsics.len(), who); - let mut peers = self.context_data.peers.write(); - if let Some(ref mut peer) = peers.get_mut(&who) { - for t in extrinsics { - if let Some(hash) = self.transaction_pool.import(&t) { - peer.known_extrinsics.insert(hash); - } - } - } - } - - /// Called when we propagate ready extrinsics to peers. - pub fn propagate_extrinsics(&self, io: &mut SyncIo) { - debug!(target: "sync", "Propagating extrinsics"); - - // Accept transactions only when fully synced - if self.sync.read().status().state != SyncState::Idle { - return; - } - - let extrinsics = self.transaction_pool.transactions(); - - let mut propagated_to = HashMap::new(); - let mut peers = self.context_data.peers.write(); - for (who, ref mut peer) in peers.iter_mut() { - let (hashes, to_send): (Vec<_>, Vec<_>) = extrinsics - .iter() - .cloned() - .filter(|&(ref hash, _)| peer.known_extrinsics.insert(hash.clone())) - .unzip(); - - if !to_send.is_empty() { - let node_id = io.peer_session_info(*who).map(|info| match info.id { - Some(id) => format!("{}@{:x}", info.remote_address, id), - None => info.remote_address.clone(), - }); - - if let Some(id) = node_id { - for hash in hashes { - propagated_to.entry(hash).or_insert_with(Vec::new).push(id.clone()); - } - } - trace!(target: "sync", "Sending {} transactions to {}", to_send.len(), who); - self.send_message(io, *who, GenericMessage::Transactions(to_send)); - } - } - self.transaction_pool.on_broadcasted(propagated_to); - } - - /// Send Status message - fn send_status(&self, io: &mut SyncIo, who: NodeIndex) { - if let Ok(info) = self.context_data.chain.info() { - let status = message::generic::Status { - version: CURRENT_VERSION, - genesis_hash: info.chain.genesis_hash, - roles: self.config.roles.into(), - best_number: info.chain.best_number, - best_hash: info.chain.best_hash, - chain_status: self.specialization.read().status(), - }; - self.send_message(io, who, GenericMessage::Status(status)) - } - } - - pub fn abort(&self) { - let mut sync = self.sync.write(); - let mut spec = self.specialization.write(); - let mut peers = self.context_data.peers.write(); - let mut handshaking_peers = self.handshaking_peers.write(); - sync.clear(); - spec.on_abort(); - peers.clear(); - handshaking_peers.clear(); - } - - pub fn stop(&self) { - // stop processing import requests first (without holding a sync lock) - let import_queue = self.sync.read().import_queue(); - import_queue.stop(); - - // and then clear all the sync data - self.abort(); - } - - pub fn on_block_announce(&self, io: &mut SyncIo, who: NodeIndex, announce: message::BlockAnnounce) { - let header = announce.header; - let hash = header.hash(); - { - let mut peers = self.context_data.peers.write(); - if let Some(ref mut peer) = peers.get_mut(&who) { - peer.known_blocks.insert(hash.clone()); - } - } - self.sync.write().on_block_announce(&mut ProtocolContext::new(&self.context_data, io), who, hash, &header); - } - - pub fn on_block_imported(&self, io: &mut SyncIo, hash: B::Hash, header: &B::Header) { - self.sync.write().update_chain_info(&header); - self.specialization.write().on_block_imported( - &mut ProtocolContext::new(&self.context_data, io), - hash.clone(), - header - ); - - // blocks are not announced by light clients - if self.config.roles & Roles::LIGHT == Roles::LIGHT { - return; - } - - // send out block announcements - let mut peers = self.context_data.peers.write(); - - for (who, ref mut peer) in peers.iter_mut() { - if peer.known_blocks.insert(hash.clone()) { - trace!(target: "sync", "Announcing block {:?} to {}", hash, who); - self.send_message(io, *who, GenericMessage::BlockAnnounce(message::BlockAnnounce { - header: header.clone() - })); - } - } - } - - fn on_remote_call_request(&self, io: &mut SyncIo, who: NodeIndex, request: message::RemoteCallRequest) { - trace!(target: "sync", "Remote call request {} from {} ({} at {})", request.id, who, request.method, request.block); - let proof = match self.context_data.chain.execution_proof(&request.block, &request.method, &request.data) { - Ok((_, proof)) => proof, - Err(error) => { - trace!(target: "sync", "Remote call request {} from {} ({} at {}) failed with: {}", - request.id, who, request.method, request.block, error); - Default::default() - }, - }; - - self.send_message(io, who, GenericMessage::RemoteCallResponse(message::RemoteCallResponse { - id: request.id, proof, - })); - } - - fn on_remote_call_response(&self, io: &mut SyncIo, who: NodeIndex, response: message::RemoteCallResponse) { - trace!(target: "sync", "Remote call response {} from {}", response.id, who); - self.on_demand.as_ref().map(|s| s.on_remote_call_response(io, who, response)); - } - - fn on_remote_read_request(&self, io: &mut SyncIo, who: NodeIndex, request: message::RemoteReadRequest) { - trace!(target: "sync", "Remote read request {} from {} ({} at {})", - request.id, who, request.key.to_hex(), request.block); - let proof = match self.context_data.chain.read_proof(&request.block, &request.key) { - Ok(proof) => proof, - Err(error) => { - trace!(target: "sync", "Remote read request {} from {} ({} at {}) failed with: {}", - request.id, who, request.key.to_hex(), request.block, error); - Default::default() - }, - }; - self.send_message(io, who, GenericMessage::RemoteReadResponse(message::RemoteReadResponse { - id: request.id, proof, - })); - } - fn on_remote_read_response(&self, io: &mut SyncIo, who: NodeIndex, response: message::RemoteReadResponse) { - trace!(target: "sync", "Remote read response {} from {}", response.id, who); - self.on_demand.as_ref().map(|s| s.on_remote_read_response(io, who, response)); - } - - fn on_remote_header_request(&self, io: &mut SyncIo, who: NodeIndex, request: message::RemoteHeaderRequest>) { - trace!(target: "sync", "Remote header proof request {} from {} ({})", - request.id, who, request.block); - let (header, proof) = match self.context_data.chain.header_proof(request.block) { - Ok((header, proof)) => (Some(header), proof), - Err(error) => { - trace!(target: "sync", "Remote header proof request {} from {} ({}) failed with: {}", - request.id, who, request.block, error); - (Default::default(), Default::default()) - }, - }; - self.send_message(io, who, GenericMessage::RemoteHeaderResponse(message::RemoteHeaderResponse { - id: request.id, header, proof, - })); - } - - fn on_remote_header_response(&self, io: &mut SyncIo, who: NodeIndex, response: message::RemoteHeaderResponse) { - trace!(target: "sync", "Remote header proof response {} from {}", response.id, who); - self.on_demand.as_ref().map(|s| s.on_remote_header_response(io, who, response)); - } - - /// Execute a closure with access to a network context and specialization. - pub fn with_spec(&self, io: &mut SyncIo, f: F) -> U - where F: FnOnce(&mut S, &mut Context) -> U - { - f(&mut* self.specialization.write(), &mut ProtocolContext::new(&self.context_data, io)) - } -} - -fn send_message(peers: &RwLock>>, io: &mut SyncIo, who: NodeIndex, mut message: Message) { - match &mut message { - &mut GenericMessage::BlockRequest(ref mut r) => { - let mut peers = peers.write(); - if let Some(ref mut peer) = peers.get_mut(&who) { - r.id = peer.next_request_id; - peer.next_request_id = peer.next_request_id + 1; - peer.block_request = Some(r.clone()); - peer.request_timestamp = Some(time::Instant::now()); - } - }, - _ => (), - } - io.send(who, message.encode()); -} - -/// Hash a message. -pub(crate) fn hash_message(message: &Message) -> B::Hash { - let data = message.encode(); - HashFor::::hash(&data) -} diff --git a/substrate/network/src/service.rs b/substrate/network/src/service.rs deleted file mode 100644 index 2260b86fb6d49..0000000000000 --- a/substrate/network/src/service.rs +++ /dev/null @@ -1,340 +0,0 @@ -// Copyright 2017 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::collections::HashMap; -use std::sync::Arc; -use std::io; -use std::time::Duration; -use futures::sync::{oneshot, mpsc}; -use network_libp2p::{NetworkProtocolHandler, NetworkContext, NodeIndex, ProtocolId, -NetworkConfiguration , NonReservedPeerMode, ErrorKind}; -use network_libp2p::{NetworkService}; -use core_io::{TimerToken}; -use io::NetSyncIo; -use protocol::{Protocol, ProtocolContext, Context, ProtocolStatus, PeerInfo as ProtocolPeerInfo}; -use config::{ProtocolConfig}; -use error::Error; -use chain::Client; -use message::LocalizedBftMessage; -use specialization::Specialization; -use on_demand::OnDemandService; -use import_queue::AsyncImportQueue; -use runtime_primitives::traits::{Block as BlockT}; - -/// Type that represents fetch completion future. -pub type FetchFuture = oneshot::Receiver>; -/// Type that represents bft messages stream. -pub type BftMessageStream = mpsc::UnboundedReceiver>; - -const TICK_TOKEN: TimerToken = 0; -const TICK_TIMEOUT: Duration = Duration::from_millis(1000); - -const PROPAGATE_TOKEN: TimerToken = 1; -const PROPAGATE_TIMEOUT: Duration = Duration::from_millis(5000); - -bitflags! { - /// Node roles bitmask. - pub struct Roles: u8 { - /// No network. - const NONE = 0b00000000; - /// Full node, does not participate in consensus. - const FULL = 0b00000001; - /// Light client node. - const LIGHT = 0b00000010; - /// Act as an authority - const AUTHORITY = 0b00000100; - } -} - -impl ::codec::Encode for Roles { - fn encode_to(&self, dest: &mut T) { - dest.push_byte(self.bits()) - } -} - -impl ::codec::Decode for Roles { - fn decode(input: &mut I) -> Option { - Self::from_bits(input.read_byte()?) - } -} - -/// Sync status -pub trait SyncProvider: Send + Sync { - /// Get sync status - fn status(&self) -> ProtocolStatus; - /// Get peers information - fn peers(&self) -> Vec>; - /// Get this node id if available. - fn node_id(&self) -> Option; -} - -pub trait ExHashT: ::std::hash::Hash + Eq + ::std::fmt::Debug + Clone + Send + Sync + 'static {} -impl ExHashT for T where T: ::std::hash::Hash + Eq + ::std::fmt::Debug + Clone + Send + Sync + 'static {} - -/// Transaction pool interface -pub trait TransactionPool: Send + Sync { - /// Get transactions from the pool that are ready to be propagated. - fn transactions(&self) -> Vec<(H, B::Extrinsic)>; - /// Import a transaction into the pool. - fn import(&self, transaction: &B::Extrinsic) -> Option; - /// Notify the pool about transactions broadcast. - fn on_broadcasted(&self, propagations: HashMap>); -} - -/// ConsensusService -pub trait ConsensusService: Send + Sync { - /// Maintain connectivity to given addresses. - fn connect_to_authorities(&self, addresses: &[String]); - - /// Get BFT message stream for messages corresponding to consensus on given - /// parent hash. - fn bft_messages(&self, parent_hash: B::Hash) -> BftMessageStream; - /// Send out a BFT message. - fn send_bft_message(&self, message: LocalizedBftMessage); -} - -/// Service able to execute closure in the network context. -pub trait ExecuteInContext: Send + Sync { - /// Execute closure in network context. - fn execute_in_context)>(&self, closure: F); -} - -/// Network protocol handler -struct ProtocolHandler, H: ExHashT> { - protocol: Protocol, -} - -/// Peer connection information -#[derive(Debug)] -pub struct PeerInfo { - /// Public node id - pub id: Option, - /// Node client ID - pub client_version: String, - /// Capabilities - pub capabilities: Vec, - /// Remote endpoint address - pub remote_address: String, - /// Local endpoint address - pub local_address: String, - /// Dot protocol info. - pub dot_info: Option>, -} - -/// Service initialization parameters. -pub struct Params { - /// Configuration. - pub config: ProtocolConfig, - /// Network layer configuration. - pub network_config: NetworkConfiguration, - /// Polkadot relay chain access point. - pub chain: Arc>, - /// On-demand service reference. - pub on_demand: Option>>, - /// Transaction pool. - pub transaction_pool: Arc>, - /// Protocol specialization. - pub specialization: S, -} - -/// Polkadot network service. Handles network IO and manages connectivity. -pub struct Service, H: ExHashT> { - /// Network service - network: NetworkService, - /// Devp2p protocol handler - handler: Arc>, - /// Devp2p protocol ID. - protocol_id: ProtocolId, -} - -impl, H: ExHashT> Service { - /// Creates and register protocol with the network service - pub fn new(params: Params, protocol_id: ProtocolId) -> Result>, Error> { - let chain = params.chain.clone(); - let import_queue = Arc::new(AsyncImportQueue::new()); - let handler = Arc::new(ProtocolHandler { - protocol: Protocol::new( - params.config, - params.chain, - import_queue.clone(), - params.on_demand, - params.transaction_pool, - params.specialization, - )?, - }); - let versions = [(::protocol::CURRENT_VERSION as u8, ::protocol::CURRENT_PACKET_COUNT)]; - let protocols = vec![(handler.clone() as Arc<_>, protocol_id, &versions[..])]; - let service = match NetworkService::new(params.network_config.clone(), protocols) { - Ok(service) => service, - Err(err) => { - match err.kind() { - ErrorKind::Io(ref e) if e.kind() == io::ErrorKind::AddrInUse => - warn!("Network port is already in use, make sure that another instance of Polkadot client is not running or change the port using the --port option."), - _ => warn!("Error starting network: {}", err), - }; - return Err(err.into()) - }, - }; - let sync = Arc::new(Service { - network: service, - protocol_id, - handler, - }); - - import_queue.start( - Arc::downgrade(sync.handler.protocol.sync()), - Arc::downgrade(&sync), - Arc::downgrade(&chain) - )?; - - Ok(sync) - } - - /// Called when a new block is imported by the client. - pub fn on_block_imported(&self, hash: B::Hash, header: &B::Header) { - self.network.with_context(self.protocol_id, |context| { - self.handler.protocol.on_block_imported(&mut NetSyncIo::new(context), hash, header) - }); - } - - /// Called when new transactons are imported by the client. - pub fn trigger_repropagate(&self) { - self.network.with_context(self.protocol_id, |context| { - self.handler.protocol.propagate_extrinsics(&mut NetSyncIo::new(context)); - }); - } - - /// Execute a closure with the chain-specific network specialization. - /// If the network is unavailable, this will return `None`. - pub fn with_spec(&self, f: F) -> Option - where F: FnOnce(&mut S, &mut Context) -> U - { - let mut res = None; - self.network.with_context(self.protocol_id, |context| { - res = Some(self.handler.protocol.with_spec(&mut NetSyncIo::new(context), f)) - }); - - res - } -} - -impl, H:ExHashT> Drop for Service { - fn drop(&mut self) { - self.handler.protocol.stop(); - } -} -impl, H: ExHashT> ExecuteInContext for Service { - fn execute_in_context)>(&self, closure: F) { - self.network.with_context(self.protocol_id, |context| { - closure(&mut ProtocolContext::new(self.handler.protocol.context_data(), &mut NetSyncIo::new(context))) - }); - } -} - -impl, H: ExHashT> SyncProvider for Service { - /// Get sync status - fn status(&self) -> ProtocolStatus { - self.handler.protocol.status() - } - - /// Get sync peers - fn peers(&self) -> Vec> { - self.network.with_context_eval(self.protocol_id, |ctx| { - let peer_ids = self.network.connected_peers(); - - peer_ids.into_iter().filter_map(|who| { - let session_info = match ctx.session_info(who) { - None => return None, - Some(info) => info, - }; - - Some(PeerInfo { - id: session_info.id.map(|id| format!("{:x}", id)), - client_version: session_info.client_version, - capabilities: session_info.peer_capabilities.into_iter().map(|c| c.to_string()).collect(), - remote_address: session_info.remote_address, - local_address: session_info.local_address, - dot_info: self.handler.protocol.peer_info(who), - }) - }).collect() - }).unwrap_or_else(Vec::new) - } - - fn node_id(&self) -> Option { - self.network.external_url() - } -} - -impl, H: ExHashT> NetworkProtocolHandler for ProtocolHandler { - fn initialize(&self, io: &NetworkContext) { - io.register_timer(TICK_TOKEN, TICK_TIMEOUT) - .expect("Error registering sync timer"); - - io.register_timer(PROPAGATE_TOKEN, PROPAGATE_TIMEOUT) - .expect("Error registering transaction propagation timer"); - } - - fn read(&self, io: &NetworkContext, peer: &NodeIndex, _packet_id: u8, data: &[u8]) { - self.protocol.handle_packet(&mut NetSyncIo::new(io), *peer, data); - } - - fn connected(&self, io: &NetworkContext, peer: &NodeIndex) { - self.protocol.on_peer_connected(&mut NetSyncIo::new(io), *peer); - } - - fn disconnected(&self, io: &NetworkContext, peer: &NodeIndex) { - self.protocol.on_peer_disconnected(&mut NetSyncIo::new(io), *peer); - } - - fn timeout(&self, io: &NetworkContext, timer: TimerToken) { - match timer { - TICK_TOKEN => self.protocol.tick(&mut NetSyncIo::new(io)), - PROPAGATE_TOKEN => self.protocol.propagate_extrinsics(&mut NetSyncIo::new(io)), - _ => {} - } - } -} - -/// Trait for managing network -pub trait ManageNetwork: Send + Sync { - /// 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: String) -> Result<(), String>; - /// Add reserved peer - fn add_reserved_peer(&self, peer: String) -> Result<(), String>; -} - - -impl, H: ExHashT> ManageNetwork for Service { - fn accept_unreserved_peers(&self) { - self.network.set_non_reserved_mode(NonReservedPeerMode::Accept); - } - - fn deny_unreserved_peers(&self) { - self.network.set_non_reserved_mode(NonReservedPeerMode::Deny); - } - - fn remove_reserved_peer(&self, peer: String) -> Result<(), String> { - self.network.remove_reserved_peer(&peer).map_err(|e| format!("{:?}", e)) - } - - fn add_reserved_peer(&self, peer: String) -> Result<(), String> { - self.network.add_reserved_peer(&peer).map_err(|e| format!("{:?}", e)) - } -} diff --git a/substrate/network/src/specialization.rs b/substrate/network/src/specialization.rs deleted file mode 100644 index 3c04a367059cd..0000000000000 --- a/substrate/network/src/specialization.rs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2017 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 . - -//! Specializations of the substrate network protocol to allow more complex forms of communication. - -use ::NodeIndex; -use runtime_primitives::traits::Block as BlockT; -use protocol::Context; - -/// A specialization of the substrate network protocol. Handles events and sends messages. -pub trait Specialization: Send + Sync + 'static { - /// Get the current specialization-status. - fn status(&self) -> Vec; - - /// Called on start-up. - fn on_start(&mut self) { } - - /// Called when a peer successfully handshakes. - fn on_connect(&mut self, ctx: &mut Context, who: NodeIndex, status: ::message::Status); - - /// Called when a peer is disconnected. If the peer ID is unknown, it should be ignored. - fn on_disconnect(&mut self, ctx: &mut Context, who: NodeIndex); - - /// Called when a network-specific message arrives. - fn on_message(&mut self, ctx: &mut Context, who: NodeIndex, message: ::message::Message); - - /// Called on abort. - fn on_abort(&mut self) { } - - /// Called periodically to maintain peers and handle timeouts. - fn maintain_peers(&mut self, _ctx: &mut Context) { } - - /// Called when a block is _imported_ at the head of the chain (not during major sync). - fn on_block_imported(&mut self, _ctx: &mut Context, _hash: B::Hash, _header: &B::Header) { } -} diff --git a/substrate/network/src/sync.rs b/substrate/network/src/sync.rs deleted file mode 100644 index 33c3e57d7842a..0000000000000 --- a/substrate/network/src/sync.rs +++ /dev/null @@ -1,428 +0,0 @@ -// Copyright 2017 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::collections::HashMap; -use std::sync::Arc; -use protocol::Context; -use network_libp2p::{Severity, NodeIndex}; -use client::{BlockStatus, BlockOrigin, ClientInfo}; -use client::error::Error as ClientError; -use blocks::{self, BlockCollection}; -use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, As, NumberFor}; -use runtime_primitives::generic::BlockId; -use message::{self, generic::Message as GenericMessage}; -use service::Roles; -use import_queue::ImportQueue; - -// 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; - -struct PeerSync { - pub common_hash: B::Hash, - pub common_number: NumberFor, - pub best_hash: B::Hash, - pub best_number: NumberFor, - pub state: PeerSyncState, -} - -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -enum PeerSyncState { - AncestorSearch(NumberFor), - Available, - DownloadingNew(NumberFor), - DownloadingStale(B::Hash), -} - -/// Relay chain sync strategy. -pub struct ChainSync { - genesis_hash: B::Hash, - peers: HashMap>, - blocks: BlockCollection, - best_queued_number: NumberFor, - best_queued_hash: B::Hash, - required_block_attributes: message::BlockAttributes, - import_queue: Arc>, -} - -/// Reported sync state. -#[derive(Clone, Eq, PartialEq, Debug)] -pub enum SyncState { - /// Initial sync is complete, keep-up sync is active. - Idle, - /// Actively catching up with the chain. - Downloading -} - -/// Syncing status and statistics -#[derive(Clone)] -pub struct Status { - /// Current global sync state. - pub state: SyncState, - /// Target sync block number. - pub best_seen_block: Option>, -} - -impl ChainSync { - /// Create a new instance. - pub(crate) fn new(role: Roles, info: &ClientInfo, import_queue: Arc>) -> Self { - let mut required_block_attributes = message::BlockAttributes::HEADER | message::BlockAttributes::JUSTIFICATION; - if role.intersects(Roles::FULL | Roles::AUTHORITY) { - required_block_attributes |= message::BlockAttributes::BODY; - } - - ChainSync { - genesis_hash: info.chain.genesis_hash, - peers: HashMap::new(), - blocks: BlockCollection::new(), - best_queued_hash: info.best_queued_hash.unwrap_or(info.chain.best_hash), - best_queued_number: info.best_queued_number.unwrap_or(info.chain.best_number), - required_block_attributes, - import_queue, - } - } - - fn best_seen_block(&self) -> Option> { - self.peers.values().max_by_key(|p| p.best_number).map(|p| p.best_number) - } - - /// Returns import queue reference. - pub(crate) fn import_queue(&self) -> Arc> { - self.import_queue.clone() - } - - /// Returns sync status. - pub(crate) fn status(&self) -> Status { - let best_seen = self.best_seen_block(); - let state = match &best_seen { - &Some(n) if n > self.best_queued_number && n - self.best_queued_number > As::sa(5) => SyncState::Downloading, - _ => SyncState::Idle, - }; - Status { - state: state, - best_seen_block: best_seen, - } - } - - /// Handle new connected peer. - pub(crate) fn new_peer(&mut self, protocol: &mut Context, who: NodeIndex) { - if let Some(info) = protocol.peer_info(who) { - match (block_status(&*protocol.client(), &*self.import_queue, info.best_hash), info.best_number) { - (Err(e), _) => { - debug!(target:"sync", "Error reading blockchain: {:?}", e); - protocol.report_peer(who, Severity::Useless(&format!("Error legimimately reading blockchain status: {:?}", e))); - }, - (Ok(BlockStatus::KnownBad), _) => { - protocol.report_peer(who, Severity::Bad(&format!("New peer with known bad best block {} ({}).", info.best_hash, info.best_number))); - }, - (Ok(BlockStatus::Unknown), b) if b == As::sa(0) => { - protocol.report_peer(who, Severity::Bad(&format!("New peer with unknown genesis hash {} ({}).", info.best_hash, info.best_number))); - }, - (Ok(BlockStatus::Unknown), _) => { - let our_best = self.best_queued_number; - if our_best > As::sa(0) { - 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_hash: self.genesis_hash, - common_number: As::sa(0), - best_hash: info.best_hash, - best_number: info.best_number, - state: PeerSyncState::AncestorSearch(our_best), - }); - Self::request_ancestry(protocol, who, our_best) - } else { - // 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, PeerSync { - common_hash: self.genesis_hash, - common_number: As::sa(0), - best_hash: info.best_hash, - best_number: info.best_number, - state: PeerSyncState::Available, - }); - self.download_new(protocol, who) - } - }, - (Ok(BlockStatus::Queued), _) | (Ok(BlockStatus::InChain), _) => { - debug!(target:"sync", "New peer with known best hash {} ({}).", info.best_hash, info.best_number); - self.peers.insert(who, PeerSync { - common_hash: info.best_hash, - common_number: info.best_number, - best_hash: info.best_hash, - best_number: info.best_number, - state: PeerSyncState::Available, - }); - } - } - } - } - - pub(crate) fn on_block_data(&mut self, protocol: &mut Context, who: NodeIndex, _request: message::BlockRequest, response: message::BlockResponse) { - let new_blocks = if let Some(ref mut peer) = self.peers.get_mut(&who) { - match peer.state { - PeerSyncState::DownloadingNew(start_block) => { - self.blocks.clear_peer_download(who); - peer.state = PeerSyncState::Available; - - self.blocks.insert(start_block, response.blocks, who); - self.blocks.drain(self.best_queued_number + As::sa(1)) - }, - PeerSyncState::DownloadingStale(_) => { - peer.state = PeerSyncState::Available; - response.blocks.into_iter().map(|b| blocks::BlockData { - origin: who, - block: b - }).collect() - }, - PeerSyncState::AncestorSearch(n) => { - match response.blocks.get(0) { - Some(ref block) => { - trace!(target: "sync", "Got ancestry block #{} ({}) from peer {}", n, block.hash, who); - match protocol.client().block_hash(n) { - Ok(Some(block_hash)) if block_hash == block.hash => { - if peer.common_number < n { - peer.common_hash = block.hash; - peer.common_number = n; - } - peer.state = PeerSyncState::Available; - trace!(target:"sync", "Found common ancestor for peer {}: {} ({})", who, block.hash, n); - vec![] - }, - Ok(our_best) if n > As::sa(0) => { - trace!(target:"sync", "Ancestry block mismatch for peer {}: theirs: {} ({}), ours: {:?}", who, block.hash, n, our_best); - let n = n - As::sa(1); - peer.state = PeerSyncState::AncestorSearch(n); - Self::request_ancestry(protocol, who, n); - return; - }, - Ok(_) => { // genesis mismatch - trace!(target:"sync", "Ancestry search: genesis mismatch for peer {}", who); - protocol.report_peer(who, Severity::Bad("Ancestry search: genesis mismatch for peer")); - return; - }, - Err(e) => { - protocol.report_peer(who, Severity::Useless(&format!("Error answering legitimate blockchain query: {:?}", e))); - return; - } - } - }, - None => { - trace!(target:"sync", "Invalid response when searching for ancestor from {}", who); - protocol.report_peer(who, Severity::Bad("Invalid response when searching for ancestor")); - return; - } - } - }, - PeerSyncState::Available => Vec::new(), - } - } else { - vec![] - }; - - let best_seen = self.best_seen_block(); - let is_best = new_blocks.first().and_then(|b| b.block.header.as_ref()).map(|h| best_seen.as_ref().map_or(false, |n| h.number() >= n)); - let origin = if is_best.unwrap_or_default() { BlockOrigin::NetworkBroadcast } else { BlockOrigin::NetworkInitialSync }; - let import_queue = self.import_queue.clone(); - if let Some((hash, number)) = new_blocks.last() - .and_then(|b| b.block.header.as_ref().map(|h|(b.block.hash.clone(), *h.number()))) - { - if number > self.best_queued_number { - self.best_queued_number = number; - self.best_queued_hash = hash; - } - } - import_queue.import_blocks(self, protocol, (origin, new_blocks)); - self.maintain_sync(protocol); - } - - pub fn maintain_sync(&mut self, protocol: &mut Context) { - let peers: Vec = self.peers.keys().map(|p| *p).collect(); - for peer in peers { - self.download_new(protocol, peer); - } - } - - pub fn block_imported(&mut self, hash: &B::Hash, number: NumberFor) { - if number > self.best_queued_number { - self.best_queued_number = number; - self.best_queued_hash = *hash; - } - // Update common blocks - for (_, peer) in self.peers.iter_mut() { - trace!("Updating peer info ours={}, theirs={}", number, peer.best_number); - if peer.best_number >= number { - peer.common_number = number; - peer.common_hash = *hash; - } - } - } - - pub(crate) fn update_chain_info(&mut self, best_header: &B::Header) { - let hash = best_header.hash(); - self.block_imported(&hash, best_header.number().clone()) - } - - pub(crate) fn on_block_announce(&mut self, protocol: &mut Context, who: NodeIndex, hash: B::Hash, header: &B::Header) { - let number = *header.number(); - if let Some(ref mut peer) = self.peers.get_mut(&who) { - if number > peer.best_number { - peer.best_number = number; - peer.best_hash = hash; - } - if number <= self.best_queued_number && number > peer.common_number { - peer.common_number = number - } - } else { - return; - } - - if !self.is_known_or_already_downloading(protocol, &hash) { - let stale = number <= self.best_queued_number; - if stale { - if !self.is_known_or_already_downloading(protocol, header.parent_hash()) { - trace!(target: "sync", "Ignoring unknown stale block announce from {}: {} {:?}", who, hash, header); - } else { - trace!(target: "sync", "Downloading new stale block announced from {}: {} {:?}", who, hash, header); - self.download_stale(protocol, who, &hash); - } - } else { - trace!(target: "sync", "Downloading new block announced from {}: {} {:?}", who, hash, header); - self.download_new(protocol, who); - } - } else { - trace!(target: "sync", "Known block announce from {}: {}", who, hash); - } - } - - fn is_known_or_already_downloading(&self, protocol: &mut Context, hash: &B::Hash) -> bool { - self.peers.iter().any(|(_, p)| p.state == PeerSyncState::DownloadingStale(*hash)) - || block_status(&*protocol.client(), &*self.import_queue, *hash).ok().map_or(false, |s| s != BlockStatus::Unknown) - } - - pub(crate) fn peer_disconnected(&mut self, protocol: &mut Context, who: NodeIndex) { - self.blocks.clear_peer_download(who); - self.peers.remove(&who); - self.maintain_sync(protocol); - } - - pub(crate) fn restart(&mut self, protocol: &mut Context) { - self.import_queue.clear(); - self.blocks.clear(); - let ids: Vec = self.peers.keys().map(|p| *p).collect(); - for id in ids { - self.new_peer(protocol, id); - } - match protocol.client().info() { - Ok(info) => { - self.best_queued_hash = info.best_queued_hash.unwrap_or(info.chain.best_hash); - self.best_queued_number = info.best_queued_number.unwrap_or(info.chain.best_number); - }, - Err(e) => { - debug!(target:"sync", "Error reading blockchain: {:?}", e); - self.best_queued_hash = self.genesis_hash; - self.best_queued_number = As::sa(0); - } - } - } - - pub(crate) fn clear(&mut self) { - self.blocks.clear(); - self.peers.clear(); - } - - // Download old block. - fn download_stale(&mut self, protocol: &mut Context, who: NodeIndex, hash: &B::Hash) { - if let Some(ref mut peer) = self.peers.get_mut(&who) { - match peer.state { - PeerSyncState::Available => { - let request = 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), - }; - peer.state = PeerSyncState::DownloadingStale(*hash); - protocol.send_message(who, GenericMessage::BlockRequest(request)); - }, - _ => (), - } - } - } - - // Issue a request for a peer to download new blocks, if any are available - fn download_new(&mut self, protocol: &mut Context, who: NodeIndex) { - if let Some(ref mut peer) = self.peers.get_mut(&who) { - let import_status = self.import_queue.status(); - // when there are too many blocks in the queue => do not try to download new blocks - if import_status.importing_count > MAX_IMPORTING_BLOCKS { - return; - } - // we should not download already queued blocks - let common_number = ::std::cmp::max(peer.common_number, import_status.best_importing_number); - - trace!(target: "sync", "Considering new block download from {}, common block is {}, best is {:?}", who, common_number, peer.best_number); - match peer.state { - PeerSyncState::Available => { - if let Some(range) = self.blocks.needed_blocks(who, MAX_BLOCKS_TO_REQUEST, peer.best_number, common_number) { - trace!(target: "sync", "Requesting blocks from {}, ({} to {})", who, range.start, range.end); - let request = message::generic::BlockRequest { - id: 0, - fields: self.required_block_attributes.clone(), - from: message::FromBlock::Number(range.start), - to: None, - direction: message::Direction::Ascending, - max: Some((range.end - range.start).as_() as u32), - }; - peer.state = PeerSyncState::DownloadingNew(range.start); - protocol.send_message(who, GenericMessage::BlockRequest(request)); - } else { - trace!(target: "sync", "Nothing to request"); - } - }, - _ => (), - } - } - } - - fn request_ancestry(protocol: &mut Context, who: NodeIndex, block: NumberFor) { - trace!(target: "sync", "Requesting ancestry block #{} from {}", block, who); - let request = message::generic::BlockRequest { - id: 0, - fields: message::BlockAttributes::HEADER | message::BlockAttributes::JUSTIFICATION, - from: message::FromBlock::Number(block), - to: None, - direction: message::Direction::Ascending, - max: Some(1), - }; - protocol.send_message(who, GenericMessage::BlockRequest(request)); - } -} - -/// Get block status, taking into account import queue. -fn block_status( - chain: &::chain::Client, - queue: &ImportQueue, - hash: B::Hash) -> Result -{ - if queue.is_importing(&hash) { - return Ok(BlockStatus::Queued); - } - - chain.block_status(&BlockId::Hash(hash)) -} diff --git a/substrate/network/src/test/mod.rs b/substrate/network/src/test/mod.rs deleted file mode 100644 index 7a78dd28fc01b..0000000000000 --- a/substrate/network/src/test/mod.rs +++ /dev/null @@ -1,330 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -mod sync; - -use std::collections::{VecDeque, HashSet, HashMap}; -use std::sync::Arc; - -use parking_lot::RwLock; -use client; -use client::block_builder::BlockBuilder; -use runtime_primitives::traits::Block as BlockT; -use runtime_primitives::generic::BlockId; -use io::SyncIo; -use protocol::{Context, Protocol}; -use primitives::{Blake2Hasher, RlpCodec}; -use config::ProtocolConfig; -use service::TransactionPool; -use network_libp2p::{NodeIndex, SessionInfo, Severity}; -use keyring::Keyring; -use codec::Encode; -use import_queue::tests::SyncImportQueue; -use test_client::{self, TestClient}; -use test_client::runtime::{Block, Hash, Transfer, Extrinsic}; -use specialization::Specialization; - -pub struct DummySpecialization; - -impl Specialization for DummySpecialization { - fn status(&self) -> Vec { vec![] } - - fn on_connect(&mut self, _ctx: &mut Context, _peer_id: NodeIndex, _status: ::message::Status) { - - } - - fn on_disconnect(&mut self, _ctx: &mut Context, _peer_id: NodeIndex) { - - } - - fn on_message(&mut self, _ctx: &mut Context, _peer_id: NodeIndex, _message: ::message::Message) { - - } -} - -pub struct TestIo<'p> { - queue: &'p RwLock>, - pub to_disconnect: HashSet, - packets: Vec, - peers_info: HashMap, - _sender: Option, -} - -impl<'p> TestIo<'p> where { - pub fn new(queue: &'p RwLock>, sender: Option) -> TestIo<'p> { - TestIo { - queue: queue, - _sender: sender, - to_disconnect: HashSet::new(), - packets: Vec::new(), - peers_info: HashMap::new(), - } - } -} - -impl<'p> Drop for TestIo<'p> { - fn drop(&mut self) { - self.queue.write().extend(self.packets.drain(..)); - } -} - -impl<'p> SyncIo for TestIo<'p> { - fn report_peer(&mut self, who: NodeIndex, _reason: Severity) { - self.to_disconnect.insert(who); - } - - fn is_expired(&self) -> bool { - false - } - - fn send(&mut self, who: NodeIndex, data: Vec) { - self.packets.push(TestPacket { - data: data, - recipient: who, - }); - } - - fn peer_info(&self, who: NodeIndex) -> String { - self.peers_info.get(&who) - .cloned() - .unwrap_or_else(|| who.to_string()) - } - - fn peer_session_info(&self, _peer_id: NodeIndex) -> Option { - None - } -} - -/// Mocked subprotocol packet -pub struct TestPacket { - data: Vec, - recipient: NodeIndex, -} - -pub struct Peer { - client: Arc>, - pub sync: Protocol, - pub queue: RwLock>, -} - -impl Peer { - /// 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().expect("In-mem client does not fail"); - let header = self.client.header(&BlockId::Hash(info.chain.best_hash)).unwrap().unwrap(); - self.sync.on_block_imported(&mut TestIo::new(&self.queue, None), info.chain.best_hash, &header); - } - - /// Called on connection to other indicated peer. - fn on_connect(&self, other: NodeIndex) { - self.sync.on_peer_connected(&mut TestIo::new(&self.queue, Some(other)), other); - } - - /// Called on disconnect from other indicated peer. - fn on_disconnect(&self, other: NodeIndex) { - let mut io = TestIo::new(&self.queue, Some(other)); - self.sync.on_peer_disconnected(&mut io, other); - } - - /// Receive a message from another peer. Return a set of peers to disconnect. - fn receive_message(&self, from: NodeIndex, msg: TestPacket) -> HashSet { - let mut io = TestIo::new(&self.queue, Some(from)); - self.sync.handle_packet(&mut io, from, &msg.data); - self.flush(); - io.to_disconnect.clone() - } - - /// Produce the next pending message to send to another peer. - fn pending_message(&self) -> Option { - self.flush(); - self.queue.write().pop_front() - } - - /// Whether this peer is done syncing (has no messages to send). - fn is_done(&self) -> bool { - self.queue.read().is_empty() - } - - /// Execute a "sync step". This is called for each peer after it sends a packet. - fn sync_step(&self) { - self.flush(); - self.sync.tick(&mut TestIo::new(&self.queue, None)); - } - - /// Restart sync for a peer. - fn restart_sync(&self) { - self.sync.abort(); - } - - fn flush(&self) { - } - - fn generate_blocks(&self, count: usize, mut edit_block: F) - where F: FnMut(&mut BlockBuilder) - { - for _ in 0 .. count { - let mut builder = self.client.new_block().unwrap(); - edit_block(&mut builder); - let block = builder.bake().unwrap(); - trace!("Generating {}, (#{})", block.hash(), block.header.number); - self.client.justify_and_import(client::BlockOrigin::File, block).unwrap(); - } - } - - fn push_blocks(&self, count: usize, with_tx: bool) { - let mut nonce = 0; - if with_tx { - self.generate_blocks(count, |builder| { - let transfer = Transfer { - from: Keyring::Alice.to_raw_public().into(), - to: Keyring::Alice.to_raw_public().into(), - amount: 1, - nonce, - }; - let signature = Keyring::from_raw_public(transfer.from.0).unwrap().sign(&transfer.encode()).into(); - builder.push(Extrinsic { transfer, signature }).unwrap(); - nonce = nonce + 1; - }); - } else { - self.generate_blocks(count, |_| ()); - } - } -} - -pub struct EmptyTransactionPool; - -impl TransactionPool for EmptyTransactionPool { - fn transactions(&self) -> Vec<(Hash, Extrinsic)> { - Vec::new() - } - - fn import(&self, _transaction: &Extrinsic) -> Option { - None - } - - fn on_broadcasted(&self, _: HashMap>) {} -} - -pub struct TestNet { - peers: Vec>, - started: bool, - disconnect_events: Vec<(NodeIndex, NodeIndex)>, //disconnected (initiated by, to) -} - -impl TestNet { - fn new(n: usize) -> Self { - Self::new_with_config(n, ProtocolConfig::default()) - } - - fn new_with_config(n: usize, config: ProtocolConfig) -> Self { - let mut net = TestNet { - peers: Vec::new(), - started: false, - disconnect_events: Vec::new(), - }; - - for _ in 0..n { - net.add_peer(&config); - } - net - } - - pub fn add_peer(&mut self, config: &ProtocolConfig) { - let client = Arc::new(test_client::new()); - let tx_pool = Arc::new(EmptyTransactionPool); - let import_queue = Arc::new(SyncImportQueue); - let sync = Protocol::new(config.clone(), client.clone(), import_queue, None, tx_pool, DummySpecialization).unwrap(); - self.peers.push(Arc::new(Peer { - sync: sync, - client: client, - queue: RwLock::new(VecDeque::new()), - })); - } - - pub fn peer(&self, i: usize) -> &Peer { - &self.peers[i] - } - - fn start(&mut self) { - if self.started { - return; - } - for peer in 0..self.peers.len() { - self.peers[peer].start(); - for client in 0..self.peers.len() { - if peer != client { - self.peers[peer].on_connect(client as NodeIndex); - } - } - } - self.started = true; - } - - fn sync_step(&mut self) { - for peer in 0..self.peers.len() { - let packet = self.peers[peer].pending_message(); - if let Some(packet) = packet { - let disconnecting = { - let recipient = packet.recipient; - trace!("--- {} -> {} ---", peer, recipient); - let to_disconnect = self.peers[recipient].receive_message(peer as NodeIndex, packet); - for d in &to_disconnect { - // notify this that disconnecting peers are disconnecting - self.peers[recipient].on_disconnect(*d as NodeIndex); - self.disconnect_events.push((peer, *d)); - } - to_disconnect - }; - for d in &disconnecting { - // notify other peers that this peer is disconnecting - self.peers[*d].on_disconnect(peer as NodeIndex); - } - } - - self.sync_step_peer(peer); - } - } - - fn sync_step_peer(&mut self, peer_num: usize) { - self.peers[peer_num].sync_step(); - } - - fn restart_peer(&mut self, i: usize) { - self.peers[i].restart_sync(); - } - - fn sync(&mut self) -> u32 { - self.start(); - let mut total_steps = 0; - while !self.done() { - self.sync_step(); - total_steps += 1; - } - total_steps - } - - fn sync_steps(&mut self, count: usize) { - self.start(); - for _ in 0..count { - self.sync_step(); - } - } - - fn done(&self) -> bool { - self.peers.iter().all(|p| p.is_done()) - } -} diff --git a/substrate/network/src/test/sync.rs b/substrate/network/src/test/sync.rs deleted file mode 100644 index 77367a3df1389..0000000000000 --- a/substrate/network/src/test/sync.rs +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2017 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 client::backend::Backend; -use client::blockchain::HeaderBackend as BlockchainHeaderBackend; -use sync::SyncState; -use Roles; -use super::*; - -#[test] -fn sync_from_two_peers_works() { - ::env_logger::init().ok(); - let mut net = TestNet::new(3); - net.peer(1).push_blocks(100, false); - net.peer(2).push_blocks(100, false); - net.sync(); - assert!(net.peer(0).client.backend().blockchain().equals_to(net.peer(1).client.backend().blockchain())); - let status = net.peer(0).sync.status(); - assert_eq!(status.sync.state, SyncState::Idle); -} - -#[test] -fn sync_from_two_peers_with_ancestry_search_works() { - ::env_logger::init().ok(); - 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.restart_peer(0); - net.sync(); - assert!(net.peer(0).client.backend().blockchain().canon_equals_to(net.peer(1).client.backend().blockchain())); -} - -#[test] -fn sync_long_chain_works() { - let mut net = TestNet::new(2); - net.peer(1).push_blocks(500, false); - net.sync_steps(3); - assert_eq!(net.peer(0).sync.status().sync.state, SyncState::Downloading); - net.sync(); - assert!(net.peer(0).client.backend().blockchain().equals_to(net.peer(1).client.backend().blockchain())); -} - -#[test] -fn sync_no_common_longer_chain_fails() { - ::env_logger::init().ok(); - let mut net = TestNet::new(3); - net.peer(0).push_blocks(20, true); - net.peer(1).push_blocks(20, false); - net.sync(); - assert!(!net.peer(0).client.backend().blockchain().canon_equals_to(net.peer(1).client.backend().blockchain())); -} - -#[test] -fn sync_after_fork_works() { - ::env_logger::init().ok(); - let mut net = TestNet::new(3); - net.peer(0).push_blocks(30, false); - net.peer(1).push_blocks(30, false); - net.peer(2).push_blocks(30, false); - - net.peer(0).push_blocks(10, true); - net.peer(1).push_blocks(20, false); - net.peer(2).push_blocks(20, false); - - net.peer(1).push_blocks(10, true); - net.peer(2).push_blocks(1, false); - - // peer 1 has the best chain - let peer1_chain = net.peer(1).client.backend().blockchain().clone(); - net.sync(); - assert!(net.peer(0).client.backend().blockchain().canon_equals_to(&peer1_chain)); - assert!(net.peer(1).client.backend().blockchain().canon_equals_to(&peer1_chain)); - assert!(net.peer(2).client.backend().blockchain().canon_equals_to(&peer1_chain)); -} - -#[test] -fn blocks_are_not_announced_by_light_nodes() { - ::env_logger::init().ok(); - let mut net = TestNet::new(0); - - // full peer0 is connected to light peer - // light peer1 is connected to full peer2 - let mut light_config = ProtocolConfig::default(); - light_config.roles = Roles::LIGHT; - net.add_peer(&ProtocolConfig::default()); - net.add_peer(&light_config); - net.add_peer(&ProtocolConfig::default()); - - net.peer(0).push_blocks(1, false); - net.peer(0).start(); - net.peer(1).start(); - net.peer(2).start(); - net.peer(0).on_connect(1); - net.peer(1).on_connect(2); - - // generate block at peer0 && run sync - while !net.done() { - net.sync_step(); - } - - // peer 0 has the best chain - // peer 1 has the best chain - // peer 2 has genesis-chain only - assert_eq!(net.peer(0).client.backend().blockchain().info().unwrap().best_number, 1); - assert_eq!(net.peer(1).client.backend().blockchain().info().unwrap().best_number, 1); - assert_eq!(net.peer(2).client.backend().blockchain().info().unwrap().best_number, 0); -} diff --git a/substrate/primitives/Cargo.toml b/substrate/primitives/Cargo.toml deleted file mode 100644 index 206bb24c5626a..0000000000000 --- a/substrate/primitives/Cargo.toml +++ /dev/null @@ -1,51 +0,0 @@ -[package] -name = "substrate-primitives" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -crunchy = "0.1" -substrate-runtime-std = { path = "../runtime-std", default_features = false } -substrate-codec = { path = "../codec", default_features = false } -substrate-codec-derive = { path = "../codec/derive", default_features = false } -elastic-array = {version = "0.10", optional = true } -fixed-hash = { version = "0.2.2", default_features = false } -rustc-hex = { version = "2.0", default_features = false } -serde = { version = "1.0", default_features = false } -serde_derive = { version = "1.0", optional = true } -uint = { version = "0.4.1", default_features = false } -rlp = { version = "0.2.4", optional = true } -twox-hash = { version = "1.1.0", optional = true } -byteorder = { version = "1.1", default_features = false } -wasmi = { version = "0.4", optional = true } -hashdb = { version = "0.2.1", default_features = false } -patricia-trie = { version = "0.2.1", optional = true } -plain_hasher = { version = "0.2", default_features = false } - -blake2-rfc = { version = "0.2.18", optional = true } - -[dev-dependencies] -substrate-serializer = { path = "../serializer" } -pretty_assertions = "0.4" -heapsize = "0.4" - -[features] -default = ["std"] -std = [ - "wasmi", - "uint/std", - "fixed-hash/std", - "fixed-hash/heapsizeof", - "fixed-hash/libc", - "substrate-codec/std", - "substrate-runtime-std/std", - "serde/std", - "rustc-hex/std", - "twox-hash", - "blake2-rfc", - "serde_derive", - "byteorder/std", - "patricia-trie", - "rlp", - "elastic-array", -] diff --git a/substrate/primitives/README.adoc b/substrate/primitives/README.adoc deleted file mode 100644 index ed98cf12adf1f..0000000000000 --- a/substrate/primitives/README.adoc +++ /dev/null @@ -1,13 +0,0 @@ - -= Primitives - -.Summary -[source, toml] ----- -include::Cargo.toml[lines=2..5] ----- - -.Description ----- -include::src/lib.rs[tag=description] ----- diff --git a/substrate/primitives/src/authority_id.rs b/substrate/primitives/src/authority_id.rs deleted file mode 100644 index c82261bce58a0..0000000000000 --- a/substrate/primitives/src/authority_id.rs +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - - -#[cfg(feature = "std")] -use serde::{Serialize, Serializer, Deserialize, Deserializer}; -use H256; - -/// An identifier for an authority in the consensus algorithm. The same size as ed25519::Public. -#[derive(Clone, Copy, PartialEq, Eq, Default, Encode, Decode)] -pub struct AuthorityId(pub [u8; 32]); - -impl AuthorityId { - /// Create an id from a 32-byte slice. Panics with other lengths. - #[cfg(feature = "std")] - fn from_slice(data: &[u8]) -> Self { - let mut r = [0u8; 32]; - r.copy_from_slice(data); - AuthorityId(r) - } -} - -#[cfg(feature = "std")] -impl ::std::fmt::Display for AuthorityId { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - write!(f, "{}", ::hexdisplay::HexDisplay::from(&self.0)) - } -} - -#[cfg(feature = "std")] -impl ::std::fmt::Debug for AuthorityId { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - write!(f, "{}", ::hexdisplay::HexDisplay::from(&self.0)) - } -} - -#[cfg(feature = "std")] -impl ::std::hash::Hash for AuthorityId { - fn hash(&self, state: &mut H) { - self.0.hash(state); - } -} - -impl AsRef<[u8; 32]> for AuthorityId { - fn as_ref(&self) -> &[u8; 32] { - &self.0 - } -} - -impl AsRef<[u8]> for AuthorityId { - fn as_ref(&self) -> &[u8] { - &self.0[..] - } -} - -impl Into<[u8; 32]> for AuthorityId { - fn into(self) -> [u8; 32] { - self.0 - } -} - -impl From<[u8; 32]> for AuthorityId { - fn from(a: [u8; 32]) -> Self { - AuthorityId(a) - } -} - -impl AsRef for AuthorityId { - fn as_ref(&self) -> &AuthorityId { - &self - } -} - -impl Into for AuthorityId { - fn into(self) -> H256 { - self.0.into() - } -} - -#[cfg(feature = "std")] -impl Serialize for AuthorityId { - fn serialize(&self, serializer: S) -> Result where S: Serializer { - ::bytes::serialize(&self.0, serializer) - } -} - -#[cfg(feature = "std")] -impl<'de> Deserialize<'de> for AuthorityId { - fn deserialize(deserializer: D) -> Result where D: Deserializer<'de> { - ::bytes::deserialize_check_len(deserializer, ::bytes::ExpectedLen::Exact(32)) - .map(|x| AuthorityId::from_slice(&x)) - } -} diff --git a/substrate/primitives/src/bytes.rs b/substrate/primitives/src/bytes.rs deleted file mode 100644 index 04605bedf8b94..0000000000000 --- a/substrate/primitives/src/bytes.rs +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2017 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 . - -//! Simple type for representing Vec with regards to serde. - -use core::fmt; - -use serde::{de, Serializer, Deserializer}; - -#[cfg(not(feature = "std"))] -mod alloc_types { - pub use ::alloc::string::String; - pub use ::alloc::vec::Vec; -} - -#[cfg(feature = "std")] -mod alloc_types { - pub use ::std::vec::Vec; - pub use ::std::string::String; -} - -pub use self::alloc_types::*; - -/// Serializes a slice of bytes. -pub fn serialize(bytes: &[u8], serializer: S) -> Result where - S: Serializer, -{ - let hex: String = ::rustc_hex::ToHex::to_hex(bytes); - serializer.serialize_str(&format!("0x{}", hex)) -} - -/// Serialize a slice of bytes as uint. -/// -/// The representation will have all leading zeros trimmed. -pub fn serialize_uint(bytes: &[u8], serializer: S) -> Result where - S: Serializer, -{ - let non_zero = bytes.iter().take_while(|b| **b == 0).count(); - let bytes = &bytes[non_zero..]; - if bytes.is_empty() { - return serializer.serialize_str("0x0"); - } - - let hex: String = ::rustc_hex::ToHex::to_hex(bytes); - let has_leading_zero = !hex.is_empty() && &hex[0..1] == "0"; - serializer.serialize_str( - &format!("0x{}", if has_leading_zero { &hex[1..] } else { &hex }) - ) -} - -/// Expected length of bytes vector. -#[derive(PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(Debug))] -pub enum ExpectedLen { - /// Any length in bytes. - #[cfg_attr(not(feature = "std"), allow(unused))] - Any, - /// Exact length in bytes. - Exact(usize), - /// A bytes length between (min; max]. - Between(usize, usize), -} - -impl fmt::Display for ExpectedLen { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - match *self { - ExpectedLen::Any => write!(fmt, "even length"), - ExpectedLen::Exact(v) => write!(fmt, "length of {}", v * 2), - ExpectedLen::Between(min, max) => write!(fmt, "length between ({}; {}]", min * 2, max * 2), - } - } -} - -/// Deserialize into vector of bytes. -#[cfg(feature = "std")] -pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> where - D: Deserializer<'de>, -{ - deserialize_check_len(deserializer, ExpectedLen::Any) -} - -/// Deserialize into vector of bytes with additional size check. -pub fn deserialize_check_len<'de, D>(deserializer: D, len: ExpectedLen) -> Result, D::Error> where - D: Deserializer<'de>, -{ - struct Visitor { - len: ExpectedLen, - } - - impl<'a> de::Visitor<'a> for Visitor { - type Value = Vec; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a 0x-prefixed hex string with {}", self.len) - } - - fn visit_str(self, v: &str) -> Result { - if v.len() < 2 || &v[0..2] != "0x" { - return Err(E::custom("prefix is missing")) - } - - let is_len_valid = match self.len { - // just make sure that we have all nibbles - ExpectedLen::Any => v.len() % 2 == 0, - ExpectedLen::Exact(len) => v.len() == 2 * len + 2, - ExpectedLen::Between(min, max) => v.len() <= 2 * max + 2 && v.len() > 2 * min + 2, - }; - - if !is_len_valid { - return Err(E::invalid_length(v.len() - 2, &self)) - } - - let bytes = match self.len { - ExpectedLen::Between(..) if v.len() % 2 != 0 => { - ::rustc_hex::FromHex::from_hex(&*format!("0{}", &v[2..])) - }, - _ => ::rustc_hex::FromHex::from_hex(&v[2..]) - }; - - #[cfg(feature = "std")] - fn format_err(e: ::rustc_hex::FromHexError) -> String { - format!("invalid hex value: {:?}", e) - } - - #[cfg(not(feature = "std"))] - fn format_err(e: ::rustc_hex::FromHexError) -> String { - match e { - ::rustc_hex::InvalidHexLength => format!("invalid hex value: invalid length"), - ::rustc_hex::InvalidHexCharacter(c, p) => - format!("invalid hex value: invalid character {} at position {}", c, p), - } - } - - bytes.map_err(|e| E::custom(format_err(e))) - } - - #[cfg(feature = "std")] - fn visit_string(self, v: String) -> Result { - self.visit_str(&v) - } - } - // TODO [ToDr] Use raw bytes if we switch to RLP / binencoding - // (visit_bytes, visit_bytes_buf) - deserializer.deserialize_str(Visitor { len }) -} diff --git a/substrate/primitives/src/hash.rs b/substrate/primitives/src/hash.rs deleted file mode 100644 index c8883da42d3d6..0000000000000 --- a/substrate/primitives/src/hash.rs +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! A fixed hash type. - -#[cfg(feature = "std")] -use serde::{Serialize, Serializer, Deserialize, Deserializer}; - -#[cfg(feature = "std")] -use bytes; -#[cfg(feature = "std")] -use core::cmp; -#[cfg(feature = "std")] -use rlp::{Rlp, RlpStream, DecoderError}; - -macro_rules! impl_rest { - ($name: ident, $len: expr) => { - #[cfg(feature = "std")] - impl Serialize for $name { - fn serialize(&self, serializer: S) -> Result where S: Serializer { - bytes::serialize(&self.0, serializer) - } - } - - #[cfg(feature = "std")] - impl<'de> Deserialize<'de> for $name { - fn deserialize(deserializer: D) -> Result where D: Deserializer<'de> { - bytes::deserialize_check_len(deserializer, bytes::ExpectedLen::Exact($len)) - .map(|x| (&*x).into()) - } - } - - impl ::codec::Encode for $name { - fn using_encoded R>(&self, f: F) -> R { - self.0.using_encoded(f) - } - } - impl ::codec::Decode for $name { - fn decode(input: &mut I) -> Option { - <[u8; $len] as ::codec::Decode>::decode(input).map($name) - } - } - - #[cfg(feature = "std")] - impl ::rlp::Encodable for $name { - fn rlp_append(&self, s: &mut RlpStream) { - s.encoder().encode_value(self); - } - } - - #[cfg(feature = "std")] - impl ::rlp::Decodable for $name { - fn decode(rlp: &Rlp) -> Result { - rlp.decoder().decode_value(|bytes| match bytes.len().cmp(&$len) { - cmp::Ordering::Less => Err(DecoderError::RlpIsTooShort), - cmp::Ordering::Greater => Err(DecoderError::RlpIsTooBig), - cmp::Ordering::Equal => { - let mut t = [0u8; $len]; - t.copy_from_slice(bytes); - Ok($name(t)) - } - }) - } - } - - } -} - -construct_hash!(H160, 20); -construct_hash!(H256, 32); -construct_hash!(H512, 64); -impl_rest!(H160, 20); -impl_rest!(H256, 32); -impl_rest!(H512, 64); - -#[cfg(test)] -mod tests { - use super::*; - use substrate_serializer as ser; - use rlp::{Encodable, RlpStream}; - - #[test] - fn test_hash_is_encodable() { - let h = H160::from(21); - let mut s = RlpStream::new(); - h.rlp_append(&mut s); - assert_eq!(s.drain().into_vec(), &[148, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21]); - } - - #[test] - fn test_hash_is_decodable() { - let data = vec![148, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 123]; - let res = ::rlp::decode::(&data); - assert!(res.is_ok()); - assert_eq!(res.unwrap(), H160::from(123)); - - let res = ::rlp::decode::(&data); - assert!(res.is_err()); - } - - #[test] - fn test_h160() { - let tests = vec![ - (Default::default(), "0x0000000000000000000000000000000000000000"), - (H160::from(2), "0x0000000000000000000000000000000000000002"), - (H160::from(15), "0x000000000000000000000000000000000000000f"), - (H160::from(16), "0x0000000000000000000000000000000000000010"), - (H160::from(1_000), "0x00000000000000000000000000000000000003e8"), - (H160::from(100_000), "0x00000000000000000000000000000000000186a0"), - (H160::from(u64::max_value()), "0x000000000000000000000000ffffffffffffffff"), - ]; - - for (number, expected) in tests { - assert_eq!(format!("{:?}", expected), ser::to_string_pretty(&number)); - assert_eq!(number, ser::from_str(&format!("{:?}", expected)).unwrap()); - } - } - - #[test] - fn test_h256() { - let tests = vec![ - (Default::default(), "0x0000000000000000000000000000000000000000000000000000000000000000"), - (H256::from(2), "0x0000000000000000000000000000000000000000000000000000000000000002"), - (H256::from(15), "0x000000000000000000000000000000000000000000000000000000000000000f"), - (H256::from(16), "0x0000000000000000000000000000000000000000000000000000000000000010"), - (H256::from(1_000), "0x00000000000000000000000000000000000000000000000000000000000003e8"), - (H256::from(100_000), "0x00000000000000000000000000000000000000000000000000000000000186a0"), - (H256::from(u64::max_value()), "0x000000000000000000000000000000000000000000000000ffffffffffffffff"), - ]; - - for (number, expected) in tests { - assert_eq!(format!("{:?}", expected), ser::to_string_pretty(&number)); - assert_eq!(number, ser::from_str(&format!("{:?}", expected)).unwrap()); - } - } - - #[test] - fn test_invalid() { - assert!(ser::from_str::("\"0x000000000000000000000000000000000000000000000000000000000000000\"").unwrap_err().is_data()); - assert!(ser::from_str::("\"0x000000000000000000000000000000000000000000000000000000000000000g\"").unwrap_err().is_data()); - assert!(ser::from_str::("\"0x00000000000000000000000000000000000000000000000000000000000000000\"").unwrap_err().is_data()); - assert!(ser::from_str::("\"\"").unwrap_err().is_data()); - assert!(ser::from_str::("\"0\"").unwrap_err().is_data()); - assert!(ser::from_str::("\"10\"").unwrap_err().is_data()); - } - - #[test] - fn test_heapsizeof() { - use heapsize::HeapSizeOf; - let h = H256::new(); - assert_eq!(h.heap_size_of_children(), 0); - } -} diff --git a/substrate/primitives/src/hasher.rs b/substrate/primitives/src/hasher.rs deleted file mode 100644 index d4dd39b855498..0000000000000 --- a/substrate/primitives/src/hasher.rs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2018 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 . - -//! Polkadot Blake2b Hasher implementation - -use hashdb::Hasher; -use plain_hasher::PlainHasher; -use hash::H256; - -pub mod blake2 { - use super::{Hasher, PlainHasher, H256}; - #[cfg(feature = "std")] - use hashing::blake2_256; - - #[cfg(not(feature = "std"))] - extern "C" { - fn ext_blake2_256(data: *const u8, len: u32, out: *mut u8); - } - #[cfg(not(feature = "std"))] - fn blake2_256(data: &[u8]) -> [u8; 32] { - let mut result: [u8; 32] = Default::default(); - unsafe { - ext_blake2_256(data.as_ptr(), data.len() as u32, result.as_mut_ptr()); - } - result - } - - /// Concrete implementation of Hasher using Blake2b 256-bit hashes - #[derive(Debug)] - pub struct Blake2Hasher; - - impl Hasher for Blake2Hasher { - type Out = H256; - type StdHasher = PlainHasher; - const LENGTH:usize = 32; - fn hash(x: &[u8]) -> Self::Out { - blake2_256(x).into() - } - } -} diff --git a/substrate/primitives/src/hashing.rs b/substrate/primitives/src/hashing.rs deleted file mode 100644 index 379ea4a56f4e7..0000000000000 --- a/substrate/primitives/src/hashing.rs +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2017 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 . - -//! Hashing functions. - -use blake2_rfc; -use twox_hash; - -/// Do a Blake2 512-bit hash and place result in `dest`. -pub fn blake2_512_into(data: &[u8], dest: &mut [u8; 64]) { - dest.copy_from_slice(blake2_rfc::blake2b::blake2b(64, &[], data).as_bytes()); -} - -/// Do a Blake2 512-bit hash and return result. -pub fn blake2_512(data: &[u8]) -> [u8; 64] { - let mut r = [0; 64]; - blake2_512_into(data, &mut r); - r -} - -/// Do a Blake2 256-bit hash and place result in `dest`. -pub fn blake2_256_into(data: &[u8], dest: &mut [u8; 32]) { - dest.copy_from_slice(blake2_rfc::blake2b::blake2b(32, &[], data).as_bytes()); -} - -/// Do a Blake2 256-bit hash and return result. -pub fn blake2_256(data: &[u8]) -> [u8; 32] { - let mut r = [0; 32]; - blake2_256_into(data, &mut r); - r -} - -/// Do a Blake2 128-bit hash and place result in `dest`. -pub fn blake2_128_into(data: &[u8], dest: &mut [u8; 16]) { - dest.copy_from_slice(blake2_rfc::blake2b::blake2b(16, &[], data).as_bytes()); -} - -/// Do a Blake2 128-bit hash and return result. -pub fn blake2_128(data: &[u8]) -> [u8; 16] { - let mut r = [0; 16]; - blake2_128_into(data, &mut r); - r -} - -/// Do a XX 128-bit hash and place result in `dest`. -pub fn twox_128_into(data: &[u8], dest: &mut [u8; 16]) { - use ::core::hash::Hasher; - let mut h0 = twox_hash::XxHash::with_seed(0); - let mut h1 = twox_hash::XxHash::with_seed(1); - h0.write(data); - h1.write(data); - let r0 = h0.finish(); - let r1 = h1.finish(); - use byteorder::{ByteOrder, LittleEndian}; - LittleEndian::write_u64(&mut dest[0..8], r0); - LittleEndian::write_u64(&mut dest[8..16], r1); -} - -/// Do a XX 128-bit hash and return result. -pub fn twox_128(data: &[u8]) -> [u8; 16] { - let mut r: [u8; 16] = [0; 16]; - twox_128_into(data, &mut r); - r -} - -/// Do a XX 256-bit hash and place result in `dest`. -pub fn twox_256_into(data: &[u8], dest: &mut [u8; 32]) { - use ::core::hash::Hasher; - use byteorder::{ByteOrder, LittleEndian}; - let mut h0 = twox_hash::XxHash::with_seed(0); - let mut h1 = twox_hash::XxHash::with_seed(1); - let mut h2 = twox_hash::XxHash::with_seed(2); - let mut h3 = twox_hash::XxHash::with_seed(3); - h0.write(data); - h1.write(data); - h2.write(data); - h3.write(data); - let r0 = h0.finish(); - let r1 = h1.finish(); - let r2 = h2.finish(); - let r3 = h3.finish(); - LittleEndian::write_u64(&mut dest[0..8], r0); - LittleEndian::write_u64(&mut dest[8..16], r1); - LittleEndian::write_u64(&mut dest[16..24], r2); - LittleEndian::write_u64(&mut dest[24..32], r3); -} - -/// Do a XX 256-bit hash and return result. -pub fn twox_256(data: &[u8]) -> [u8; 32] { - let mut r: [u8; 32] = [0; 32]; - twox_256_into(data, &mut r); - r -} diff --git a/substrate/primitives/src/hexdisplay.rs b/substrate/primitives/src/hexdisplay.rs deleted file mode 100644 index 938d3945c5aa3..0000000000000 --- a/substrate/primitives/src/hexdisplay.rs +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2017 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 . - -//! Wrapper type for byte collections that outputs hex. - -/// Simple wrapper to display hex representation of bytes. -pub struct HexDisplay<'a>(&'a [u8]); - -impl<'a> HexDisplay<'a> { - /// Create new instance that will display `d` as a hex string when displayed. - pub fn from(d: &'a AsBytesRef) -> Self { HexDisplay(d.as_bytes_ref()) } -} - -impl<'a> ::core::fmt::Display for HexDisplay<'a> { - fn fmt(&self, fmtr: &mut ::core::fmt::Formatter) -> Result<(), ::core::fmt::Error> { - if self.0.len() < 1027 { - for byte in self.0 { - fmtr.write_fmt(format_args!("{:02x}", byte))?; - } - } else { - for byte in &self.0[0..512] { - fmtr.write_fmt(format_args!("{:02x}", byte))?; - } - fmtr.write_str("...")?; - for byte in &self.0[self.0.len() - 512..] { - fmtr.write_fmt(format_args!("{:02x}", byte))?; - } - } - Ok(()) - } -} - -/// Simple trait to transform various types to `&[u8]` -pub trait AsBytesRef { - /// Transform `self` into `&[u8]`. - fn as_bytes_ref(&self) -> &[u8]; -} - -impl<'a> AsBytesRef for &'a [u8] { - fn as_bytes_ref(&self) -> &[u8] { self } -} - -impl AsBytesRef for [u8] { - fn as_bytes_ref(&self) -> &[u8] { &self } -} - -impl AsBytesRef for ::bytes::Vec { - fn as_bytes_ref(&self) -> &[u8] { &self } -} - -macro_rules! impl_non_endians { - ( $( $t:ty ),* ) => { $( - impl AsBytesRef for $t { - fn as_bytes_ref(&self) -> &[u8] { &self[..] } - } - )* } -} - -impl_non_endians!([u8; 1], [u8; 2], [u8; 3], [u8; 4], [u8; 5], [u8; 6], [u8; 7], [u8; 8], - [u8; 10], [u8; 12], [u8; 14], [u8; 16], [u8; 20], [u8; 24], [u8; 28], [u8; 32], [u8; 40], - [u8; 48], [u8; 56], [u8; 64], [u8; 80], [u8; 96], [u8; 112], [u8; 128]); - -/// Format into ASCII + # + hex, suitable for storage key preimages. -pub fn ascii_format(asciish: &[u8]) -> String { - let mut r = String::new(); - let mut latch = false; - for c in asciish { - match (latch, *c) { - (false, 32...127) => r.push(*c as char), - _ => { - if !latch { - r.push('#'); - latch = true; - } - r.push_str(&format!("{:02x}", *c)); - } - } - } - r -} - diff --git a/substrate/primitives/src/lib.rs b/substrate/primitives/src/lib.rs deleted file mode 100644 index 5fddf350d811f..0000000000000 --- a/substrate/primitives/src/lib.rs +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -// tag::description[] -//! Shareable Polkadot types. -// end::description[] - -#![warn(missing_docs)] - -#![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(not(feature = "std"), feature(alloc))] - -#[macro_use] -extern crate crunchy; -#[macro_use] -extern crate fixed_hash; -#[macro_use] -extern crate uint as uint_crate; -#[macro_use] -extern crate substrate_codec_derive; - -extern crate rustc_hex; -extern crate byteorder; -extern crate substrate_codec as codec; -#[cfg(feature = "std")] -extern crate rlp; - -#[cfg(feature = "std")] -extern crate serde; -#[cfg(feature = "std")] -extern crate twox_hash; - -#[cfg(feature = "std")] -extern crate blake2_rfc; - -#[cfg(feature = "std")] -#[macro_use] -extern crate serde_derive; -#[cfg(feature = "std")] -extern crate core; -#[cfg(feature = "std")] -extern crate wasmi; -extern crate hashdb; -extern crate plain_hasher; -#[cfg(feature = "std")] -extern crate patricia_trie; -#[cfg(feature = "std")] -extern crate elastic_array; - -extern crate substrate_runtime_std as rstd; - -#[cfg(test)] -extern crate substrate_serializer; - -#[cfg(test)] -extern crate heapsize; - -#[cfg(test)] -#[macro_use] -extern crate pretty_assertions; - -#[macro_export] -macro_rules! map { - ($( $name:expr => $value:expr ),*) => ( - vec![ $( ( $name, $value ) ),* ].into_iter().collect() - ) -} - -use rstd::prelude::*; -use rstd::ops::Deref; - -#[cfg(feature = "std")] -pub mod bytes; -#[cfg(feature = "std")] -pub mod hashing; -#[cfg(feature = "std")] -pub use hashing::{blake2_256, twox_128, twox_256}; -#[cfg(feature = "std")] -pub mod hexdisplay; - -pub mod u32_trait; - -pub mod hash; -mod hasher; -pub mod sandbox; -pub mod storage; -pub mod uint; -mod authority_id; -#[cfg(feature = "std")] -mod rlp_codec; - -#[cfg(test)] -mod tests; - -pub use self::hash::{H160, H256, H512}; -pub use self::uint::U256; -pub use authority_id::AuthorityId; - -// Switch back to Blake after PoC-3 is out -// pub use self::hasher::blake::BlakeHasher; -pub use self::hasher::blake2::Blake2Hasher; - -#[cfg(feature = "std")] -pub use self::rlp_codec::RlpCodec; - -/// A 512-bit value interpreted as a signature. -pub type Signature = hash::H512; - -/// Hex-serialised shim for `Vec`. -#[derive(PartialEq, Eq, Clone)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug, Hash, PartialOrd, Ord))] -pub struct Bytes(#[cfg_attr(feature = "std", serde(with="bytes"))] pub Vec); - -impl From> for Bytes { - fn from(s: Vec) -> Self { Bytes(s) } -} - -impl Deref for Bytes { - type Target = [u8]; - fn deref(&self) -> &[u8] { &self.0[..] } -} diff --git a/substrate/primitives/src/rlp_codec.rs b/substrate/primitives/src/rlp_codec.rs deleted file mode 100644 index bf1ae978b3d12..0000000000000 --- a/substrate/primitives/src/rlp_codec.rs +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2018 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 . - -//! Polkadot Blake2b (trie) NodeCodec implementation - -use elastic_array::{ElasticArray1024, ElasticArray128}; -use hashdb::Hasher; -use rlp::{DecoderError, RlpStream, Rlp, Prototype}; -use core::marker::PhantomData; -use patricia_trie::{NibbleSlice, NodeCodec, node::Node, ChildReference}; - -use hash::H256; -use Blake2Hasher; - -/// Concrete implementation of a `NodeCodec` with Rlp encoding, generic over the `Hasher` -pub struct RlpNodeCodec {mark: PhantomData} - -/// Convenience type for a Blake2_256/Rlp flavoured NodeCodec -pub type RlpCodec = RlpNodeCodec; - -impl NodeCodec for RlpCodec { - type Error = DecoderError; - const HASHED_NULL_NODE : H256 = H256( [0x45, 0xb0, 0xcf, 0xc2, 0x20, 0xce, 0xec, 0x5b, 0x7c, 0x1c, 0x62, 0xc4, 0xd4, 0x19, 0x3d, 0x38, 0xe4, 0xeb, 0xa4, 0x8e, 0x88, 0x15, 0x72, 0x9c, 0xe7, 0x5f, 0x9c, 0xa, 0xb0, 0xe4, 0xc1, 0xc0] ); - fn decode(data: &[u8]) -> ::std::result::Result { - let r = Rlp::new(data); - match r.prototype()? { - // either leaf or extension - decode first item with NibbleSlice::??? - // and use is_leaf return to figure out which. - // if leaf, second item is a value (is_data()) - // if extension, second item is a node (either SHA3 to be looked up and - // fed back into this function or inline RLP which can be fed back into this function). - Prototype::List(2) => match NibbleSlice::from_encoded(r.at(0)?.data()?) { - (slice, true) => Ok(Node::Leaf(slice, r.at(1)?.data()?)), - (slice, false) => Ok(Node::Extension(slice, r.at(1)?.as_raw())), - }, - // branch - first 16 are nodes, 17th is a value (or empty). - Prototype::List(17) => { - let mut nodes = [&[] as &[u8]; 16]; - for i in 0..16 { - nodes[i] = r.at(i)?.as_raw(); - } - Ok(Node::Branch(nodes, if r.at(16)?.is_empty() { None } else { Some(r.at(16)?.data()?) })) - }, - // an empty branch index. - Prototype::Data(0) => Ok(Node::Empty), - // something went wrong. - _ => Err(DecoderError::Custom("Rlp is not valid.")) - } - } - fn try_decode_hash(data: &[u8]) -> Option<::Out> { - let r = Rlp::new(data); - if r.is_data() && r.size() == Blake2Hasher::LENGTH { - Some(r.as_val().expect("Hash is the correct size; qed")) - } else { - None - } - } - fn is_empty_node(data: &[u8]) -> bool { - Rlp::new(data).is_empty() - } - fn empty_node() -> ElasticArray1024 { - let mut stream = RlpStream::new(); - stream.append_empty_data(); - stream.drain() - } - - fn leaf_node(partial: &[u8], value: &[u8]) -> ElasticArray1024 { - let mut stream = RlpStream::new_list(2); - stream.append(&partial); - stream.append(&value); - stream.drain() - } - - fn ext_node(partial: &[u8], child_ref: ChildReference<::Out>) -> ElasticArray1024 { - let mut stream = RlpStream::new_list(2); - stream.append(&partial); - match child_ref { - ChildReference::Hash(h) => stream.append(&h), - ChildReference::Inline(inline_data, len) => { - let bytes = &AsRef::<[u8]>::as_ref(&inline_data)[..len]; - stream.append_raw(bytes, 1) - }, - }; - stream.drain() - } - - fn branch_node(children: I, value: Option>) -> ElasticArray1024 - where I: IntoIterator::Out>>> - { - let mut stream = RlpStream::new_list(17); - for child_ref in children { - match child_ref { - Some(c) => match c { - ChildReference::Hash(h) => stream.append(&h), - ChildReference::Inline(inline_data, len) => { - let bytes = &AsRef::<[u8]>::as_ref(&inline_data)[..len]; - stream.append_raw(bytes, 1) - }, - }, - None => stream.append_empty_data() - }; - } - if let Some(value) = value { - stream.append(&&*value); - } else { - stream.append_empty_data(); - } - stream.drain() - } -} diff --git a/substrate/primitives/src/sandbox.rs b/substrate/primitives/src/sandbox.rs deleted file mode 100644 index 2e3144b24ff25..0000000000000 --- a/substrate/primitives/src/sandbox.rs +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright 2018 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 . - -//! Definition of a sandbox environment. - -#[cfg(test)] -use codec::Encode; -use rstd::vec::Vec; - -/// Error error that can be returned from host function. -#[derive(Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] -pub struct HostError; - -/// Representation of a typed wasm value. -#[derive(Clone, Copy, PartialEq, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] -pub enum TypedValue { - /// Value of 32-bit signed or unsigned integer. - #[codec(index = "1")] - I32(i32), - - /// Value of 64-bit signed or unsigned integer. - #[codec(index = "2")] - I64(i64), - - /// Value of 32-bit IEEE 754-2008 floating point number represented as a bit pattern. - #[codec(index = "3")] - F32(i32), - - /// Value of 64-bit IEEE 754-2008 floating point number represented as a bit pattern. - #[codec(index = "4")] - F64(i64), -} - -impl TypedValue { - /// Returns `Some` if this value of type `I32`. - pub fn as_i32(&self) -> Option { - match *self { - TypedValue::I32(v) => Some(v), - _ => None, - } - } -} - -#[cfg(feature = "std")] -impl From<::wasmi::RuntimeValue> for TypedValue { - fn from(val: ::wasmi::RuntimeValue) -> TypedValue { - use ::wasmi::RuntimeValue; - match val { - RuntimeValue::I32(v) => TypedValue::I32(v), - RuntimeValue::I64(v) => TypedValue::I64(v), - RuntimeValue::F32(v) => TypedValue::F32(v.to_bits() as i32), - RuntimeValue::F64(v) => TypedValue::F64(v.to_bits() as i64), - } - } -} - -#[cfg(feature = "std")] -impl From for ::wasmi::RuntimeValue { - fn from(val: TypedValue) -> ::wasmi::RuntimeValue { - use ::wasmi::RuntimeValue; - use ::wasmi::nan_preserving_float::{F32, F64}; - match val { - TypedValue::I32(v) => RuntimeValue::I32(v), - TypedValue::I64(v) => RuntimeValue::I64(v), - TypedValue::F32(v_bits) => RuntimeValue::F32(F32::from_bits(v_bits as u32)), - TypedValue::F64(v_bits) => RuntimeValue::F64(F64::from_bits(v_bits as u64)), - } - } -} - -/// Typed value that can be returned from a function. -/// -/// Basically a `TypedValue` plus `Unit`, for functions which return nothing. -#[derive(Clone, Copy, PartialEq, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] -pub enum ReturnValue { - /// For returning nothing. - Unit, - /// For returning some concrete value. - Value(TypedValue), -} - -impl From for ReturnValue { - fn from(v: TypedValue) -> ReturnValue { - ReturnValue::Value(v) - } -} - -impl ReturnValue { - /// Maximum number of bytes `ReturnValue` might occupy when serialized with - /// `Codec`. - /// - /// Breakdown: - /// 1 byte for encoding unit/value variant - /// 1 byte for encoding value type - /// 8 bytes for encoding the biggest value types available in wasm: f64, i64. - pub const ENCODED_MAX_SIZE: usize = 10; -} - -#[test] -fn return_value_encoded_max_size() { - let encoded = ReturnValue::Value(TypedValue::I64(-1)).encode(); - assert_eq!(encoded.len(), ReturnValue::ENCODED_MAX_SIZE); -} - -/// Describes an entity to define or import into the environment. -#[derive(Clone, PartialEq, Eq, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] -pub enum ExternEntity { - /// Function that is specified by an index in a default table of - /// a module that creates the sandbox. - #[codec(index = "1")] - Function(u32), - - /// Linear memory that is specified by some identifier returned by sandbox - /// module upon creation new sandboxed memory. - #[codec(index = "2")] - Memory(u32), -} - -/// An entry in a environment definition table. -/// -/// Each entry has a two-level name and description of an entity -/// being defined. -#[derive(Clone, PartialEq, Eq, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] -pub struct Entry { - /// Module name of which corresponding entity being defined. - pub module_name: Vec, - /// Field name in which corresponding entity being defined. - pub field_name: Vec, - /// External entity being defined. - pub entity: ExternEntity, -} - -/// Definition of runtime that could be used by sandboxed code. -#[derive(Clone, PartialEq, Eq, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] -pub struct EnvironmentDefinition { - /// Vector of all entries in the environment definition. - pub entries: Vec, -} - -/// Constant for specifying no limit when creating a sandboxed -/// memory instance. For FFI purposes. -pub const MEM_UNLIMITED: u32 = -1i32 as u32; - -/// No error happened. -/// -/// For FFI purposes. -pub const ERR_OK: u32 = 0; - -/// Validation or instantiation error occured when creating new -/// sandboxed module instance. -/// -/// For FFI purposes. -pub const ERR_MODULE: u32 = -1i32 as u32; - -/// Out-of-bounds access attempted with memory or table. -/// -/// For FFI purposes. -pub const ERR_OUT_OF_BOUNDS: u32 = -2i32 as u32; - -/// Execution error occurred (typically trap). -/// -/// For FFI purposes. -pub const ERR_EXECUTION: u32 = -3i32 as u32; - -#[cfg(test)] -mod tests { - use super::*; - use std::fmt; - use codec::Codec; - - fn roundtrip(s: S) { - let encoded = s.encode(); - assert_eq!(S::decode(&mut &encoded[..]).unwrap(), s); - } - - #[test] - fn env_def_roundtrip() { - roundtrip(EnvironmentDefinition { - entries: vec![], - }); - - roundtrip(EnvironmentDefinition { - entries: vec![ - Entry { - module_name: b"kernel"[..].into(), - field_name: b"memory"[..].into(), - entity: ExternEntity::Memory(1337), - }, - ], - }); - - roundtrip(EnvironmentDefinition { - entries: vec![ - Entry { - module_name: b"env"[..].into(), - field_name: b"abort"[..].into(), - entity: ExternEntity::Function(228), - }, - ], - }); - } -} diff --git a/substrate/primitives/src/storage.rs b/substrate/primitives/src/storage.rs deleted file mode 100644 index 55d62f6a81356..0000000000000 --- a/substrate/primitives/src/storage.rs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2017 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 . - -//! Contract execution data. - -#[cfg(feature = "std")] -use bytes; -use rstd::vec::Vec; - -/// Contract storage key. -#[derive(PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug, Hash, PartialOrd, Ord, Clone))] -pub struct StorageKey(#[cfg_attr(feature = "std", serde(with="bytes"))] pub Vec); - -/// Contract storage entry data. -#[derive(PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug, Hash, PartialOrd, Ord, Clone))] -pub struct StorageData(#[cfg_attr(feature = "std", serde(with="bytes"))] pub Vec); - -/// Storage change set -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug, PartialEq, Eq))] -pub struct StorageChangeSet { - /// Block hash - pub block: Hash, - /// A list of changes - pub changes: Vec<( - StorageKey, - Option, - )>, -} - diff --git a/substrate/primitives/src/tests.rs b/substrate/primitives/src/tests.rs deleted file mode 100644 index c650104394505..0000000000000 --- a/substrate/primitives/src/tests.rs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2017 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. diff --git a/substrate/primitives/src/u32_trait.rs b/substrate/primitives/src/u32_trait.rs deleted file mode 100644 index 5fba6f8e04de6..0000000000000 --- a/substrate/primitives/src/u32_trait.rs +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2017 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 u32 trait with "values" as impl'd types. - -/// A u32 value, wrapped in a trait because we don't yet have const generics. -pub trait Value { - /// The actual value represented by the impl'ing type. - const VALUE: u32; -} -/// Type representing the value 0 for the `Value` trait. -pub struct _0; impl Value for _0 { const VALUE: u32 = 0; } -/// Type representing the value 1 for the `Value` trait. -pub struct _1; impl Value for _1 { const VALUE: u32 = 1; } -/// Type representing the value 2 for the `Value` trait. -pub struct _2; impl Value for _2 { const VALUE: u32 = 2; } -/// Type representing the value 3 for the `Value` trait. -pub struct _3; impl Value for _3 { const VALUE: u32 = 3; } -/// Type representing the value 4 for the `Value` trait. -pub struct _4; impl Value for _4 { const VALUE: u32 = 4; } -/// Type representing the value 5 for the `Value` trait. -pub struct _5; impl Value for _5 { const VALUE: u32 = 5; } -/// Type representing the value 6 for the `Value` trait. -pub struct _6; impl Value for _6 { const VALUE: u32 = 6; } -/// Type representing the value 7 for the `Value` trait. -pub struct _7; impl Value for _7 { const VALUE: u32 = 7; } -/// Type representing the value 8 for the `Value` trait. -pub struct _8; impl Value for _8 { const VALUE: u32 = 8; } -/// Type representing the value 9 for the `Value` trait. -pub struct _9; impl Value for _9 { const VALUE: u32 = 9; } -/// Type representing the value 10 for the `Value` trait. -pub struct _10; impl Value for _10 { const VALUE: u32 = 10; } -/// Type representing the value 11 for the `Value` trait. -pub struct _11; impl Value for _11 { const VALUE: u32 = 11; } -/// Type representing the value 12 for the `Value` trait. -pub struct _12; impl Value for _12 { const VALUE: u32 = 12; } -/// Type representing the value 13 for the `Value` trait. -pub struct _13; impl Value for _13 { const VALUE: u32 = 13; } -/// Type representing the value 14 for the `Value` trait. -pub struct _14; impl Value for _14 { const VALUE: u32 = 14; } -/// Type representing the value 15 for the `Value` trait. -pub struct _15; impl Value for _15 { const VALUE: u32 = 15; } -/// Type representing the value 16 for the `Value` trait. -pub struct _16; impl Value for _16 { const VALUE: u32 = 16; } -/// Type representing the value 24 for the `Value` trait. -pub struct _24; impl Value for _24 { const VALUE: u32 = 24; } -/// Type representing the value 32 for the `Value` trait. -pub struct _32; impl Value for _32 { const VALUE: u32 = 32; } -/// Type representing the value 40 for the `Value` trait. -pub struct _40; impl Value for _40 { const VALUE: u32 = 40; } -/// Type representing the value 48 for the `Value` trait. -pub struct _48; impl Value for _48 { const VALUE: u32 = 48; } -/// Type representing the value 56 for the `Value` trait. -pub struct _56; impl Value for _56 { const VALUE: u32 = 56; } -/// Type representing the value 64 for the `Value` trait. -pub struct _64; impl Value for _64 { const VALUE: u32 = 64; } -/// Type representing the value 80 for the `Value` trait. -pub struct _80; impl Value for _80 { const VALUE: u32 = 80; } -/// Type representing the value 96 for the `Value` trait. -pub struct _96; impl Value for _96 { const VALUE: u32 = 96; } -/// Type representing the value 112 for the `Value` trait. -pub struct _112; impl Value for _112 { const VALUE: u32 = 112; } -/// Type representing the value 128 for the `Value` trait. -pub struct _128; impl Value for _128 { const VALUE: u32 = 128; } -/// Type representing the value 160 for the `Value` trait. -pub struct _160; impl Value for _160 { const VALUE: u32 = 160; } -/// Type representing the value 192 for the `Value` trait. -pub struct _192; impl Value for _192 { const VALUE: u32 = 192; } -/// Type representing the value 224 for the `Value` trait. -pub struct _224; impl Value for _224 { const VALUE: u32 = 224; } -/// Type representing the value 256 for the `Value` trait. -pub struct _256; impl Value for _256 { const VALUE: u32 = 256; } -/// Type representing the value 384 for the `Value` trait. -pub struct _384; impl Value for _384 { const VALUE: u32 = 384; } -/// Type representing the value 512 for the `Value` trait. -pub struct _512; impl Value for _512 { const VALUE: u32 = 512; } diff --git a/substrate/primitives/src/uint.rs b/substrate/primitives/src/uint.rs deleted file mode 100644 index af3278ef01809..0000000000000 --- a/substrate/primitives/src/uint.rs +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2017 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 unsigned fixed-size integer. - -#[cfg(feature = "std")] -use serde::{Serialize, Serializer, Deserialize, Deserializer}; - -#[cfg(feature = "std")] -use bytes; - -macro_rules! impl_serde { - ($name: ident, $len: expr) => { - #[cfg(feature = "std")] - impl Serialize for $name { - fn serialize(&self, serializer: S) -> Result where S: Serializer { - let mut bytes = [0u8; $len * 8]; - self.to_big_endian(&mut bytes); - bytes::serialize_uint(&bytes, serializer) - } - } - - #[cfg(feature = "std")] - impl<'de> Deserialize<'de> for $name { - fn deserialize(deserializer: D) -> Result where D: Deserializer<'de> { - bytes::deserialize_check_len(deserializer, bytes::ExpectedLen::Between(0, $len * 8)) - .map(|x| (&*x).into()) - } - } - } -} - -construct_uint!(U256, 4); -impl_serde!(U256, 4); - -#[cfg(test)] -mod tests { - use super::*; - use substrate_serializer as ser; - - macro_rules! test { - ($name: ident, $test_name: ident) => { - #[test] - fn $test_name() { - let tests = vec![ - ($name::from(0), "0x0"), - ($name::from(1), "0x1"), - ($name::from(2), "0x2"), - ($name::from(10), "0xa"), - ($name::from(15), "0xf"), - ($name::from(15), "0xf"), - ($name::from(16), "0x10"), - ($name::from(1_000), "0x3e8"), - ($name::from(100_000), "0x186a0"), - ($name::from(u64::max_value()), "0xffffffffffffffff"), - ($name::from(u64::max_value()) + $name::from(1), "0x10000000000000000"), - ]; - - for (number, expected) in tests { - assert_eq!(format!("{:?}", expected), ser::to_string_pretty(&number)); - assert_eq!(number, ser::from_str(&format!("{:?}", expected)).unwrap()); - } - - // Invalid examples - assert!(ser::from_str::<$name>("\"0x\"").unwrap_err().is_data()); - assert!(ser::from_str::<$name>("\"0xg\"").unwrap_err().is_data()); - assert!(ser::from_str::<$name>("\"\"").unwrap_err().is_data()); - assert!(ser::from_str::<$name>("\"10\"").unwrap_err().is_data()); - assert!(ser::from_str::<$name>("\"0\"").unwrap_err().is_data()); - } - } - } - - test!(U256, test_u256); - - #[test] - fn test_large_values() { - assert_eq!( - ser::to_string_pretty(&!U256::zero()), - "\"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\"" - ); - assert!( - ser::from_str::("\"0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\"").unwrap_err().is_data() - ); - } -} diff --git a/substrate/pwasm-alloc/Cargo.toml b/substrate/pwasm-alloc/Cargo.toml deleted file mode 100644 index d5dbe87e6adfe..0000000000000 --- a/substrate/pwasm-alloc/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "pwasm-alloc" -version = "0.1.0" -authors = ["Nikolay Volf "] -license = "MIT/Apache-2.0" -readme = "README.md" -repository = "https://github.com/paritytech/pwasm-std" -homepage = "https://github.com/paritytech/pwasm-std" -documentation = "https://paritytech.github.io/pwasm-std/pwasm_std/" -description = "Parity WebAssembly standard library internal allocator" -keywords = ["wasm", "parity", "webassembly", "blockchain"] -categories = ["no-std", "embedded"] -build = "build.rs" - -[dependencies] -pwasm-libc = { path = "../pwasm-libc", version = "0.1" } - -[build-dependencies] -rustc_version = "0.2" - -[features] -strict = [] -nightly = [] diff --git a/substrate/pwasm-alloc/README.adoc b/substrate/pwasm-alloc/README.adoc deleted file mode 100644 index 5ddc97ea1b901..0000000000000 --- a/substrate/pwasm-alloc/README.adoc +++ /dev/null @@ -1,25 +0,0 @@ - -= Pwasm-alloc - -Parity WASM contracts standard library libc bindings. - -See https://paritytech.github.io/pwasm-std/pwasm_alloc/ for the documentation. - -== License - -`pwasm_alloc` is primarily distributed under the terms of both the MIT -license and the Apache License (Version 2.0), at your choice. - -See LICENSE-APACHE, and LICENSE-MIT for details. - -.Summary -[source, toml] ----- -include::Cargo.toml[lines=2..5] ----- - -.Description ----- -include::src/lib.rs[tag=description] ----- - diff --git a/substrate/pwasm-alloc/build.rs b/substrate/pwasm-alloc/build.rs deleted file mode 100644 index 35eb154f3a69a..0000000000000 --- a/substrate/pwasm-alloc/build.rs +++ /dev/null @@ -1,14 +0,0 @@ -//! Set a nightly feature - -extern crate rustc_version; -use rustc_version::{version, version_meta, Channel}; - -fn main() { - // Assert we haven't travelled back in time - assert!(version().unwrap().major >= 1); - - // Set cfg flags depending on release channel - if let Channel::Nightly = version_meta().unwrap().channel { - println!("cargo:rustc-cfg=feature=\"nightly\""); - } -} diff --git a/substrate/pwasm-alloc/src/lib.rs b/substrate/pwasm-alloc/src/lib.rs deleted file mode 100644 index b08eef6854808..0000000000000 --- a/substrate/pwasm-alloc/src/lib.rs +++ /dev/null @@ -1,34 +0,0 @@ -#![warn(missing_docs)] -#![cfg_attr(feature = "strict", deny(warnings))] -#![no_std] -#![crate_type = "rlib"] - -// tag::description[] -//! Custom allocator crate for wasm -// end::description[] - -/// Wasm allocator -pub struct WasmAllocator; - -#[cfg(feature = "nightly")] -#[global_allocator] -static ALLOCATOR: WasmAllocator = WasmAllocator; - -#[cfg(feature = "nightly")] -mod __impl { - extern crate pwasm_libc; - - use core::alloc::{GlobalAlloc, Layout}; - - use super::WasmAllocator; - - unsafe impl GlobalAlloc for WasmAllocator { - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - pwasm_libc::malloc(layout.size()) as *mut u8 - } - - unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { - pwasm_libc::free(ptr as *mut u8) - } - } -} diff --git a/substrate/pwasm-libc/Cargo.toml b/substrate/pwasm-libc/Cargo.toml deleted file mode 100644 index d3ff1f1f320e6..0000000000000 --- a/substrate/pwasm-libc/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "pwasm-libc" -version = "0.1.0" -authors = ["Sergey Pepyakin "] -license = "MIT/Apache-2.0" -readme = "README.md" -repository = "https://github.com/paritytech/pwasm-std" -homepage = "https://github.com/paritytech/pwasm-std" -documentation = "https://paritytech.github.io/pwasm-std/pwasm_std/" -description = "Parity WebAssembly standard library libc bindings" -keywords = ["wasm", "parity", "webassembly", "blockchain"] -categories = ["no-std", "embedded"] - -[features] -strict = [] diff --git a/substrate/pwasm-libc/README.adoc b/substrate/pwasm-libc/README.adoc deleted file mode 100644 index e1cba6632f1df..0000000000000 --- a/substrate/pwasm-libc/README.adoc +++ /dev/null @@ -1,24 +0,0 @@ - -= Pwasm-libc - -Parity WASM contracts standard library libc bindings - -https://paritytech.github.io/pwasm-std/pwasm_libc/[Documentation] - -== License - -`pwasm-libc` is primarily distributed under the terms of both the MIT -license and the Apache License (Version 2.0), at your choice. - -See LICENSE-APACHE, and LICENSE-MIT for details. - -.Summary -[source, toml] ----- -include::Cargo.toml[lines=2..5] ----- - -.Description ----- -include::src/lib.rs[tag=description] ----- diff --git a/substrate/pwasm-libc/src/lib.rs b/substrate/pwasm-libc/src/lib.rs deleted file mode 100644 index 6b97ff378890b..0000000000000 --- a/substrate/pwasm-libc/src/lib.rs +++ /dev/null @@ -1,55 +0,0 @@ -#![warn(missing_docs)] -#![cfg_attr(feature = "strict", deny(warnings))] -#![no_std] - -// tag::description[] -//! libc externs crate -// end::description[] - -extern "C" { - fn ext_memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8; - fn ext_memmove(dest: *mut u8, src: *const u8, n: usize) -> *mut u8; - fn ext_memset(dest: *mut u8, c: i32, n: usize) -> *mut u8; - fn ext_memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32; - fn ext_malloc(size: usize) -> *mut u8; - fn ext_free(ptr: *mut u8); -} - -// Declaring these function here prevents Emscripten from including it's own verisons -// into final binary. - -/// memcpy extern -#[no_mangle] -pub unsafe extern "C" fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 { - ext_memcpy(dest, src, n) -} - -/// memcmp extern -#[no_mangle] -pub unsafe extern "C" fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32 { - ext_memcmp(s1, s2, n) -} - -/// memmove extern -#[no_mangle] -pub unsafe extern "C" fn memmove(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 { - ext_memmove(dest, src, n) -} - -/// memset extern -#[no_mangle] -pub unsafe extern "C" fn memset(dest: *mut u8, c: i32, n: usize) -> *mut u8 { - ext_memset(dest, c, n) -} - -/// malloc extern -#[no_mangle] -pub unsafe extern "C" fn malloc(size: usize) -> *mut u8 { - ext_malloc(size) -} - -/// free extern -#[no_mangle] -pub unsafe extern "C" fn free(ptr: *mut u8) { - ext_free(ptr); -} diff --git a/substrate/rpc-servers/Cargo.toml b/substrate/rpc-servers/Cargo.toml deleted file mode 100644 index 6b7038d44a628..0000000000000 --- a/substrate/rpc-servers/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "substrate-rpc-servers" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git" } -jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git" } -jsonrpc-pubsub = { git = "https://github.com/paritytech/jsonrpc.git" } -jsonrpc-ws-server = { git = "https://github.com/paritytech/jsonrpc.git" } -log = "0.3" -serde = "1.0" -substrate-rpc = { path = "../rpc", version = "0.1" } -substrate-runtime-primitives = { path = "../runtime/primitives" } diff --git a/substrate/rpc-servers/README.adoc b/substrate/rpc-servers/README.adoc deleted file mode 100644 index 18840be421fdd..0000000000000 --- a/substrate/rpc-servers/README.adoc +++ /dev/null @@ -1,14 +0,0 @@ - -= RPC Server - -.Summary -[source, toml] ----- -include::Cargo.toml[lines=2..5] ----- - -.Description ----- -include::src/lib.rs[tag=description] ----- - diff --git a/substrate/rpc-servers/src/lib.rs b/substrate/rpc-servers/src/lib.rs deleted file mode 100644 index 6139d32aec963..0000000000000 --- a/substrate/rpc-servers/src/lib.rs +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -// tag::description[] -//! Substrate RPC servers. -// end::description[] - -#[warn(missing_docs)] - -pub extern crate substrate_rpc as apis; - -extern crate jsonrpc_core as rpc; -extern crate jsonrpc_http_server as http; -extern crate jsonrpc_pubsub as pubsub; -extern crate jsonrpc_ws_server as ws; -extern crate serde; -extern crate substrate_runtime_primitives; - -#[macro_use] -extern crate log; - -use std::io; -use substrate_runtime_primitives::traits::{Block as BlockT, NumberFor}; - -type Metadata = apis::metadata::Metadata; -type RpcHandler = pubsub::PubSubHandler; -pub type HttpServer = http::Server; -pub type WsServer = ws::Server; - -/// Construct rpc `IoHandler` -pub fn rpc_handler( - state: S, - chain: C, - author: A, - system: Y, -) -> RpcHandler where - Block: BlockT + 'static, - ExHash: Send + Sync + 'static + substrate_runtime_primitives::Serialize + substrate_runtime_primitives::DeserializeOwned, - PendingExtrinsics: serde::Serialize + serde::de::DeserializeOwned + Send + Sync + 'static, - S: apis::state::StateApi, - C: apis::chain::ChainApi, Block::Extrinsic, Metadata=Metadata>, - A: apis::author::AuthorApi, - Y: apis::system::SystemApi, -{ - let mut io = pubsub::PubSubHandler::default(); - io.extend_with(state.to_delegate()); - io.extend_with(chain.to_delegate()); - io.extend_with(author.to_delegate()); - io.extend_with(system.to_delegate()); - io -} - -/// Start HTTP server listening on given address. -pub fn start_http( - addr: &std::net::SocketAddr, - io: RpcHandler, -) -> io::Result { - http::ServerBuilder::new(io) - .threads(4) - .rest_api(http::RestApi::Unsecure) - .cors(http::DomainsValidation::Disabled) - .start_http(addr) -} - -/// Start WS server listening on given address. -pub fn start_ws( - addr: &std::net::SocketAddr, - io: RpcHandler, -) -> io::Result { - ws::ServerBuilder::with_meta_extractor(io, |context: &ws::RequestContext| Metadata::new(context.sender())) - .start(addr) - .map_err(|err| match err { - ws::Error(ws::ErrorKind::Io(io), _) => io, - ws::Error(ws::ErrorKind::ConnectionClosed, _) => io::ErrorKind::BrokenPipe.into(), - ws::Error(e, _) => { - error!("{}", e); - io::ErrorKind::Other.into() - } - }) -} diff --git a/substrate/rpc/Cargo.toml b/substrate/rpc/Cargo.toml deleted file mode 100644 index 423a0523b8b69..0000000000000 --- a/substrate/rpc/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "substrate-rpc" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -error-chain = "0.12" -jsonrpc-core = { git="https://github.com/paritytech/jsonrpc.git" } -jsonrpc-macros = { git="https://github.com/paritytech/jsonrpc.git" } -jsonrpc-pubsub = { git="https://github.com/paritytech/jsonrpc.git" } -log = "0.3" -parking_lot = "0.4" -substrate-codec = { path = "../codec" } -substrate-client = { path = "../client" } -substrate-executor = { path = "../executor" } -substrate-extrinsic-pool = { path = "../extrinsic-pool" } -substrate-primitives = { path = "../primitives" } -substrate-runtime-primitives = { path = "../runtime/primitives" } -substrate-runtime-version = { path = "../runtime/version" } -substrate-state-machine = { path = "../state-machine" } -tokio = "0.1.7" -serde_json = "1.0" - -[dev-dependencies] -assert_matches = "1.1" -substrate-test-client = { path = "../test-client" } -rustc-hex = "2.0" diff --git a/substrate/rpc/README.adoc b/substrate/rpc/README.adoc deleted file mode 100644 index 5e4db4909976a..0000000000000 --- a/substrate/rpc/README.adoc +++ /dev/null @@ -1,13 +0,0 @@ - -= RPC - -.Summary -[source, toml] ----- -include::Cargo.toml[lines=2..5] ----- - -.Description ----- -include::src/lib.rs[tag=description] ----- diff --git a/substrate/rpc/src/author/error.rs b/substrate/rpc/src/author/error.rs deleted file mode 100644 index e831770056271..0000000000000 --- a/substrate/rpc/src/author/error.rs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2017 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 . - -//! Authoring RPC module errors. - -use client; -use extrinsic_pool; -use rpc; - -use errors; - -error_chain! { - links { - Pool(extrinsic_pool::Error, extrinsic_pool::ErrorKind) #[doc = "Pool error"]; - Client(client::error::Error, client::error::ErrorKind) #[doc = "Client error"]; - } - errors { - /// Not implemented yet - Unimplemented { - description("not yet implemented"), - display("Method Not Implemented"), - } - /// Incorrect extrinsic format. - BadFormat { - description("bad format"), - display("Invalid extrinsic format"), - } - /// Verification error - Verification(e: Box<::std::error::Error + Send>) { - description("extrinsic verification error"), - display("Extrinsic verification error: {}", e.description()), - } - } -} - -const ERROR: i64 = 1000; - -impl From for rpc::Error { - fn from(e: Error) -> Self { - match e { - Error(ErrorKind::Unimplemented, _) => errors::unimplemented(), - Error(ErrorKind::BadFormat, _) => rpc::Error { - code: rpc::ErrorCode::ServerError(ERROR + 1), - message: "Extrinsic has invalid format.".into(), - data: None, - }, - Error(ErrorKind::Verification(e), _) => rpc::Error { - code: rpc::ErrorCode::ServerError(ERROR + 2), - message: e.description().into(), - data: Some(format!("{:?}", e).into()), - }, - e => errors::internal(e), - } - } -} diff --git a/substrate/rpc/src/author/mod.rs b/substrate/rpc/src/author/mod.rs deleted file mode 100644 index 5ba5140c5ee52..0000000000000 --- a/substrate/rpc/src/author/mod.rs +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! Substrate block-author/full-node API. - -use std::sync::Arc; - -use client::{self, Client}; -use codec::Decode; -use extrinsic_pool::{ - Pool, - IntoPoolError, - ChainApi as PoolChainApi, - watcher::Status, - VerifiedTransaction, - AllExtrinsics, - ExHash, - ExtrinsicFor, -}; -use jsonrpc_macros::pubsub; -use jsonrpc_pubsub::SubscriptionId; -use primitives::{Bytes, Blake2Hasher, RlpCodec}; -use rpc::futures::{Sink, Stream, Future}; -use runtime_primitives::{generic, traits}; -use subscriptions::Subscriptions; -use tokio::runtime::TaskExecutor; - -pub mod error; - -#[cfg(test)] -mod tests; - -use self::error::Result; - -build_rpc_trait! { - /// Substrate authoring RPC API - pub trait AuthorApi { - type Metadata; - - /// Submit extrinsic for inclusion in block. - #[rpc(name = "author_submitRichExtrinsic")] - fn submit_rich_extrinsic(&self, Extrinsic) -> Result; - /// Submit hex-encoded extrinsic for inclusion in block. - #[rpc(name = "author_submitExtrinsic")] - fn submit_extrinsic(&self, Bytes) -> Result; - - /// Returns all pending extrinsics, potentially grouped by sender. - #[rpc(name = "author_pendingExtrinsics")] - fn pending_extrinsics(&self) -> Result; - - #[pubsub(name = "author_extrinsicUpdate")] { - /// Submit an extrinsic to watch. - #[rpc(name = "author_submitAndWatchExtrinsic")] - fn watch_extrinsic(&self, Self::Metadata, pubsub::Subscriber>, Bytes); - - /// Unsubscribe from extrinsic watching. - #[rpc(name = "author_unwatchExtrinsic")] - fn unwatch_extrinsic(&self, SubscriptionId) -> Result; - } - - } -} - -/// Authoring API -pub struct Author where - P: PoolChainApi + Sync + Send + 'static, -{ - /// Substrate client - client: Arc::Block>>, - /// Extrinsic pool - pool: Arc>, - /// Subscriptions manager - subscriptions: Subscriptions, -} - -impl Author where - P: PoolChainApi + Sync + Send + 'static, -{ - /// Create new instance of Authoring API. - pub fn new(client: Arc::Block>>, pool: Arc>, executor: TaskExecutor) -> Self { - Author { - client, - pool, - subscriptions: Subscriptions::new(executor), - } - } -} - -impl AuthorApi, ExtrinsicFor

, AllExtrinsics

> for Author where - B: client::backend::Backend<

::Block, Blake2Hasher, RlpCodec> + Send + Sync + 'static, - E: client::CallExecutor<

::Block, Blake2Hasher, RlpCodec> + Send + Sync + 'static, - P: PoolChainApi + Sync + Send + 'static, - P::Error: 'static, -{ - type Metadata = ::metadata::Metadata; - - fn submit_extrinsic(&self, xt: Bytes) -> Result> { - let dxt = Decode::decode(&mut &xt[..]).ok_or(error::Error::from(error::ErrorKind::BadFormat))?; - self.submit_rich_extrinsic(dxt) - } - - fn submit_rich_extrinsic(&self, xt: <

::Block as traits::Block>::Extrinsic) -> Result> { - let best_block_hash = self.client.info()?.chain.best_hash; - self.pool - .submit_one(&generic::BlockId::hash(best_block_hash), xt) - .map_err(|e| e.into_pool_error() - .map(Into::into) - .unwrap_or_else(|e| error::ErrorKind::Verification(Box::new(e)).into()) - ) - .map(|ex| ex.hash().clone()) - } - - fn pending_extrinsics(&self) -> Result> { - Ok(self.pool.all()) - } - - fn watch_extrinsic(&self, _metadata: Self::Metadata, subscriber: pubsub::Subscriber>>, xt: Bytes) { - let submit = || -> Result<_> { - let best_block_hash = self.client.info()?.chain.best_hash; - let dxt = <

::Block as traits::Block>::Extrinsic::decode(&mut &xt[..]).ok_or(error::Error::from(error::ErrorKind::BadFormat))?; - self.pool - .submit_and_watch(&generic::BlockId::hash(best_block_hash), dxt) - .map_err(|e| e.into_pool_error() - .map(Into::into) - .unwrap_or_else(|e| error::ErrorKind::Verification(Box::new(e)).into()) - ) - }; - - let watcher = match submit() { - Ok(watcher) => watcher, - Err(err) => { - // reject the subscriber (ignore errors - we don't care if subscriber is no longer there). - let _ = subscriber.reject(err.into()); - return; - }, - }; - - self.subscriptions.add(subscriber, move |sink| { - sink - .sink_map_err(|e| warn!("Error sending notifications: {:?}", e)) - .send_all(watcher.into_stream().map(Ok)) - .map(|_| ()) - }) - } - - fn unwatch_extrinsic(&self, id: SubscriptionId) -> Result { - Ok(self.subscriptions.cancel(id)) - } -} diff --git a/substrate/rpc/src/author/tests.rs b/substrate/rpc/src/author/tests.rs deleted file mode 100644 index e5cdf759da4fb..0000000000000 --- a/substrate/rpc/src/author/tests.rs +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -use super::*; - -use std::{sync::Arc, result::Result}; -use codec::Encode; -use extrinsic_pool::{VerifiedTransaction, scoring, Transaction, ChainApi, Error as PoolError, - Readiness, ExtrinsicFor, VerifiedFor}; -use test_client::runtime::{Block, Extrinsic, Transfer}; -use test_client; -use tokio::runtime; -use runtime_primitives::generic::BlockId; - -#[derive(Clone, Debug)] -pub struct Verified -{ - sender: u64, - hash: u64, -} - -impl VerifiedTransaction for Verified { - type Hash = u64; - type Sender = u64; - - fn hash(&self) -> &Self::Hash { &self.hash } - fn sender(&self) -> &Self::Sender { &self.sender } - fn mem_usage(&self) -> usize { 256 } -} - -struct TestApi; - -impl ChainApi for TestApi { - type Block = Block; - type Hash = u64; - type Sender = u64; - type Error = PoolError; - type VEx = Verified; - type Score = u64; - type Event = (); - type Ready = (); - - fn verify_transaction(&self, _at: &BlockId, uxt: &ExtrinsicFor) -> Result { - Ok(Verified { - sender: uxt.transfer.from[31] as u64, - hash: uxt.transfer.nonce, - }) - } - - fn is_ready(&self, _at: &BlockId, _c: &mut Self::Ready, _xt: &VerifiedFor) -> Readiness { - Readiness::Ready - } - - fn ready(&self) -> Self::Ready { } - - fn compare(old: &VerifiedFor, other: &VerifiedFor) -> ::std::cmp::Ordering { - old.verified.hash().cmp(&other.verified.hash()) - } - - fn choose(_old: &VerifiedFor, _new: &VerifiedFor) -> scoring::Choice { - scoring::Choice::ReplaceOld - } - - fn update_scores(xts: &[Transaction>], scores: &mut [Self::Score], _change: scoring::Change<()>) { - for i in 0..xts.len() { - scores[i] = xts[i].verified.sender - } - } - - fn should_replace(_old: &VerifiedFor, _new: &VerifiedFor) -> scoring::Choice { - scoring::Choice::ReplaceOld - } -} - -type DummyTxPool = Pool; - -fn uxt(sender: u64, hash: u64) -> Extrinsic { - Extrinsic { - signature: Default::default(), - transfer: Transfer { - amount: Default::default(), - nonce: hash, - from: From::from(sender), - to: Default::default(), - } - } -} - -#[test] -fn submit_transaction_should_not_cause_error() { - let runtime = runtime::Runtime::new().unwrap(); - let p = Author { - client: Arc::new(test_client::new()), - pool: Arc::new(DummyTxPool::new(Default::default(), TestApi)), - subscriptions: Subscriptions::new(runtime.executor()), - }; - - assert_matches!( - AuthorApi::submit_extrinsic(&p, uxt(5, 1).encode().into()), - Ok(1) - ); - assert!( - AuthorApi::submit_extrinsic(&p, uxt(5, 1).encode().into()).is_err() - ); -} - -#[test] -fn submit_rich_transaction_should_not_cause_error() { - let runtime = runtime::Runtime::new().unwrap(); - let p = Author { - client: Arc::new(test_client::new()), - pool: Arc::new(DummyTxPool::new(Default::default(), TestApi)), - subscriptions: Subscriptions::new(runtime.executor()), - }; - - assert_matches!( - AuthorApi::submit_rich_extrinsic(&p, uxt(5, 0)), - Ok(0) - ); - assert!( - AuthorApi::submit_rich_extrinsic(&p, uxt(5, 0)).is_err() - ); -} - -#[test] -fn should_watch_extrinsic() { - //given - let mut runtime = runtime::Runtime::new().unwrap(); - let pool = Arc::new(DummyTxPool::new(Default::default(), TestApi)); - let p = Author { - client: Arc::new(test_client::new()), - pool: pool.clone(), - subscriptions: Subscriptions::new(runtime.executor()), - }; - let (subscriber, id_rx, data) = ::jsonrpc_macros::pubsub::Subscriber::new_test("test"); - - // when - p.watch_extrinsic(Default::default(), subscriber, uxt(5, 5).encode().into()); - - // then - assert_eq!(runtime.block_on(id_rx), Ok(Ok(0.into()))); - // check notifications - AuthorApi::submit_rich_extrinsic(&p, uxt(5, 1)).unwrap(); - assert_eq!( - runtime.block_on(data.into_future()).unwrap().0, - Some(r#"{"jsonrpc":"2.0","method":"test","params":{"result":{"usurped":1},"subscription":0}}"#.into()) - ); -} - -#[test] -fn should_return_pending_extrinsics() { - let runtime = runtime::Runtime::new().unwrap(); - let pool = Arc::new(DummyTxPool::new(Default::default(), TestApi)); - let p = Author { - client: Arc::new(test_client::new()), - pool: pool.clone(), - subscriptions: Subscriptions::new(runtime.executor()), - }; - let ex = uxt(5, 1); - AuthorApi::submit_rich_extrinsic(&p, ex.clone()).unwrap(); - assert_matches!( - p.pending_extrinsics(), - Ok(ref expected) if expected.get(&5) == Some(&vec![ex]) - ); -} diff --git a/substrate/rpc/src/chain/error.rs b/substrate/rpc/src/chain/error.rs deleted file mode 100644 index 2c42e8c98956f..0000000000000 --- a/substrate/rpc/src/chain/error.rs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2017 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 client; -use rpc; - -use errors; - -error_chain! { - links { - Client(client::error::Error, client::error::ErrorKind) #[doc = "Client error"]; - } - errors { - /// Not implemented yet - Unimplemented { - description("not yet implemented"), - display("Method Not Implemented"), - } - } -} - -impl From for rpc::Error { - fn from(e: Error) -> Self { - match e { - Error(ErrorKind::Unimplemented, _) => errors::unimplemented(), - e => errors::internal(e), - } - } -} diff --git a/substrate/rpc/src/chain/mod.rs b/substrate/rpc/src/chain/mod.rs deleted file mode 100644 index 86551e04847d0..0000000000000 --- a/substrate/rpc/src/chain/mod.rs +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! Substrate blockchain API. - -use std::sync::Arc; - -use client::{self, Client, BlockchainEvents}; -use jsonrpc_macros::{pubsub, Trailing}; -use jsonrpc_pubsub::SubscriptionId; -use rpc::Result as RpcResult; -use rpc::futures::{stream, Future, Sink, Stream}; -use runtime_primitives::generic::{BlockId, SignedBlock}; -use runtime_primitives::traits::{Block as BlockT, Header, NumberFor}; -use runtime_version::RuntimeVersion; -use tokio::runtime::TaskExecutor; -use primitives::{Blake2Hasher, RlpCodec}; - -use subscriptions::Subscriptions; - -mod error; -#[cfg(test)] -mod tests; - -use self::error::Result; - -build_rpc_trait! { - /// Polkadot blockchain API - pub trait ChainApi { - type Metadata; - - /// Get header of a relay chain block. - #[rpc(name = "chain_getHeader")] - fn header(&self, Trailing) -> Result>; - - /// Get header and body of a relay chain block. - #[rpc(name = "chain_getBlock")] - fn block(&self, Trailing) -> Result>>; - - /// Get hash of the n-th block in the canon chain. - /// - /// By default returns latest block hash. - #[rpc(name = "chain_getBlockHash", alias = ["chain_getHead", ])] - fn block_hash(&self, Trailing) -> Result>; - - /// Get the runtime version. - #[rpc(name = "chain_getRuntimeVersion")] - fn runtime_version(&self, Trailing) -> Result; - - #[pubsub(name = "chain_newHead")] { - /// New head subscription - #[rpc(name = "chain_subscribeNewHead", alias = ["subscribe_newHead", ])] - fn subscribe_new_head(&self, Self::Metadata, pubsub::Subscriber

); - - /// Unsubscribe from new head subscription. - #[rpc(name = "chain_unsubscribeNewHead", alias = ["unsubscribe_newHead", ])] - fn unsubscribe_new_head(&self, SubscriptionId) -> RpcResult; - } - } -} - -/// Chain API with subscriptions support. -pub struct Chain { - /// Substrate client. - client: Arc>, - /// Current subscriptions. - subscriptions: Subscriptions, -} - -impl Chain { - /// Create new Chain API RPC handler. - pub fn new(client: Arc>, executor: TaskExecutor) -> Self { - Self { - client, - subscriptions: Subscriptions::new(executor), - } - } -} - -impl Chain where - Block: BlockT + 'static, - B: client::backend::Backend + Send + Sync + 'static, - E: client::CallExecutor + Send + Sync + 'static, -{ - fn unwrap_or_best(&self, hash: Trailing) -> Result { - Ok(match hash.into() { - None => self.client.info()?.chain.best_hash, - Some(hash) => hash, - }) - } -} - -impl ChainApi, Block::Extrinsic> for Chain where - Block: BlockT + 'static, - B: client::backend::Backend + Send + Sync + 'static, - E: client::CallExecutor + Send + Sync + 'static, -{ - type Metadata = ::metadata::Metadata; - - fn header(&self, hash: Trailing) -> Result> { - let hash = self.unwrap_or_best(hash)?; - Ok(self.client.header(&BlockId::Hash(hash))?) - } - - fn block(&self, hash: Trailing) -> Result>> { - let hash = self.unwrap_or_best(hash)?; - Ok(self.client.block(&BlockId::Hash(hash))?) - } - - fn block_hash(&self, number: Trailing>) -> Result> { - Ok(match number.into() { - None => Some(self.client.info()?.chain.best_hash), - Some(number) => self.client.header(&BlockId::number(number))?.map(|h| h.hash()), - }) - } - - fn runtime_version(&self, at: Trailing) -> Result { - let at = self.unwrap_or_best(at)?; - Ok(self.client.runtime_version_at(&BlockId::Hash(at))?) - } - - fn subscribe_new_head(&self, _metadata: Self::Metadata, subscriber: pubsub::Subscriber) { - self.subscriptions.add(subscriber, |sink| { - // send current head right at the start. - let header = self.block_hash(None.into()) - .and_then(|hash| self.header(hash.into())) - .and_then(|header| { - header.ok_or_else(|| self::error::ErrorKind::Unimplemented.into()) - }) - .map_err(Into::into); - - // send further subscriptions - let stream = self.client.import_notification_stream() - .filter(|notification| notification.is_new_best) - .map(|notification| Ok(notification.header)) - .map_err(|e| warn!("Block notification stream error: {:?}", e)); - - sink - .sink_map_err(|e| warn!("Error sending notifications: {:?}", e)) - .send_all( - stream::iter_result(vec![Ok(header)]) - .chain(stream) - ) - // we ignore the resulting Stream (if the first stream is over we are unsubscribed) - .map(|_| ()) - }); - } - - fn unsubscribe_new_head(&self, id: SubscriptionId) -> RpcResult { - Ok(self.subscriptions.cancel(id)) - } -} diff --git a/substrate/rpc/src/chain/tests.rs b/substrate/rpc/src/chain/tests.rs deleted file mode 100644 index def410532cd25..0000000000000 --- a/substrate/rpc/src/chain/tests.rs +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -use super::*; -use jsonrpc_macros::pubsub; -use client::BlockOrigin; -use test_client::{self, TestClient}; -use test_client::runtime::{Block, Header}; - -#[test] -fn should_return_header() { - let core = ::tokio::runtime::Runtime::new().unwrap(); - let remote = core.executor(); - - let client = Chain { - client: Arc::new(test_client::new()), - subscriptions: Subscriptions::new(remote), - }; - - assert_matches!( - client.header(Some(client.client.genesis_hash()).into()), - Ok(Some(ref x)) if x == &Header { - parent_hash: 0.into(), - number: 0, - state_root: x.state_root.clone(), - extrinsics_root: "45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0".into(), - digest: Default::default(), - } - ); - - assert_matches!( - client.header(None.into()), - Ok(Some(ref x)) if x == &Header { - parent_hash: 0.into(), - number: 0, - state_root: x.state_root.clone(), - extrinsics_root: "45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0".into(), - digest: Default::default(), - } - ); - - assert_matches!( - client.header(Some(5.into()).into()), - Ok(None) - ); -} - -#[test] -fn should_return_a_block() { - let core = ::tokio::runtime::Runtime::new().unwrap(); - let remote = core.executor(); - - let api = Chain { - client: Arc::new(test_client::new()), - subscriptions: Subscriptions::new(remote), - }; - - let block = api.client.new_block().unwrap().bake().unwrap(); - let block_hash = block.hash(); - api.client.justify_and_import(BlockOrigin::Own, block).unwrap(); - - - // Genesis block is not justified, so we can't query it? - assert_matches!( - api.block(Some(api.client.genesis_hash()).into()), - Ok(None) - ); - - assert_matches!( - api.block(Some(block_hash).into()), - Ok(Some(ref x)) if x.block == Block { - header: Header { - parent_hash: api.client.genesis_hash(), - number: 1, - state_root: x.block.header.state_root.clone(), - extrinsics_root: "45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0".into(), - digest: Default::default(), - }, - extrinsics: vec![], - } - ); - - assert_matches!( - api.block(None.into()), - Ok(Some(ref x)) if x.block == Block { - header: Header { - parent_hash: api.client.genesis_hash(), - number: 1, - state_root: x.block.header.state_root.clone(), - extrinsics_root: "45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0".into(), - digest: Default::default(), - }, - extrinsics: vec![], - } - ); - - assert_matches!( - api.block(Some(5.into()).into()), - Ok(None) - ); -} - -#[test] -fn should_return_block_hash() { - let core = ::tokio::runtime::Runtime::new().unwrap(); - let remote = core.executor(); - - let client = Chain { - client: Arc::new(test_client::new()), - subscriptions: Subscriptions::new(remote), - }; - - assert_matches!( - client.block_hash(None.into()), - Ok(Some(ref x)) if x == &client.client.genesis_hash() - ); - - - assert_matches!( - client.block_hash(Some(0u64).into()), - Ok(Some(ref x)) if x == &client.client.genesis_hash() - ); - - assert_matches!( - client.block_hash(Some(1u64).into()), - Ok(None) - ); - - let block = client.client.new_block().unwrap().bake().unwrap(); - client.client.justify_and_import(BlockOrigin::Own, block.clone()).unwrap(); - - assert_matches!( - client.block_hash(Some(0u64).into()), - Ok(Some(ref x)) if x == &client.client.genesis_hash() - ); - assert_matches!( - client.block_hash(Some(1u64).into()), - Ok(Some(ref x)) if x == &block.hash() - ); - -} - -#[test] -fn should_notify_about_latest_block() { - let mut core = ::tokio::runtime::Runtime::new().unwrap(); - let remote = core.executor(); - let (subscriber, id, transport) = pubsub::Subscriber::new_test("test"); - - { - let api = Chain { - client: Arc::new(test_client::new()), - subscriptions: Subscriptions::new(remote), - }; - - api.subscribe_new_head(Default::default(), subscriber); - - // assert id assigned - assert_eq!(core.block_on(id), Ok(Ok(SubscriptionId::Number(0)))); - - let builder = api.client.new_block().unwrap(); - api.client.justify_and_import(BlockOrigin::Own, builder.bake().unwrap()).unwrap(); - } - - // assert initial head sent. - let (notification, next) = core.block_on(transport.into_future()).unwrap(); - assert!(notification.is_some()); - // assert notification sent to transport - let (notification, next) = core.block_on(next.into_future()).unwrap(); - assert!(notification.is_some()); - // no more notifications on this channel - assert_eq!(core.block_on(next.into_future()).unwrap().0, None); -} - -#[test] -fn should_return_runtime_version() { - let core = ::tokio::runtime::Runtime::new().unwrap(); - let remote = core.executor(); - - let client = Chain { - client: Arc::new(test_client::new()), - subscriptions: Subscriptions::new(remote), - }; - - assert_matches!( - client.runtime_version(None.into()), - Ok(ref ver) if ver == &RuntimeVersion { - spec_name: "test".into(), - impl_name: "parity-test".into(), - authoring_version: 1, - spec_version: 1, - impl_version: 1, - } - ); -} diff --git a/substrate/rpc/src/errors.rs b/substrate/rpc/src/errors.rs deleted file mode 100644 index a9b9e27a9cbc6..0000000000000 --- a/substrate/rpc/src/errors.rs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2018 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 rpc; - -pub fn unimplemented() -> rpc::Error { - rpc::Error { - code: rpc::ErrorCode::ServerError(1), - message: "Not implemented yet".into(), - data: None, - } -} - -pub fn internal(e: E) -> rpc::Error { - warn!("Unknown error: {:?}", e); - rpc::Error { - code: rpc::ErrorCode::InternalError, - message: "Unknown error occured".into(), - data: Some(format!("{:?}", e).into()), - } -} diff --git a/substrate/rpc/src/helpers.rs b/substrate/rpc/src/helpers.rs deleted file mode 100644 index dca1a45db563d..0000000000000 --- a/substrate/rpc/src/helpers.rs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2018 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 . - -/// Unwraps the trailing parameter or falls back with the closure result. -pub fn unwrap_or_else(or_else: F, optional: ::jsonrpc_macros::Trailing) -> Result where - F: FnOnce() -> Result, -{ - match optional.into() { - None => or_else(), - Some(x) => Ok(x), - } -} diff --git a/substrate/rpc/src/lib.rs b/substrate/rpc/src/lib.rs deleted file mode 100644 index b4e07fc823e80..0000000000000 --- a/substrate/rpc/src/lib.rs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -// tag::description[] -//! Substrate RPC interfaces. -// end::description[] - -#![warn(missing_docs)] - -extern crate jsonrpc_core as rpc; -extern crate jsonrpc_pubsub; -extern crate parking_lot; -extern crate substrate_codec as codec; -extern crate substrate_client as client; -extern crate substrate_extrinsic_pool as extrinsic_pool; -extern crate substrate_primitives as primitives; -extern crate substrate_runtime_primitives as runtime_primitives; -extern crate substrate_state_machine as state_machine; -extern crate substrate_runtime_version as runtime_version; -extern crate tokio; -extern crate serde_json; - -#[macro_use] -extern crate error_chain; -#[macro_use] -extern crate jsonrpc_macros; -#[macro_use] -extern crate log; - -#[cfg(test)] -#[macro_use] -extern crate assert_matches; -#[cfg(test)] -extern crate substrate_test_client as test_client; -#[cfg(test)] -extern crate rustc_hex; - -mod errors; -mod helpers; -mod subscriptions; - -pub mod author; -pub mod chain; -pub mod metadata; -pub mod state; -pub mod system; diff --git a/substrate/rpc/src/metadata.rs b/substrate/rpc/src/metadata.rs deleted file mode 100644 index 06d19d11d8cec..0000000000000 --- a/substrate/rpc/src/metadata.rs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2017-2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! RPC Metadata -use std::sync::Arc; - -use jsonrpc_pubsub::{Session, PubSubMetadata}; -use rpc::futures::sync::mpsc; - -/// RPC Metadata. -/// -/// Manages persistent session for transports that support it -/// and may contain some additional info extracted from specific transports -/// (like remote client IP address, request headers, etc) -#[derive(Default, Clone)] -pub struct Metadata { - session: Option>, -} - -impl ::rpc::Metadata for Metadata {} -impl PubSubMetadata for Metadata { - fn session(&self) -> Option> { - self.session.clone() - } -} - -impl Metadata { - /// Create new `Metadata` with session (Pub/Sub) support. - pub fn new(transport: mpsc::Sender) -> Self { - Metadata { - session: Some(Arc::new(Session::new(transport))), - } - } - - /// Create new `Metadata` for tests. - #[cfg(test)] - pub fn new_test() -> (mpsc::Receiver, Self) { - let (tx, rx) = mpsc::channel(1); - (rx, Self::new(tx)) - } -} diff --git a/substrate/rpc/src/state/error.rs b/substrate/rpc/src/state/error.rs deleted file mode 100644 index de65e466c08b9..0000000000000 --- a/substrate/rpc/src/state/error.rs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2017 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 client; -use rpc; - -use errors; - -use serde_json; - -error_chain! { - links { - Client(client::error::Error, client::error::ErrorKind) #[doc = "Client error"]; - } - - foreign_links { - Json(serde_json::Error); - } - - errors { - /// Provided block range couldn't be resolved to a list of blocks. - InvalidBlockRange(from: String, to: String, details: String) { - description("Invalid block range"), - display("Cannot resolve a block range ['{:?}' ... '{:?}]. {}", from, to, details), - } - /// Not implemented yet - Unimplemented { - description("not implemented yet"), - display("Method Not Implemented"), - } - } -} - -impl From for rpc::Error { - fn from(e: Error) -> Self { - match e { - Error(ErrorKind::Unimplemented, _) => errors::unimplemented(), - e => errors::internal(e), - } - } -} diff --git a/substrate/rpc/src/state/mod.rs b/substrate/rpc/src/state/mod.rs deleted file mode 100644 index e335310af74aa..0000000000000 --- a/substrate/rpc/src/state/mod.rs +++ /dev/null @@ -1,277 +0,0 @@ -// Copyright 2017 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 . - -//! Polkadot state API. - -use std::{ - collections::HashMap, - sync::Arc, -}; - -use client::{self, Client, CallExecutor, BlockchainEvents}; -use jsonrpc_macros::Trailing; -use jsonrpc_macros::pubsub; -use jsonrpc_pubsub::SubscriptionId; -use primitives::hexdisplay::HexDisplay; -use primitives::storage::{StorageKey, StorageData, StorageChangeSet}; -use primitives::{Blake2Hasher, RlpCodec}; -use rpc::Result as RpcResult; -use rpc::futures::{stream, Future, Sink, Stream}; -use runtime_primitives::generic::BlockId; -use runtime_primitives::traits::{Block as BlockT, Header}; -use tokio::runtime::TaskExecutor; -use serde_json; - -use subscriptions::Subscriptions; - -mod error; -#[cfg(test)] -mod tests; - -use self::error::Result; - -build_rpc_trait! { - /// Polkadot state API - pub trait StateApi { - type Metadata; - - /// Call a contract at a block's state. - #[rpc(name = "state_call", alias = ["state_callAt", ])] - fn call(&self, String, Vec, Trailing) -> Result>; - - /// Returns a storage entry at a specific block's state. - #[rpc(name = "state_getStorage", alias = ["state_getStorageAt", ])] - fn storage(&self, StorageKey, Trailing) -> Result>; - - /// Returns the hash of a storage entry at a block's state. - #[rpc(name = "state_getStorageHash", alias = ["state_getStorageHashAt", ])] - fn storage_hash(&self, StorageKey, Trailing) -> Result>; - - /// Returns the size of a storage entry at a block's state. - #[rpc(name = "state_getStorageSize", alias = ["state_getStorageSizeAt", ])] - fn storage_size(&self, StorageKey, Trailing) -> Result>; - - /// Returns the runtime metadata as JSON. - #[rpc(name = "state_getMetadata")] - fn metadata(&self, Trailing) -> Result; - - /// Query historical storage entries (by key) starting from a block given as the second parameter. - /// - /// NOTE This first returned result contains the initial state of storage for all keys. - /// Subsequent values in the vector represent changes to the previous state (diffs). - #[rpc(name = "state_queryStorage")] - fn query_storage(&self, Vec, Hash, Trailing) -> Result>>; - - #[pubsub(name = "state_storage")] { - /// New storage subscription - #[rpc(name = "state_subscribeStorage")] - fn subscribe_storage(&self, Self::Metadata, pubsub::Subscriber>, Trailing>); - - /// Unsubscribe from storage subscription - #[rpc(name = "state_unsubscribeStorage")] - fn unsubscribe_storage(&self, SubscriptionId) -> RpcResult; - } - } -} - -/// State API with subscriptions support. -pub struct State { - /// Substrate client. - client: Arc>, - /// Current subscriptions. - subscriptions: Subscriptions, -} - -impl State { - /// Create new State API RPC handler. - pub fn new(client: Arc>, executor: TaskExecutor) -> Self { - Self { - client, - subscriptions: Subscriptions::new(executor), - } - } -} - -impl State where - Block: BlockT, - B: client::backend::Backend, - E: CallExecutor, -{ - fn unwrap_or_best(&self, hash: Trailing) -> Result { - ::helpers::unwrap_or_else(|| Ok(self.client.info()?.chain.best_hash), hash) - } -} - -impl StateApi for State where - Block: BlockT + 'static, - B: client::backend::Backend + Send + Sync + 'static, - E: CallExecutor + Send + Sync + 'static, -{ - type Metadata = ::metadata::Metadata; - - fn call(&self, method: String, data: Vec, block: Trailing) -> Result> { - let block = self.unwrap_or_best(block)?; - trace!(target: "rpc", "Calling runtime at {:?} for method {} ({})", block, method, HexDisplay::from(&data)); - Ok(self.client.executor().call(&BlockId::Hash(block), &method, &data)?.return_data) - } - - fn storage(&self, key: StorageKey, block: Trailing) -> Result> { - let block = self.unwrap_or_best(block)?; - trace!(target: "rpc", "Querying storage at {:?} for key {}", block, HexDisplay::from(&key.0)); - Ok(self.client.storage(&BlockId::Hash(block), &key)?) - } - - fn storage_hash(&self, key: StorageKey, block: Trailing) -> Result> { - use runtime_primitives::traits::{Hash, Header as HeaderT}; - Ok(self.storage(key, block)?.map(|x| ::Hashing::hash(&x.0))) - } - - fn storage_size(&self, key: StorageKey, block: Trailing) -> Result> { - Ok(self.storage(key, block)?.map(|x| x.0.len() as u64)) - } - - fn metadata(&self, block: Trailing) -> Result { - let block = self.unwrap_or_best(block)?; - let metadata = self.client.json_metadata(&BlockId::Hash(block))?; - serde_json::from_str(&metadata).map_err(Into::into) - } - - fn query_storage(&self, keys: Vec, from: Block::Hash, to: Trailing) -> Result>> { - let to = self.unwrap_or_best(to)?; - - let from_hdr = self.client.header(&BlockId::hash(from))?; - let to_hdr = self.client.header(&BlockId::hash(to))?; - - match (from_hdr, to_hdr) { - (Some(ref from), Some(ref to)) if from.number() <= to.number() => { - let from = from.clone(); - let to = to.clone(); - // check if we can get from `to` to `from` by going through parent_hashes. - let blocks = { - let mut blocks = vec![to.hash()]; - let mut last = to.clone(); - while last.number() > from.number() { - if let Some(hdr) = self.client.header(&BlockId::hash(*last.parent_hash()))? { - blocks.push(hdr.hash()); - last = hdr; - } else { - bail!(invalid_block_range( - Some(from), - Some(to), - format!("Parent of {} ({}) not found", last.number(), last.hash()), - )) - } - } - if last.hash() != from.hash() { - bail!(invalid_block_range( - Some(from), - Some(to), - format!("Expected to reach `from`, got {} ({})", last.number(), last.hash()), - )) - } - blocks.reverse(); - blocks - }; - let mut result = Vec::new(); - let mut last_state: HashMap<_, Option<_>> = Default::default(); - for block in blocks { - let mut changes = vec![]; - let id = BlockId::hash(block.clone()); - - for key in &keys { - let (has_changed, data) = { - let curr_data = self.client.storage(&id, key)?; - let prev_data = last_state.get(key).and_then(|x| x.as_ref()); - - (curr_data.as_ref() != prev_data, curr_data) - }; - - if has_changed { - changes.push((key.clone(), data.clone())); - } - - last_state.insert(key.clone(), data); - } - - result.push(StorageChangeSet { - block, - changes, - }); - } - Ok(result) - }, - (from, to) => bail!(invalid_block_range(from, to, "Invalid range or unknown block".into())), - } - } - - fn subscribe_storage( - &self, - _meta: Self::Metadata, - subscriber: pubsub::Subscriber>, - keys: Trailing> - ) { - let keys = Into::>>::into(keys); - let stream = match self.client.storage_changes_notification_stream(keys.as_ref().map(|x| &**x)) { - Ok(stream) => stream, - Err(err) => { - let _ = subscriber.reject(error::Error::from(err).into()); - return; - }, - }; - - // initial values - let initial = stream::iter_result(keys - .map(|keys| { - let block = self.client.info().map(|info| info.chain.best_hash).unwrap_or_default(); - let changes = keys - .into_iter() - .map(|key| self.storage(key.clone(), Some(block.clone()).into()) - .map(|val| (key.clone(), val)) - .unwrap_or_else(|_| (key, None)) - ) - .collect(); - vec![Ok(Ok(StorageChangeSet { block, changes }))] - }).unwrap_or_default()); - - self.subscriptions.add(subscriber, |sink| { - let stream = stream - .map_err(|e| warn!("Error creating storage notification stream: {:?}", e)) - .map(|(block, changes)| Ok(StorageChangeSet { - block, - changes: changes.iter().cloned().collect(), - })); - - sink - .sink_map_err(|e| warn!("Error sending notifications: {:?}", e)) - .send_all(initial.chain(stream)) - // we ignore the resulting Stream (if the first stream is over we are unsubscribed) - .map(|_| ()) - }) - } - - fn unsubscribe_storage(&self, id: SubscriptionId) -> RpcResult { - Ok(self.subscriptions.cancel(id)) - } -} - -fn invalid_block_range(from: Option, to: Option, reason: String) -> error::ErrorKind { - let to_string = |x: Option| match x { - None => "unknown hash".into(), - Some(h) => format!("{} ({})", h.number(), h.hash()), - }; - - error::ErrorKind::InvalidBlockRange(to_string(from), to_string(to), reason) -} diff --git a/substrate/rpc/src/state/tests.rs b/substrate/rpc/src/state/tests.rs deleted file mode 100644 index bad934b8405a3..0000000000000 --- a/substrate/rpc/src/state/tests.rs +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -use super::*; -use self::error::{Error, ErrorKind}; - -use client::BlockOrigin; -use jsonrpc_macros::pubsub; -use rustc_hex::FromHex; -use test_client::{self, runtime, keyring::Keyring, TestClient, BlockBuilderExt}; - -#[test] -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, core.executor()); - - assert_matches!( - client.storage(StorageKey(vec![10]), Some(genesis_hash).into()), - Ok(None) - ) -} - -#[test] -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, core.executor()); - - assert_matches!( - client.call("balanceOf".into(), vec![1,2,3], Some(genesis_hash).into()), - Err(Error(ErrorKind::Client(client::error::ErrorKind::Execution(_)), _)) - ) -} - -#[test] -fn should_notify_about_storage_changes() { - let mut core = ::tokio::runtime::Runtime::new().unwrap(); - let remote = core.executor(); - let (subscriber, id, transport) = pubsub::Subscriber::new_test("test"); - - { - let api = State { - client: Arc::new(test_client::new()), - subscriptions: Subscriptions::new(remote), - }; - - api.subscribe_storage(Default::default(), subscriber, None.into()); - - // assert id assigned - assert_eq!(core.block_on(id), Ok(Ok(SubscriptionId::Number(0)))); - - let mut builder = api.client.new_block().unwrap(); - builder.push_transfer(runtime::Transfer { - from: Keyring::Alice.to_raw_public().into(), - to: Keyring::Ferdie.to_raw_public().into(), - amount: 42, - nonce: 0, - }).unwrap(); - api.client.justify_and_import(BlockOrigin::Own, builder.bake().unwrap()).unwrap(); - } - - // assert notification sent to transport - let (notification, next) = core.block_on(transport.into_future()).unwrap(); - assert!(notification.is_some()); - // no more notifications on this channel - assert_eq!(core.block_on(next.into_future()).unwrap().0, None); -} - -#[test] -fn should_send_initial_storage_changes_and_notifications() { - let mut core = ::tokio::runtime::Runtime::new().unwrap(); - let remote = core.executor(); - let (subscriber, id, transport) = pubsub::Subscriber::new_test("test"); - - { - let api = State { - client: Arc::new(test_client::new()), - subscriptions: Subscriptions::new(remote), - }; - - api.subscribe_storage(Default::default(), subscriber, Some(vec![ - StorageKey("a52da2b7c269da1366b3ed1cdb7299ce".from_hex().unwrap()), - ]).into()); - - // assert id assigned - assert_eq!(core.block_on(id), Ok(Ok(SubscriptionId::Number(0)))); - - let mut builder = api.client.new_block().unwrap(); - builder.push_transfer(runtime::Transfer { - from: Keyring::Alice.to_raw_public().into(), - to: Keyring::Ferdie.to_raw_public().into(), - amount: 42, - nonce: 0, - }).unwrap(); - api.client.justify_and_import(BlockOrigin::Own, builder.bake().unwrap()).unwrap(); - } - - // assert initial values sent to transport - let (notification, next) = core.block_on(transport.into_future()).unwrap(); - assert!(notification.is_some()); - // assert notification sent to transport - let (notification, next) = core.block_on(next.into_future()).unwrap(); - assert!(notification.is_some()); - // no more notifications on this channel - assert_eq!(core.block_on(next.into_future()).unwrap().0, None); -} - -#[test] -fn should_query_storage() { - let core = ::tokio::runtime::Runtime::new().unwrap(); - let client = Arc::new(test_client::new()); - let api = State::new(client.clone(), core.executor()); - - let add_block = |nonce| { - let mut builder = client.new_block().unwrap(); - builder.push_transfer(runtime::Transfer { - from: Keyring::Alice.to_raw_public().into(), - to: Keyring::Ferdie.to_raw_public().into(), - amount: 42, - nonce, - }).unwrap(); - let block = builder.bake().unwrap(); - let hash = block.header.hash(); - client.justify_and_import(BlockOrigin::Own, block).unwrap(); - hash - }; - let block1_hash = add_block(0); - let block2_hash = add_block(1); - let genesis_hash = client.genesis_hash(); - - - let mut expected = vec![ - StorageChangeSet { - block: genesis_hash, - changes: vec![ - (StorageKey("a52da2b7c269da1366b3ed1cdb7299ce".from_hex().unwrap()), Some(StorageData(vec![232, 3, 0, 0, 0, 0, 0, 0]))), - ], - }, - StorageChangeSet { - block: block1_hash, - changes: vec![ - (StorageKey("a52da2b7c269da1366b3ed1cdb7299ce".from_hex().unwrap()), Some(StorageData(vec![190, 3, 0, 0, 0, 0, 0, 0]))), - ], - }, - ]; - - // Query changes only up to block1 - let result = api.query_storage( - vec![StorageKey("a52da2b7c269da1366b3ed1cdb7299ce".from_hex().unwrap())], - genesis_hash, - Some(block1_hash).into(), - ); - - assert_eq!(result.unwrap(), expected); - - // Query all changes - let result = api.query_storage( - vec![StorageKey("a52da2b7c269da1366b3ed1cdb7299ce".from_hex().unwrap())], - genesis_hash, - None.into(), - ); - - expected.push(StorageChangeSet { - block: block2_hash, - changes: vec![ - (StorageKey("a52da2b7c269da1366b3ed1cdb7299ce".from_hex().unwrap()), Some(StorageData(vec![148, 3, 0, 0, 0, 0, 0, 0]))), - ], - }); - assert_eq!(result.unwrap(), expected); -} diff --git a/substrate/rpc/src/subscriptions.rs b/substrate/rpc/src/subscriptions.rs deleted file mode 100644 index 9013edf742e1c..0000000000000 --- a/substrate/rpc/src/subscriptions.rs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2017-2018 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::collections::HashMap; -use std::sync::atomic::{self, AtomicUsize}; - -use jsonrpc_macros::pubsub; -use jsonrpc_pubsub::SubscriptionId; -use parking_lot::Mutex; -use rpc::futures::sync::oneshot; -use rpc::futures::{Future, future}; -use tokio::runtime::TaskExecutor; - -type Id = u64; - -/// Subscriptions manager. -/// -/// Takes care of assigning unique subscription ids and -/// driving the sinks into completion. -#[derive(Debug)] -pub struct Subscriptions { - next_id: AtomicUsize, - active_subscriptions: Mutex>>, - executor: TaskExecutor, -} - -impl Subscriptions { - /// Creates new `Subscriptions` object. - pub fn new(executor: TaskExecutor) -> Self { - Subscriptions { - next_id: Default::default(), - active_subscriptions: Default::default(), - executor, - } - } - - /// Creates new subscription for given subscriber. - /// - /// Second parameter is a function that converts Subscriber sink into a future. - /// This future will be driven to completion bu underlying event loop - /// or will be cancelled in case #cancel is invoked. - pub fn add(&self, subscriber: pubsub::Subscriber, into_future: G) where - G: FnOnce(pubsub::Sink) -> R, - R: future::IntoFuture, - F: future::Future + Send + 'static, - { - let id = self.next_id.fetch_add(1, atomic::Ordering::AcqRel) as u64; - if let Ok(sink) = subscriber.assign_id(id.into()) { - let (tx, rx) = oneshot::channel(); - let future = into_future(sink) - .into_future() - .select(rx.map_err(|e| warn!("Error timeing out: {:?}", e))) - .then(|_| Ok(())); - - self.active_subscriptions.lock().insert(id, tx); - self.executor.spawn(future); - } - } - - /// Cancel subscription. - /// - /// Returns true if subscription existed or false otherwise. - pub fn cancel(&self, id: SubscriptionId) -> bool { - if let SubscriptionId::Number(id) = id { - if let Some(tx) = self.active_subscriptions.lock().remove(&id) { - let _ = tx.send(()); - return true; - } - } - false - } -} diff --git a/substrate/rpc/src/system/error.rs b/substrate/rpc/src/system/error.rs deleted file mode 100644 index 42d215caefd33..0000000000000 --- a/substrate/rpc/src/system/error.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2017 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 . - -//! System RPC module errors. - -use rpc; - -use errors; - -error_chain! { - errors { - /// Not implemented yet - Unimplemented { - description("not yet implemented"), - display("Method Not Implemented"), - } - } -} - -impl From for rpc::Error { - fn from(e: Error) -> Self { - match e { - Error(ErrorKind::Unimplemented, _) => errors::unimplemented(), - e => errors::internal(e), - } - } -} diff --git a/substrate/rpc/src/system/mod.rs b/substrate/rpc/src/system/mod.rs deleted file mode 100644 index 56d06be9e5cda..0000000000000 --- a/substrate/rpc/src/system/mod.rs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! Substrate system API. - -pub mod error; - -#[cfg(test)] -mod tests; - -use self::error::Result; - -build_rpc_trait! { - /// Substrate system RPC API - pub trait SystemApi { - /// Get the node's implementation name. Plain old string. - #[rpc(name = "system_name")] - fn system_name(&self) -> Result; - - /// Get the node implementation's version. Should be a semver string. - #[rpc(name = "system_version")] - fn system_version(&self) -> Result; - - /// Get the chain's type. Given as a string identifier. - #[rpc(name = "system_chain")] - fn system_chain(&self) -> Result; - } -} diff --git a/substrate/rpc/src/system/tests.rs b/substrate/rpc/src/system/tests.rs deleted file mode 100644 index f22cd5a157779..0000000000000 --- a/substrate/rpc/src/system/tests.rs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -use super::*; -use super::error::*; - -impl SystemApi for () { - fn system_name(&self) -> Result { - Ok("testclient".into()) - } - fn system_version(&self) -> Result { - Ok("0.2.0".into()) - } - fn system_chain(&self) -> Result { - Ok("testchain".into()) - } -} - -#[test] -fn system_name_works() { - assert_eq!( - SystemApi::system_name(&()).unwrap(), - "testclient".to_owned() - ); -} - -#[test] -fn system_version_works() { - assert_eq!( - SystemApi::system_version(&()).unwrap(), - "0.2.0".to_owned() - ); -} - -#[test] -fn system_chain_works() { - assert_eq!( - SystemApi::system_chain(&()).unwrap(), - "testchain".to_owned() - ); -} diff --git a/substrate/runtime-io/Cargo.toml b/substrate/runtime-io/Cargo.toml deleted file mode 100644 index cf8a01dcf8731..0000000000000 --- a/substrate/runtime-io/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ -[package] -name = "substrate-runtime-io" -version = "0.1.0" -authors = ["Parity Technologies "] -build = "build.rs" - -[build-dependencies] -rustc_version = "0.2" - -[dependencies] -substrate-runtime-std = { path = "../runtime-std", default_features = false } -environmental = { path = "../environmental", optional = true } -substrate-state-machine = { path = "../state-machine", optional = true } -substrate-primitives = { path = "../primitives", default_features = false } -substrate-codec = { path = "../codec", default_features = false } -triehash = { version = "0.2", optional = true } -ed25519 = { path = "../ed25519", optional = true } -hashdb = { version = "0.2", default_features = false } -rlp = { version = "0.2", optional = true, default_features = false } - -[features] -default = ["std"] -std = [ - "environmental", - "substrate-state-machine", - "triehash", - "substrate-primitives/std", - "substrate-codec/std", - "substrate-runtime-std/std", - "ed25519", - "rlp" -] -nightly = [] -strict = [] diff --git a/substrate/runtime-io/README.adoc b/substrate/runtime-io/README.adoc deleted file mode 100644 index c035be1461188..0000000000000 --- a/substrate/runtime-io/README.adoc +++ /dev/null @@ -1,13 +0,0 @@ - -= Runtime io - -.Summary -[source, toml] ----- -include::Cargo.toml[lines=2..5] ----- - -.Description ----- -include::src/lib.rs[tag=description] ----- diff --git a/substrate/runtime-io/build.rs b/substrate/runtime-io/build.rs deleted file mode 100644 index 35eb154f3a69a..0000000000000 --- a/substrate/runtime-io/build.rs +++ /dev/null @@ -1,14 +0,0 @@ -//! Set a nightly feature - -extern crate rustc_version; -use rustc_version::{version, version_meta, Channel}; - -fn main() { - // Assert we haven't travelled back in time - assert!(version().unwrap().major >= 1); - - // Set cfg flags depending on release channel - if let Channel::Nightly = version_meta().unwrap().channel { - println!("cargo:rustc-cfg=feature=\"nightly\""); - } -} diff --git a/substrate/runtime-io/src/lib.rs b/substrate/runtime-io/src/lib.rs deleted file mode 100644 index 9214a7aa37b98..0000000000000 --- a/substrate/runtime-io/src/lib.rs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -// tag::description[] -//! This is part of the Substrate runtime. -// end::description[] - -#![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(not(feature = "std"), feature(lang_items))] -#![cfg_attr(not(feature = "std"), feature(panic_handler))] -#![cfg_attr(not(feature = "std"), feature(alloc_error_handler))] -#![cfg_attr(not(feature = "std"), feature(core_intrinsics))] -#![cfg_attr(not(feature = "std"), feature(alloc))] - -#![cfg_attr(feature = "std", doc = "Substrate runtime standard library as compiled when linked with Rust's standard library.")] -#![cfg_attr(not(feature = "std"), doc = "Substrate's runtime standard library as compiled without Rust's standard library.")] - -#[cfg(feature = "std")] -include!("../with_std.rs"); - -#[cfg(not(feature = "std"))] -include!("../without_std.rs"); diff --git a/substrate/runtime-io/with_std.rs b/substrate/runtime-io/with_std.rs index c1d85d914ebef..8b868efed691f 100644 --- a/substrate/runtime-io/with_std.rs +++ b/substrate/runtime-io/with_std.rs @@ -22,7 +22,6 @@ extern crate substrate_primitives as primitives; extern crate substrate_state_machine; extern crate triehash; -extern crate ed25519; extern crate hashdb; extern crate rlp; diff --git a/substrate/runtime-io/without_std.rs b/substrate/runtime-io/without_std.rs deleted file mode 100644 index 34c4191e97e22..0000000000000 --- a/substrate/runtime-io/without_std.rs +++ /dev/null @@ -1,329 +0,0 @@ -// Copyright 2017 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 . - - -extern crate substrate_primitives as primitives; -extern crate hashdb; - -#[doc(hidden)] -pub extern crate substrate_runtime_std as rstd; - -#[doc(hidden)] -pub extern crate substrate_codec as codec; - -use core::intrinsics; -use rstd::vec::Vec; -use hashdb::Hasher; -use primitives::Blake2Hasher; -pub use rstd::{mem, slice}; - -#[panic_handler] -#[no_mangle] -pub fn panic(info: &::core::panic::PanicInfo) -> ! { - unsafe { - if let Some(loc) = info.location() { - ext_print_utf8(loc.file().as_ptr() as *const u8, loc.file().len() as u32); - ext_print_num(loc.line() as u64); - ext_print_num(loc.column() as u64); - } - intrinsics::abort() - } -} - -#[alloc_error_handler] -pub extern fn oom(_: ::core::alloc::Layout) -> ! { - static OOM_MSG: &str = "Runtime memory exhausted. Aborting"; - - unsafe { - ext_print_utf8(OOM_MSG.as_ptr(), OOM_MSG.len() as u32); - intrinsics::abort(); - } -} - -extern "C" { - fn ext_print_utf8(utf8_data: *const u8, utf8_len: u32); - fn ext_print_hex(data: *const u8, len: u32); - fn ext_print_num(value: u64); - fn ext_set_storage(key_data: *const u8, key_len: u32, value_data: *const u8, value_len: u32); - fn ext_clear_storage(key_data: *const u8, key_len: u32); - fn ext_exists_storage(key_data: *const u8, key_len: u32) -> u32; - fn ext_clear_prefix(prefix_data: *const u8, prefix_len: u32); - fn ext_get_allocated_storage(key_data: *const u8, key_len: u32, written_out: *mut u32) -> *mut u8; - fn ext_get_storage_into(key_data: *const u8, key_len: u32, value_data: *mut u8, value_len: u32, value_offset: u32) -> u32; - fn ext_storage_root(result: *mut u8); - fn ext_blake2_256_enumerated_trie_root(values_data: *const u8, lens_data: *const u32, lens_len: u32, result: *mut u8); - fn ext_chain_id() -> u64; - fn ext_blake2_256(data: *const u8, len: u32, out: *mut u8); - fn ext_twox_128(data: *const u8, len: u32, out: *mut u8); - fn ext_twox_256(data: *const u8, len: u32, out: *mut u8); - fn ext_ed25519_verify(msg_data: *const u8, msg_len: u32, sig_data: *const u8, pubkey_data: *const u8) -> u32; -} - -/// Ensures we use the right crypto when calling into native -pub trait ExternTrieCrypto { - fn enumerated_trie_root(values: &[&[u8]]) -> [u8; 32]; -} - -// Ensures we use a Blake2_256-flavoured Hasher when calling into native -impl ExternTrieCrypto for Blake2Hasher { - fn enumerated_trie_root(values: &[&[u8]]) -> [u8; 32] { - let lengths = values.iter().map(|v| (v.len() as u32).to_le()).collect::>(); - let values = values.iter().fold(Vec::new(), |mut acc, sl| { acc.extend_from_slice(sl); acc }); - let mut result: [u8; 32] = Default::default(); - unsafe { - ext_blake2_256_enumerated_trie_root( - values.as_ptr(), - lengths.as_ptr(), - lengths.len() as u32, - result.as_mut_ptr() - ); - } - result - } -} - -/// Get `key` from storage and return a `Vec`, empty if there's a problem. -pub fn storage(key: &[u8]) -> Option> { - let mut length: u32 = 0; - unsafe { - let ptr = ext_get_allocated_storage(key.as_ptr(), key.len() as u32, &mut length); - if length == u32::max_value() { - None - } else { - Some(Vec::from_raw_parts(ptr, length as usize, length as usize)) - } - } -} - -/// Set the storage of some particular key to Some value. -pub fn set_storage(key: &[u8], value: &[u8]) { - unsafe { - ext_set_storage( - key.as_ptr(), key.len() as u32, - value.as_ptr(), value.len() as u32 - ); - } -} - -/// Clear the storage of some particular key. -pub fn clear_storage(key: &[u8]) { - unsafe { - ext_clear_storage( - key.as_ptr(), key.len() as u32 - ); - } -} - -/// Determine whether a particular key exists in storage. -pub fn exists_storage(key: &[u8]) -> bool { - unsafe { - ext_exists_storage( - key.as_ptr(), key.len() as u32 - ) != 0 - } -} - -/// Clear the storage entries key of which starts with the given prefix. -pub fn clear_prefix(prefix: &[u8]) { - unsafe { - ext_clear_prefix( - prefix.as_ptr(), - prefix.len() as u32 - ); - } -} - -/// Get `key` from storage, placing the value into `value_out` (as much as possible) and return -/// the number of bytes that the key in storage was beyond the offset. -pub fn read_storage(key: &[u8], value_out: &mut [u8], value_offset: usize) -> Option { - unsafe { - match ext_get_storage_into( - key.as_ptr(), key.len() as u32, - value_out.as_mut_ptr(), value_out.len() as u32, - value_offset as u32 - ) { - none if none == u32::max_value() => None, - length => Some(length as usize), - } - } -} - -/// The current storage's root. -pub fn storage_root() -> [u8; 32] { - let mut result: [u8; 32] = Default::default(); - unsafe { - ext_storage_root(result.as_mut_ptr()); - } - result -} - -/// A trie root calculated from enumerated values. -pub fn enumerated_trie_root(values: &[&[u8]]) -> [u8; 32] { - H::enumerated_trie_root(values) -} - -/// A trie root formed from the iterated items. -pub fn trie_root< - H: Hasher + ExternTrieCrypto, - I: IntoIterator, - A: AsRef<[u8]> + Ord, - B: AsRef<[u8]>, ->(_input: I) -> [u8; 32] { - unimplemented!() - // TODO Maybe implement (though probably easier/cleaner to have blake2 be the only thing - // implemneted natively and compile the trie logic as wasm). -} - -/// A trie root formed from the enumerated items. -pub fn ordered_trie_root< - H: Hasher + ExternTrieCrypto, - I: IntoIterator, - A: AsRef<[u8]> ->(_input: I) -> [u8; 32] { - unimplemented!() - // TODO Maybe implement (though probably easier/cleaner to have blake2 be the only thing - // implemneted natively and compile the trie logic as wasm). -} - -/// The current relay chain identifier. -pub fn chain_id() -> u64 { - unsafe { - ext_chain_id() - } -} - -/// Conduct a 256-bit Blake2 hash. -pub fn blake2_256(data: &[u8]) -> [u8; 32] { - let mut result: [u8; 32] = Default::default(); - unsafe { - ext_blake2_256(data.as_ptr(), data.len() as u32, result.as_mut_ptr()); - } - result -} - -/// Conduct four XX hashes to give a 256-bit result. -pub fn twox_256(data: &[u8]) -> [u8; 32] { - let mut result: [u8; 32] = Default::default(); - unsafe { - ext_twox_256(data.as_ptr(), data.len() as u32, result.as_mut_ptr()); - } - result -} - -/// Conduct two XX hashes to give a 128-bit result. -pub fn twox_128(data: &[u8]) -> [u8; 16] { - let mut result: [u8; 16] = Default::default(); - unsafe { - ext_twox_128(data.as_ptr(), data.len() as u32, result.as_mut_ptr()); - } - result -} - -/// Verify a ed25519 signature. -pub fn ed25519_verify>(sig: &[u8; 64], msg: &[u8], pubkey: P) -> bool { - unsafe { - ext_ed25519_verify(msg.as_ptr(), msg.len() as u32, sig.as_ptr(), pubkey.as_ref().as_ptr()) == 0 - } -} - -/// Trait for things which can be printed. -pub trait Printable { - fn print(self); -} - -impl<'a> Printable for &'a [u8] { - fn print(self) { - unsafe { - ext_print_hex(self.as_ptr(), self.len() as u32); - } - } -} - -impl<'a> Printable for &'a str { - fn print(self) { - unsafe { - ext_print_utf8(self.as_ptr() as *const u8, self.len() as u32); - } - } -} - -impl Printable for u64 { - fn print(self) { - unsafe { ext_print_num(self); } - } -} - -/// Print a printable value. -pub fn print(value: T) { - value.print(); -} - -#[macro_export] -macro_rules! impl_stubs { - ( $( $new_name:ident $($nodecode:ident)* => $invoke:expr ),* ) => { - $( - impl_stubs!(@METHOD $new_name $($nodecode)* => $invoke); - )* - }; - ( @METHOD $new_name:ident NO_DECODE => $invoke:expr ) => { - #[no_mangle] - pub fn $new_name(input_data: *mut u8, input_len: usize) -> u64 { - let input: &[u8] = if input_len == 0 { - &[0u8; 0] - } else { - unsafe { - $crate::slice::from_raw_parts(input_data, input_len) - } - }; - - let output: $crate::rstd::vec::Vec = $invoke(input); - let res = output.as_ptr() as u64 + ((output.len() as u64) << 32); - - // Leak the output vector to avoid it being freed. - // This is fine in a WASM context since the heap - // will be discarded after the call. - ::core::mem::forget(output); - res - } - }; - ( @METHOD $new_name:ident => $invoke:expr ) => { - #[no_mangle] - pub fn $new_name(input_data: *mut u8, input_len: usize) -> u64 { - let mut input = if input_len == 0 { - &[0u8; 0] - } else { - unsafe { - $crate::slice::from_raw_parts(input_data, input_len) - } - }; - - let input = match $crate::codec::Decode::decode(&mut input) { - Some(input) => input, - None => panic!("Bad input data provided to {}", stringify!($name)), - }; - - let output = ($invoke)(input); - let output = $crate::codec::Encode::encode(&output); - let res = output.as_ptr() as u64 + ((output.len() as u64) << 32); - - // Leak the output vector to avoid it being freed. - // This is fine in a WASM context since the heap - // will be discarded after the call. - ::core::mem::forget(output); - res - } - } -} diff --git a/substrate/runtime-sandbox/Cargo.toml b/substrate/runtime-sandbox/Cargo.toml deleted file mode 100755 index b80cb4443f8ae..0000000000000 --- a/substrate/runtime-sandbox/Cargo.toml +++ /dev/null @@ -1,31 +0,0 @@ -[package] -name = "substrate-runtime-sandbox" -version = "0.1.0" -authors = ["Parity Technologies "] -build = "build.rs" - -[build-dependencies] -rustc_version = "0.2" - -[dependencies] -wasmi = { version = "0.4", optional = true } -substrate-primitives = { path = "../primitives", default_features = false } -substrate-runtime-std = { path = "../runtime-std", default_features = false } -substrate-runtime-io = { path = "../runtime-io", default_features = false } -substrate-codec = { path = "../codec", default_features = false } - -[dev-dependencies] -wabt = "0.4" -assert_matches = "1.1" - -[features] -default = ["std"] -std = [ - "wasmi", - "substrate-primitives/std", - "substrate-runtime-std/std", - "substrate-codec/std", - "substrate-runtime-io/std", -] -nightly = [] -strict = [] diff --git a/substrate/runtime-sandbox/README.adoc b/substrate/runtime-sandbox/README.adoc deleted file mode 100644 index 61a203708c811..0000000000000 --- a/substrate/runtime-sandbox/README.adoc +++ /dev/null @@ -1,13 +0,0 @@ - -= Runtime Sandbox - -.Summary -[source, toml] ----- -include::Cargo.toml[lines=2..5] ----- - -.Description ----- -include::src/lib.rs[tag=description] ----- diff --git a/substrate/runtime-sandbox/build.rs b/substrate/runtime-sandbox/build.rs deleted file mode 100755 index 35eb154f3a69a..0000000000000 --- a/substrate/runtime-sandbox/build.rs +++ /dev/null @@ -1,14 +0,0 @@ -//! Set a nightly feature - -extern crate rustc_version; -use rustc_version::{version, version_meta, Channel}; - -fn main() { - // Assert we haven't travelled back in time - assert!(version().unwrap().major >= 1); - - // Set cfg flags depending on release channel - if let Channel::Nightly = version_meta().unwrap().channel { - println!("cargo:rustc-cfg=feature=\"nightly\""); - } -} diff --git a/substrate/runtime-sandbox/src/lib.rs b/substrate/runtime-sandbox/src/lib.rs deleted file mode 100755 index 16066af160f15..0000000000000 --- a/substrate/runtime-sandbox/src/lib.rs +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -// tag::description[] -//! This crate provides means of instantiation and execution of wasm modules. -//! -//! It works even when the user of this library is itself executes -//! inside the wasm VM. In this case same VM is used for execution -//! of both the sandbox owner and the sandboxed module, without compromising security -//! and without performance penalty of full wasm emulation inside wasm. -//! -//! This is achieved by using bindings to wasm VM which are published by the host API. -//! This API is thin and consists of only handful functions. It contains functions for instantiating -//! modules and executing them and for example doesn't contain functions for inspecting the module -//! structure. The user of this library is supposed to read wasm module by it's own means. -//! -//! When this crate is used in `std` environment all these functions are implemented by directly -//! calling wasm VM. -//! -//! Example of possible use-cases for this library are following: -//! -//! - implementing smart-contract runtimes which uses wasm for contract code -//! - executing wasm substrate runtime inside of a wasm parachain -//! - etc -// end::description[] - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(not(feature = "std"), feature(panic_handler))] -#![cfg_attr(not(feature = "std"), feature(core_intrinsics))] -#![cfg_attr(not(feature = "std"), feature(alloc))] - -extern crate substrate_codec as codec; -extern crate substrate_runtime_io as runtime_io; -#[cfg_attr(not(feature = "std"), macro_use)] -extern crate substrate_runtime_std as rstd; -extern crate substrate_primitives as primitives; - -#[cfg(test)] -extern crate wabt; - -#[cfg(test)] -#[macro_use] -extern crate assert_matches; - -use rstd::prelude::*; - -pub use primitives::sandbox::{TypedValue, ReturnValue, HostError}; - -mod imp { - #[cfg(feature = "std")] - include!("../with_std.rs"); - - #[cfg(not(feature = "std"))] - include!("../without_std.rs"); -} - -/// Error that can occur while using this crate. -#[cfg_attr(feature = "std", derive(Debug))] -pub enum Error { - /// Module is not valid, couldn't be instantiated or it's `start` function trapped - /// when executed. - Module, - - /// Access to a memory or table was made with an address or an index which is out of bounds. - /// - /// Note that if wasm module makes an out-of-bounds access then trap will occur. - OutOfBounds, - - /// Failed to invoke an exported function for some reason. - Execution, -} - -impl From for HostError { - fn from(_e: Error) -> HostError { - HostError - } -} - -/// Function pointer for specifying functions by the -/// supervisor in [`EnvironmentDefinitionBuilder`]. -/// -/// [`EnvironmentDefinitionBuilder`]: struct.EnvironmentDefinitionBuilder.html -pub type HostFuncType = fn(&mut T, &[TypedValue]) -> Result; - -/// Reference to a sandboxed linear memory, that -/// will be used by the guest module. -/// -/// The memory can't be directly accessed by supervisor, but only -/// through designated functions [`get`] and [`set`]. -/// -/// [`get`]: #method.get -/// [`set`]: #method.set -#[derive(Clone)] -pub struct Memory { - inner: imp::Memory, -} - -impl Memory { - /// Construct a new linear memory instance. - /// - /// 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). - /// - /// 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. - /// - /// Allocated memory is always zeroed. - pub fn new(initial: u32, maximum: Option) -> Result { - Ok(Memory { - inner: imp::Memory::new(initial, maximum)?, - }) - } - - /// Read a memory area at the address `ptr` with the size of the provided slice `buf`. - /// - /// Returns `Err` if the range is out-of-bounds. - pub fn get(&self, ptr: u32, buf: &mut [u8]) -> Result<(), Error> { - self.inner.get(ptr, buf) - } - - /// Write a memory area at the address `ptr` with contents of the provided slice `buf`. - /// - /// Returns `Err` if the range is out-of-bounds. - pub fn set(&self, ptr: u32, value: &[u8]) -> Result<(), Error> { - self.inner.set(ptr, value) - } -} - -/// Struct that can be used for defining an environment for a sandboxed module. -/// -/// The sandboxed module can access only the entities which were defined and passed -/// to the module at the instantiation time. -pub struct EnvironmentDefinitionBuilder { - inner: imp::EnvironmentDefinitionBuilder, -} - -impl EnvironmentDefinitionBuilder { - /// Construct a new `EnvironmentDefinitionBuilder`. - pub fn new() -> EnvironmentDefinitionBuilder { - EnvironmentDefinitionBuilder { - inner: imp::EnvironmentDefinitionBuilder::new(), - } - } - - /// Register a host function in this environment defintion. - /// - /// NOTE that there is no constraints on type of this function. An instance - /// can import function passed here with any signature it wants. It can even import - /// the same function (i.e. with same `module` and `field`) several times. It's up to - /// the user code to check or constrain the types of signatures. - pub fn add_host_func(&mut self, module: N1, field: N2, f: HostFuncType) - where - N1: Into>, - N2: Into>, - { - self.inner.add_host_func(module, field, f); - } - - /// Register a memory in this environment definition. - pub fn add_memory(&mut self, module: N1, field: N2, mem: Memory) - where - N1: Into>, - N2: Into>, - { - self.inner.add_memory(module, field, mem.inner); - } -} - -/// Sandboxed instance of a wasm module. -/// -/// This instance can be used for invoking exported functions. -pub struct Instance { - inner: imp::Instance, - -} - -impl Instance { - /// Instantiate a module with the given [`EnvironmentDefinitionBuilder`]. - /// - /// [`EnvironmentDefinitionBuilder`]: struct.EnvironmentDefinitionBuilder.html - pub fn new(code: &[u8], env_def_builder: &EnvironmentDefinitionBuilder, state: &mut T) -> Result, Error> { - Ok(Instance { - inner: imp::Instance::new(code, &env_def_builder.inner, state)?, - }) - } - - /// Invoke an exported function with the given name. - /// - /// # Errors - /// - /// Returns `Err(Error::Execution)` if: - /// - /// - An export function name isn't a proper utf8 byte sequence, - /// - This module doesn't have an exported function with the given name, - /// - If types of the arguments passed to the function doesn't match function signature - /// then trap occurs (as if the exported function was called via call_indirect), - /// - Trap occured at the execution time. - pub fn invoke( - &mut self, - name: &[u8], - args: &[TypedValue], - state: &mut T, - ) -> Result { - self.inner.invoke(name, args, state) - } -} diff --git a/substrate/runtime-sandbox/with_std.rs b/substrate/runtime-sandbox/with_std.rs deleted file mode 100755 index e4e4aa131ea34..0000000000000 --- a/substrate/runtime-sandbox/with_std.rs +++ /dev/null @@ -1,482 +0,0 @@ -// Copyright 2018 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 . - -extern crate wasmi; - -use rstd::collections::btree_map::BTreeMap; -use rstd::fmt; - -use self::wasmi::{ - Externals, FuncInstance, FuncRef, GlobalDescriptor, GlobalRef, ImportResolver, - MemoryDescriptor, MemoryInstance, MemoryRef, Module, ModuleInstance, ModuleRef, - RuntimeArgs, RuntimeValue, Signature, TableDescriptor, TableRef, Trap, TrapKind -}; -use self::wasmi::memory_units::Pages; -use super::{Error, TypedValue, ReturnValue, HostFuncType, HostError}; - -#[derive(Clone)] -pub struct Memory { - memref: MemoryRef, -} - -impl Memory { - pub fn new(initial: u32, maximum: Option) -> Result { - Ok(Memory { - memref: MemoryInstance::alloc( - Pages(initial as usize), - maximum.map(|m| Pages(m as usize)), - ).map_err(|_| Error::Module)?, - }) - } - - pub fn get(&self, ptr: u32, buf: &mut [u8]) -> Result<(), Error> { - self.memref.get_into(ptr, buf).map_err(|_| Error::OutOfBounds)?; - Ok(()) - } - - pub fn set(&self, ptr: u32, value: &[u8]) -> Result<(), Error> { - self.memref.set(ptr, value).map_err(|_| Error::OutOfBounds)?; - Ok(()) - } -} - -struct HostFuncIndex(usize); - -struct DefinedHostFunctions { - funcs: Vec>, -} - -impl Clone for DefinedHostFunctions { - fn clone(&self) -> DefinedHostFunctions { - DefinedHostFunctions { - funcs: self.funcs.clone(), - } - } -} - -impl DefinedHostFunctions { - fn new() -> DefinedHostFunctions { - DefinedHostFunctions { - funcs: Vec::new(), - } - } - - fn define(&mut self, f: HostFuncType) -> HostFuncIndex { - let idx = self.funcs.len(); - self.funcs.push(f); - HostFuncIndex(idx) - } -} - -#[derive(Debug)] -struct DummyHostError; - -impl fmt::Display for DummyHostError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "DummyHostError") - } -} - -impl self::wasmi::HostError for DummyHostError { -} - -fn from_runtime_value(v: RuntimeValue) -> TypedValue { - match v { - RuntimeValue::I32(v) => TypedValue::I32(v), - RuntimeValue::I64(v) => TypedValue::I64(v), - RuntimeValue::F32(v) => TypedValue::F32(v.to_bits() as i32), - RuntimeValue::F64(v) => TypedValue::F64(v.to_bits() as i64), - } -} - -fn to_runtime_value(v: TypedValue) -> RuntimeValue { - use self::wasmi::nan_preserving_float::{F32, F64}; - match v { - TypedValue::I32(v) => RuntimeValue::I32(v as i32), - TypedValue::I64(v) => RuntimeValue::I64(v as i64), - TypedValue::F32(v_bits) => RuntimeValue::F32(F32::from_bits(v_bits as u32)), - TypedValue::F64(v_bits) => RuntimeValue::F64(F64::from_bits(v_bits as u64)), - } -} - -struct GuestExternals<'a, T: 'a> { - state: &'a mut T, - defined_host_functions: &'a DefinedHostFunctions, -} - -impl<'a, T> Externals for GuestExternals<'a, T> { - fn invoke_index( - &mut self, - index: usize, - args: RuntimeArgs, - ) -> Result, Trap> { - let args = args.as_ref() - .iter() - .cloned() - .map(from_runtime_value) - .collect::>(); - - let result = (self.defined_host_functions.funcs[index])(self.state, &args); - match result { - Ok(value) => Ok(match value { - ReturnValue::Value(v) => Some(to_runtime_value(v)), - ReturnValue::Unit => None, - }), - Err(HostError) => Err(TrapKind::Host(Box::new(DummyHostError)).into()), - } - } -} - -enum ExternVal { - HostFunc(HostFuncIndex), - Memory(Memory), -} - -pub struct EnvironmentDefinitionBuilder { - map: BTreeMap<(Vec, Vec), ExternVal>, - defined_host_functions: DefinedHostFunctions, -} - -impl EnvironmentDefinitionBuilder { - pub fn new() -> EnvironmentDefinitionBuilder { - EnvironmentDefinitionBuilder { - map: BTreeMap::new(), - defined_host_functions: DefinedHostFunctions::new(), - } - } - - pub fn add_host_func(&mut self, module: N1, field: N2, f: HostFuncType) - where - N1: Into>, - N2: Into>, - { - let idx = self.defined_host_functions.define(f); - self.map - .insert((module.into(), field.into()), ExternVal::HostFunc(idx)); - } - - pub fn add_memory(&mut self, module: N1, field: N2, mem: Memory) - where - N1: Into>, - N2: Into>, - { - self.map - .insert((module.into(), field.into()), ExternVal::Memory(mem)); - } -} - -impl ImportResolver for EnvironmentDefinitionBuilder { - fn resolve_func( - &self, - module_name: &str, - field_name: &str, - signature: &Signature, - ) -> Result { - let key = ( - module_name.as_bytes().to_owned(), - field_name.as_bytes().to_owned(), - ); - let externval = self.map.get(&key).ok_or_else(|| { - wasmi::Error::Instantiation(format!("Export {}:{} not found", module_name, field_name)) - })?; - let host_func_idx = match *externval { - ExternVal::HostFunc(ref idx) => idx, - _ => { - return Err(wasmi::Error::Instantiation(format!( - "Export {}:{} is not a host func", - module_name, field_name - ))) - } - }; - Ok(FuncInstance::alloc_host(signature.clone(), host_func_idx.0)) - } - - fn resolve_global( - &self, - _module_name: &str, - _field_name: &str, - _global_type: &GlobalDescriptor, - ) -> Result { - Err(wasmi::Error::Instantiation(format!( - "Importing globals is not supported yet" - ))) - } - - fn resolve_memory( - &self, - module_name: &str, - field_name: &str, - _memory_type: &MemoryDescriptor, - ) -> Result { - let key = ( - module_name.as_bytes().to_owned(), - field_name.as_bytes().to_owned(), - ); - let externval = self.map.get(&key).ok_or_else(|| { - wasmi::Error::Instantiation(format!("Export {}:{} not found", module_name, field_name)) - })?; - let memory = match *externval { - ExternVal::Memory(ref m) => m, - _ => { - return Err(wasmi::Error::Instantiation(format!( - "Export {}:{} is not a memory", - module_name, field_name - ))) - } - }; - Ok(memory.memref.clone()) - } - - fn resolve_table( - &self, - _module_name: &str, - _field_name: &str, - _table_type: &TableDescriptor, - ) -> Result { - Err(wasmi::Error::Instantiation(format!( - "Importing tables is not supported yet" - ))) - } -} - -pub struct Instance { - instance: ModuleRef, - defined_host_functions: DefinedHostFunctions, - _marker: ::std::marker::PhantomData, -} - -impl Instance { - pub fn new(code: &[u8], env_def_builder: &EnvironmentDefinitionBuilder, state: &mut T) -> Result, Error> { - let module = Module::from_buffer(code).map_err(|_| Error::Module)?; - let not_started_instance = ModuleInstance::new(&module, env_def_builder) - .map_err(|_| Error::Module)?; - - - let defined_host_functions = env_def_builder.defined_host_functions.clone(); - let instance = { - let mut externals = GuestExternals { - state, - defined_host_functions: &defined_host_functions, - }; - let instance = not_started_instance.run_start(&mut externals).map_err(|_| Error::Module)?; - instance - }; - - Ok(Instance { - instance, - defined_host_functions, - _marker: ::std::marker::PhantomData::, - }) - } - - pub fn invoke( - &mut self, - name: &[u8], - args: &[TypedValue], - state: &mut T, - ) -> Result { - let args = args.iter().cloned().map(Into::into).collect::>(); - - let name = ::std::str::from_utf8(name).map_err(|_| Error::Execution)?; - let mut externals = GuestExternals { - state, - defined_host_functions: &self.defined_host_functions, - }; - let result = self.instance - .invoke_export(&name, &args, &mut externals); - - match result { - Ok(None) => Ok(ReturnValue::Unit), - Ok(Some(val)) => Ok(ReturnValue::Value(val.into())), - Err(_err) => Err(Error::Execution), - } - } -} - -#[cfg(test)] -mod tests { - use wabt; - use ::{Error, TypedValue, ReturnValue, HostError, EnvironmentDefinitionBuilder, Instance}; - - fn execute_sandboxed(code: &[u8], args: &[TypedValue]) -> Result { - struct State { - counter: u32, - } - - fn env_assert(_e: &mut State, args: &[TypedValue]) -> Result { - if args.len() != 1 { - return Err(HostError); - } - let condition = args[0].as_i32().ok_or_else(|| HostError)?; - if condition != 0 { - Ok(ReturnValue::Unit) - } else { - Err(HostError) - } - } - fn env_inc_counter(e: &mut State, args: &[TypedValue]) -> Result { - if args.len() != 1 { - return Err(HostError); - } - let inc_by = args[0].as_i32().ok_or_else(|| HostError)?; - e.counter += inc_by as u32; - Ok(ReturnValue::Value(TypedValue::I32(e.counter as i32))) - } - /// Function that takes one argument of any type and returns that value. - fn env_polymorphic_id(_e: &mut State, args: &[TypedValue]) -> Result { - if args.len() != 1 { - return Err(HostError); - } - Ok(ReturnValue::Value(args[0])) - } - - let mut state = State { counter: 0 }; - - let mut env_builder = EnvironmentDefinitionBuilder::new(); - env_builder.add_host_func("env", "assert", env_assert); - env_builder.add_host_func("env", "inc_counter", env_inc_counter); - env_builder.add_host_func("env", "polymorphic_id", env_polymorphic_id); - - let mut instance = Instance::new(code, &env_builder, &mut state)?; - let result = instance.invoke(b"call", args, &mut state); - - result.map_err(|_| HostError) - } - - #[test] - fn invoke_args() { - let code = wabt::wat2wasm(r#" - (module - (import "env" "assert" (func $assert (param i32))) - - (func (export "call") (param $x i32) (param $y i64) - ;; assert that $x = 0x12345678 - (call $assert - (i32.eq - (get_local $x) - (i32.const 0x12345678) - ) - ) - - (call $assert - (i64.eq - (get_local $y) - (i64.const 0x1234567887654321) - ) - ) - ) - ) - "#).unwrap(); - - let result = execute_sandboxed( - &code, - &[ - TypedValue::I32(0x12345678), - TypedValue::I64(0x1234567887654321), - ] - ); - assert!(result.is_ok()); - } - - #[test] - fn return_value() { - let code = wabt::wat2wasm(r#" - (module - (func (export "call") (param $x i32) (result i32) - (i32.add - (get_local $x) - (i32.const 1) - ) - ) - ) - "#).unwrap(); - - let return_val = execute_sandboxed( - &code, - &[ - TypedValue::I32(0x1336), - ] - ).unwrap(); - assert_eq!(return_val, ReturnValue::Value(TypedValue::I32(0x1337))); - } - - #[test] - fn signatures_dont_matter() { - let code = wabt::wat2wasm(r#" - (module - (import "env" "polymorphic_id" (func $id_i32 (param i32) (result i32))) - (import "env" "polymorphic_id" (func $id_i64 (param i64) (result i64))) - (import "env" "assert" (func $assert (param i32))) - - (func (export "call") - ;; assert that we can actually call the "same" function with different - ;; signatures. - (call $assert - (i32.eq - (call $id_i32 - (i32.const 0x012345678) - ) - (i32.const 0x012345678) - ) - ) - (call $assert - (i64.eq - (call $id_i64 - (i64.const 0x0123456789abcdef) - ) - (i64.const 0x0123456789abcdef) - ) - ) - ) - ) - "#).unwrap(); - - let return_val = execute_sandboxed(&code, &[]).unwrap(); - assert_eq!(return_val, ReturnValue::Unit); - } - - #[test] - fn cant_return_unmatching_type() { - fn env_returns_i32(_e: &mut (), _args: &[TypedValue]) -> Result { - Ok(ReturnValue::Value(TypedValue::I32(42))) - } - - let mut env_builder = EnvironmentDefinitionBuilder::new(); - env_builder.add_host_func("env", "returns_i32", env_returns_i32); - - let code = wabt::wat2wasm(r#" - (module - ;; It's actually returns i32, but imported as if it returned i64 - (import "env" "returns_i32" (func $returns_i32 (result i64))) - - (func (export "call") - (drop - (call $returns_i32) - ) - ) - ) - "#).unwrap(); - - // It succeeds since we are able to import functions with types we want. - let mut instance = Instance::new(&code, &env_builder, &mut ()).unwrap(); - - // But this fails since we imported a function that returns i32 as if it returned i64. - assert_matches!( - instance.invoke(b"call", &[], &mut ()), - Err(Error::Execution) - ); - } -} diff --git a/substrate/runtime-sandbox/without_std.rs b/substrate/runtime-sandbox/without_std.rs deleted file mode 100755 index f7feb466a090b..0000000000000 --- a/substrate/runtime-sandbox/without_std.rs +++ /dev/null @@ -1,303 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -use rstd::prelude::*; -use rstd::{slice, marker, mem}; -use codec::{Decode, Encode}; -use primitives::sandbox as sandbox_primitives; -use super::{Error, TypedValue, ReturnValue, HostFuncType}; - -mod ffi { - use rstd::mem; - use super::HostFuncType; - - /// Index into the default table that points to a `HostFuncType`. - pub type HostFuncIndex = usize; - - /// Coerce `HostFuncIndex` to a callable host function pointer. - /// - /// # Safety - /// - /// This function should be only called with a `HostFuncIndex` that was previously registered - /// in the environment defintion. 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 - // indeed equal. - // We can't use `static_assertions` create because it makes compiler panic, fallback to runtime assert. - // const_assert!(mem::size_of::() == mem::size_of::>(),); - assert!(mem::size_of::() == mem::size_of::>()); - mem::transmute::>(idx) - } - - extern "C" { - pub fn ext_sandbox_instantiate( - dispatch_thunk: extern "C" fn( - serialized_args_ptr: *const u8, - serialized_args_len: usize, - state: usize, - f: HostFuncIndex, - ) -> u64, - wasm_ptr: *const u8, - wasm_len: usize, - imports_ptr: *const u8, - imports_len: usize, - state: usize, - ) -> u32; - pub fn ext_sandbox_invoke( - instance_idx: u32, - export_ptr: *const u8, - export_len: usize, - args_ptr: *const u8, - args_len: usize, - return_val_ptr: *mut u8, - return_val_len: usize, - state: usize, - ) -> u32; - pub fn ext_sandbox_memory_new(initial: u32, maximum: u32) -> u32; - pub fn ext_sandbox_memory_get( - memory_idx: u32, - offset: u32, - buf_ptr: *mut u8, - buf_len: usize, - ) -> u32; - pub fn ext_sandbox_memory_set( - memory_idx: u32, - offset: u32, - val_ptr: *const u8, - val_len: usize, - ) -> u32; - pub fn ext_sandbox_memory_teardown( - memory_idx: u32, - ); - pub fn ext_sandbox_instance_teardown( - instance_idx: u32, - ); - } -} - -#[derive(Clone)] -pub struct Memory { - memory_idx: u32, -} - -impl Memory { - pub fn new(initial: u32, maximum: Option) -> Result { - let result = unsafe { - let maximum = if let Some(maximum) = maximum { - maximum - } else { - sandbox_primitives::MEM_UNLIMITED - }; - ffi::ext_sandbox_memory_new(initial, maximum) - }; - match result { - sandbox_primitives::ERR_MODULE => Err(Error::Module), - memory_idx => Ok(Memory { memory_idx }), - } - } - - pub fn get(&self, offset: u32, buf: &mut [u8]) -> Result<(), Error> { - let result = unsafe { ffi::ext_sandbox_memory_get(self.memory_idx, offset, buf.as_mut_ptr(), buf.len()) }; - match result { - sandbox_primitives::ERR_OK => Ok(()), - sandbox_primitives::ERR_OUT_OF_BOUNDS => Err(Error::OutOfBounds), - _ => unreachable!(), - } - } - - pub fn set(&self, offset: u32, val: &[u8]) -> Result<(), Error> { - let result = unsafe { ffi::ext_sandbox_memory_set(self.memory_idx, offset, val.as_ptr(), val.len()) }; - match result { - sandbox_primitives::ERR_OK => Ok(()), - sandbox_primitives::ERR_OUT_OF_BOUNDS => Err(Error::OutOfBounds), - _ => unreachable!(), - } - } -} - -impl Drop for Memory { - fn drop(&mut self) { - unsafe { - ffi::ext_sandbox_memory_teardown(self.memory_idx); - } - } -} - -pub struct EnvironmentDefinitionBuilder { - env_def: sandbox_primitives::EnvironmentDefinition, - _marker: marker::PhantomData, -} - -impl EnvironmentDefinitionBuilder { - pub fn new() -> EnvironmentDefinitionBuilder { - EnvironmentDefinitionBuilder { - env_def: sandbox_primitives::EnvironmentDefinition { - entries: Vec::new(), - }, - _marker: marker::PhantomData::, - } - } - - fn add_entry( - &mut self, - module: N1, - field: N2, - extern_entity: sandbox_primitives::ExternEntity, - ) where - N1: Into>, - N2: Into>, - { - let entry = sandbox_primitives::Entry { - module_name: module.into(), - field_name: field.into(), - entity: extern_entity, - }; - self.env_def.entries.push(entry); - } - - pub fn add_host_func(&mut self, module: N1, field: N2, f: HostFuncType) - where - N1: Into>, - N2: Into>, - { - let f = sandbox_primitives::ExternEntity::Function(f as u32); - self.add_entry(module, field, f); - } - - pub fn add_memory(&mut self, module: N1, field: N2, mem: Memory) - where - N1: Into>, - N2: Into>, - { - let mem = sandbox_primitives::ExternEntity::Memory(mem.memory_idx as u32); - self.add_entry(module, field, mem); - } -} - -pub struct Instance { - instance_idx: u32, - _marker: marker::PhantomData, -} - -/// The primary responsibility of this thunk is to deserialize arguments and -/// call the original function, specified by the index. -extern "C" fn dispatch_thunk( - serialized_args_ptr: *const u8, - serialized_args_len: usize, - state: usize, - f: ffi::HostFuncIndex, -) -> u64 { - let serialized_args = unsafe { - if serialized_args_len == 0 { - &[] - } else { - slice::from_raw_parts(serialized_args_ptr, serialized_args_len) - } - }; - let args = Vec::::decode(&mut &serialized_args[..]).expect( - "serialized args should be provided by the runtime; - correctly serialized data should be deserializable; - qed", - ); - - unsafe { - // This should be safe since `coerce_host_index_to_func` is called with an argument - // received in an `dispatch_thunk` implementation, so `f` should point - // on a valid host function. - let f = ffi::coerce_host_index_to_func(f); - - // This should be safe since mutable reference to T is passed upon the invocation. - let state = &mut *(state as *mut T); - - // Pass control flow to the designated function. - let result = f(state, &args).encode(); - - // Leak the result vector and return the pointer to return data. - let result_ptr = result.as_ptr() as u64; - let result_len = result.len() as u64; - mem::forget(result); - - (result_ptr << 32) | result_len - } -} - -impl Instance { - pub fn new(code: &[u8], env_def_builder: &EnvironmentDefinitionBuilder, state: &mut T) -> Result, Error> { - let serialized_env_def: Vec = env_def_builder.env_def.encode(); - let result = unsafe { - // It's very important to instantiate thunk with the right type. - let dispatch_thunk = dispatch_thunk::; - - ffi::ext_sandbox_instantiate( - dispatch_thunk, - code.as_ptr(), - code.len(), - serialized_env_def.as_ptr(), - serialized_env_def.len(), - state as *const T as usize, - ) - }; - let instance_idx = match result { - sandbox_primitives::ERR_MODULE => return Err(Error::Module), - instance_idx => instance_idx, - }; - Ok(Instance { - instance_idx, - _marker: marker::PhantomData::, - }) - } - - pub fn invoke( - &mut self, - name: &[u8], - args: &[TypedValue], - state: &mut T, - ) -> Result { - let serialized_args = args.to_vec().encode(); - let mut return_val = vec![0u8; sandbox_primitives::ReturnValue::ENCODED_MAX_SIZE]; - - let result = unsafe { - ffi::ext_sandbox_invoke( - self.instance_idx, - name.as_ptr(), - name.len(), - serialized_args.as_ptr(), - serialized_args.len(), - return_val.as_mut_ptr(), - return_val.len(), - state as *const T as usize, - ) - }; - match result { - sandbox_primitives::ERR_OK => { - let return_val = sandbox_primitives::ReturnValue::decode(&mut &return_val[..]) - .ok_or(Error::Execution)?; - Ok(return_val) - } - sandbox_primitives::ERR_EXECUTION => Err(Error::Execution), - _ => unreachable!(), - } - } -} - -impl Drop for Instance { - fn drop(&mut self) { - unsafe { - ffi::ext_sandbox_instance_teardown(self.instance_idx); - } - } -} diff --git a/substrate/runtime-std/Cargo.toml b/substrate/runtime-std/Cargo.toml deleted file mode 100644 index 42745a0152a10..0000000000000 --- a/substrate/runtime-std/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "substrate-runtime-std" -version = "0.1.0" -authors = ["Parity Technologies "] -build = "build.rs" - -[build-dependencies] -rustc_version = "0.2" - -[dependencies] -pwasm-alloc = { path = "../pwasm-alloc" } -pwasm-libc = { path = "../pwasm-libc" } - -[features] -default = ["std"] -std = [] -nightly = [] -strict = [] diff --git a/substrate/runtime-std/README.adoc b/substrate/runtime-std/README.adoc deleted file mode 100644 index 36ea8d99c2262..0000000000000 --- a/substrate/runtime-std/README.adoc +++ /dev/null @@ -1,14 +0,0 @@ - -= Runtime std - -.Summary -[source, toml] ----- -include::Cargo.toml[lines=2..5] ----- - -.Description ----- -include::src/lib.rs[tag=description] ----- - diff --git a/substrate/runtime-std/build.rs b/substrate/runtime-std/build.rs deleted file mode 100644 index 55688bad9cc51..0000000000000 --- a/substrate/runtime-std/build.rs +++ /dev/null @@ -1,14 +0,0 @@ -//! Set a nightly feature - -extern crate rustc_version; -use rustc_version::{version, version_meta, Channel}; - -fn main() { - // Assert we haven't travelled back in time - assert!(version().unwrap().major >= 1); - - // Set cfg flags depending on release channel - if let Channel::Nightly = version_meta().unwrap().channel { - println!("cargo:rustc-cfg=feature=\"nightly\""); - } -} diff --git a/substrate/runtime-std/src/lib.rs b/substrate/runtime-std/src/lib.rs deleted file mode 100644 index d0fddddd298af..0000000000000 --- a/substrate/runtime-std/src/lib.rs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -// tag::description[] -//! Lowest-abstraction level for the Substrate runtime: just exports useful primitives from std -//! or core/alloc to be used with any code that depends on the runtime. -// end::description[] - -#![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(not(feature = "std"), feature(panic_handler))] -#![cfg_attr(not(feature = "std"), feature(core_intrinsics))] -#![cfg_attr(not(feature = "std"), feature(alloc))] - -#![cfg_attr(feature = "std", doc = "Polkadot runtime standard library as compiled when linked with Rust's standard library.")] -#![cfg_attr(not(feature = "std"), doc = "Polkadot's runtime standard library as compiled without Rust's standard library.")] - -#[macro_export] -macro_rules! map { - ($( $name:expr => $value:expr ),*) => ( - vec![ $( ( $name, $value ) ),* ].into_iter().collect() - ) -} - -#[cfg(feature = "std")] -include!("../with_std.rs"); - -#[cfg(not(feature = "std"))] -include!("../without_std.rs"); - -/// Prelude of common useful imports. -/// -/// This should include only things which are in the normal std prelude. -pub mod prelude { - pub use ::vec::Vec; - pub use ::boxed::Box; - pub use ::cmp::{Eq, PartialEq}; - pub use ::clone::Clone; -} diff --git a/substrate/runtime-std/with_std.rs b/substrate/runtime-std/with_std.rs deleted file mode 100644 index 443fea1a61717..0000000000000 --- a/substrate/runtime-std/with_std.rs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -pub use std::borrow; -pub use std::boxed; -pub use std::cell; -pub use std::clone; -pub use std::cmp; -pub use std::fmt; -pub use std::hash; -pub use std::iter; -pub use std::marker; -pub use std::mem; -pub use std::num; -pub use std::ops; -pub use std::ptr; -pub use std::rc; -pub use std::slice; -pub use std::string; -pub use std::vec; -pub use std::result; - -pub mod collections { - pub use std::collections::btree_map; -} diff --git a/substrate/runtime-std/without_std.rs b/substrate/runtime-std/without_std.rs deleted file mode 100644 index 5219cba062703..0000000000000 --- a/substrate/runtime-std/without_std.rs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -#[cfg(feature = "nightly")] -extern crate alloc; -#[cfg(feature = "nightly")] -extern crate pwasm_libc; -#[cfg(feature = "nightly")] -extern crate pwasm_alloc; - -pub use alloc::boxed; -pub use alloc::rc; -pub use alloc::vec; -pub use alloc::string; -pub use core::borrow; -pub use core::cell; -pub use core::clone; -pub use core::cmp; -pub use core::fmt; -pub use core::hash; -pub use core::intrinsics; -pub use core::iter; -pub use core::marker; -pub use core::mem; -pub use core::num; -pub use core::ops; -pub use core::ptr; -pub use core::slice; -pub use core::result; - -pub mod collections { - pub use alloc::collections::btree_map; -} diff --git a/substrate/runtime-support/Cargo.toml b/substrate/runtime-support/Cargo.toml deleted file mode 100644 index ee8e524cf7133..0000000000000 --- a/substrate/runtime-support/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ -[package] -name = "substrate-runtime-support" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -ed25519 = { path = "../ed25519", optional = true } -hex-literal = { version = "0.1.0", optional = true } -serde = { version = "1.0", default_features = false } -serde_derive = { version = "1.0", optional = true } -substrate-runtime-std = { path = "../runtime-std", default_features = false } -substrate-runtime-io = { path = "../runtime-io", default_features = false } -substrate-primitives = { path = "../primitives", default_features = false } -substrate-codec = { path = "../codec", default_features = false } - -[dev-dependencies] -pretty_assertions = "0.5.1" -serde_json = { version = "1.0" } -substrate-codec-derive = { path = "../../substrate/codec/derive" } - -[features] -default = ["std"] -std = [ - "ed25519", - "hex-literal", - "serde/std", - "serde_derive", - "substrate-primitives/std", - "substrate-runtime-io/std", - "substrate-codec/std", - "substrate-runtime-std/std", -] -nightly = [] -strict = [] diff --git a/substrate/runtime-support/README.adoc b/substrate/runtime-support/README.adoc deleted file mode 100644 index 699235e5ae650..0000000000000 --- a/substrate/runtime-support/README.adoc +++ /dev/null @@ -1,14 +0,0 @@ - -= Runtime Support - -.Summary -[source, toml] ----- -include::Cargo.toml[lines=2..5] ----- - -.Description ----- -include::src/lib.rs[tag=description] ----- - diff --git a/substrate/runtime-support/src/dispatch.rs b/substrate/runtime-support/src/dispatch.rs deleted file mode 100644 index 90f6fd7a6d4ff..0000000000000 --- a/substrate/runtime-support/src/dispatch.rs +++ /dev/null @@ -1,584 +0,0 @@ -// Copyright 2017 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 . - -//! Dispatch system. Just dispatches calls. - -pub use rstd::prelude::{Vec, Clone, Eq, PartialEq}; -#[cfg(feature = "std")] -pub use std::fmt; -pub use rstd::result; -#[cfg(feature = "std")] -use serde; -pub use codec::{Codec, Decode, Encode, Input, Output}; - -pub type Result = result::Result<(), &'static str>; - -pub trait Dispatchable { - type Origin; - type Trait; - fn dispatch(self, origin: Self::Origin) -> Result; -} - -#[cfg(feature = "std")] -pub trait Callable { - type Call: Dispatchable + Codec + ::serde::Serialize + Clone + PartialEq + Eq; -} -#[cfg(not(feature = "std"))] -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; - -#[cfg(feature = "std")] -pub trait Parameter: Codec + serde::Serialize + Clone + Eq + fmt::Debug {} - -#[cfg(feature = "std")] -impl Parameter for T where T: Codec + serde::Serialize + Clone + Eq + fmt::Debug {} - -#[cfg(not(feature = "std"))] -pub trait Parameter: Codec + Clone + Eq {} - -#[cfg(not(feature = "std"))] -impl Parameter for T where T: Codec + Clone + Eq {} - -/// Declare a struct for this module, then implement dispatch logic to create a pairing of several -/// dispatch traits and enums. -#[macro_export] -macro_rules! decl_module { - ( - $(#[$attr:meta])* - pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident> - for enum $call_type:ident where origin: $origin_type:ty {$( - $(#[doc = $doc_attr:tt])* - fn $fn_name:ident(origin - $( - , $param_name:ident : $param:ty - )* - ) -> $result:ty; - )*} - ) => { - // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. - #[derive(Clone, Copy, PartialEq, Eq)] - #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] - // TODO: switching based on std feature is because of an issue in - // serde-derive for when we attempt to derive `Deserialize` on these types, - // in a situation where we've imported `substrate_runtime_support` as another name. - #[cfg(feature = "std")] - pub struct $mod_type<$trait_instance: $trait_name>(::std::marker::PhantomData<$trait_instance>); - - // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. - #[derive(Clone, Copy, PartialEq, Eq)] - #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] - #[cfg(not(feature = "std"))] - pub struct $mod_type<$trait_instance: $trait_name>(::core::marker::PhantomData<$trait_instance>); - - #[cfg(feature = "std")] - $(#[$attr])* - #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] - pub enum $call_type<$trait_instance: $trait_name> { - __PhantomItem(::std::marker::PhantomData<$trait_instance>), - __OtherPhantomItem(::std::marker::PhantomData<$trait_instance>), - $( - #[allow(non_camel_case_types)] - $fn_name ( $( $param ),* ), - )* - } - - #[cfg(not(feature = "std"))] - $(#[$attr])* - #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] - pub enum $call_type<$trait_instance: $trait_name> { - __PhantomItem(::core::marker::PhantomData<$trait_instance>), - __OtherPhantomItem(::core::marker::PhantomData<$trait_instance>), - $( - #[allow(non_camel_case_types)] - $fn_name ( $( $param ),* ), - )* - } - - // manual implementation of clone/eq/partialeq because using derive erroneously requires - // clone/eq/partialeq from T. - impl<$trait_instance: $trait_name> $crate::dispatch::Clone - for $call_type<$trait_instance> - { - fn clone(&self) -> Self { - match *self { - $( - $call_type::$fn_name( $( ref $param_name ),* ) => - $call_type::$fn_name( $( $param_name.clone() ),* ) - ,)* - _ => unreachable!(), - } - } - } - impl<$trait_instance: $trait_name> $crate::dispatch::PartialEq - for $call_type<$trait_instance> - { - fn eq(&self, _other: &Self) -> bool { - match *self { - $( - $call_type::$fn_name( $( ref $param_name ),* ) => { - let self_params = ( $( $param_name, )* ); - if let $call_type::$fn_name( $( ref $param_name ),* ) = *_other { - self_params == ( $( $param_name, )* ) - } else { - match *_other { - $call_type::__PhantomItem(_) => unreachable!(), - $call_type::__OtherPhantomItem(_) => unreachable!(), - _ => false, - } - } - } - )* - _ => unreachable!(), - } - } - } - impl<$trait_instance: $trait_name> $crate::dispatch::Eq - for $call_type<$trait_instance> - {} - - #[cfg(feature = "std")] - impl<$trait_instance: $trait_name> $crate::dispatch::fmt::Debug - for $call_type<$trait_instance> - { - 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 ),* ) => - write!(_f, "{}{:?}", - stringify!($fn_name), - ( $( $param_name.clone(), )* ) - ) - ,)* - _ => unreachable!(), - } - } - } - - impl<$trait_instance: $trait_name> $crate::dispatch::Decode for $call_type<$trait_instance> { - fn decode(input: &mut I) -> Option { - let _input_id = input.read_byte()?; - __impl_decode!(input; _input_id; 0; $call_type; $( fn $fn_name( $( $param_name ),* ); )*) - } - } - - impl<$trait_instance: $trait_name> $crate::dispatch::Encode for $call_type<$trait_instance> { - fn encode_to(&self, _dest: &mut W) { - __impl_encode!(_dest; *self; 0; $call_type; $( fn $fn_name( $( $param_name ),* ); )*); - if let $call_type::__PhantomItem(_) = *self { unreachable!() } - if let $call_type::__OtherPhantomItem(_) = *self { unreachable!() } - } - } - impl<$trait_instance: $trait_name> $crate::dispatch::Dispatchable - for $call_type<$trait_instance> - { - type Trait = $trait_instance; - type Origin = $origin_type; - fn dispatch(self, _origin: Self::Origin) -> $crate::dispatch::Result { - match self { - $( - $call_type::$fn_name( $( $param_name ),* ) => - <$mod_type<$trait_instance>>::$fn_name( _origin $(, $param_name )* ), - )* - _ => { panic!("__PhantomItem should never be used.") }, - } - } - } - impl<$trait_instance: $trait_name> $crate::dispatch::Callable - for $mod_type<$trait_instance> - { - type Call = $call_type<$trait_instance>; - } - - impl<$trait_instance: $trait_name> $mod_type<$trait_instance> { - pub fn dispatch>(d: D, origin: D::Origin) -> $crate::dispatch::Result { - d.dispatch(origin) - } - } - - __dispatch_impl_json_metadata! { - $mod_type $trait_instance $trait_name $call_type $origin_type - {$( $(#[doc = $doc_attr])* fn $fn_name(origin $(, $param_name : $param )*) -> $result; )*} - } - } -} - -#[macro_export] -#[doc(hidden)] -macro_rules! __impl_decode { - ( - $input:expr; - $input_id:expr; - $fn_id:expr; - $call_type:ident; - fn $fn_name:ident( - $( $param_name:ident ),* - ); - $($rest:tt)* - ) => { - { - if $input_id == ($fn_id) { - $( - let $param_name = $crate::dispatch::Decode::decode($input)?; - )* - return Some($call_type:: $fn_name( $( $param_name ),* )); - } - - __impl_decode!($input; $input_id; $fn_id + 1; $call_type; $($rest)*) - } - }; - ( - $input:expr; - $input_id:expr; - $fn_id:expr; - $call_type:ident; - ) => { - None - } -} - -#[macro_export] -#[doc(hidden)] -macro_rules! __impl_encode { - ( - $dest:expr; - $self:expr; - $fn_id:expr; - $call_type:ident; - fn $fn_name:ident( - $( $param_name:ident ),* - ); - $($rest:tt)* - ) => { - { - if let $call_type::$fn_name( - $( - ref $param_name - ),* - ) = $self { - $dest.push_byte(($fn_id) as u8); - $( - $param_name.encode_to($dest); - )* - } - - __impl_encode!($dest; $self; $fn_id + 1; $call_type; $($rest)*) - } - }; - ( - $dest:expr; - $self:expr; - $fn_id:expr; - $call_type:ident; - ) => {{}} -} - -pub trait IsSubType { - fn is_aux_sub_type(&self) -> Option<&::Call>; -} - -/// Implement a meta-dispatch module to dispatch to other dispatchers. -#[macro_export] -macro_rules! impl_outer_dispatch { - () => (); - ( - $(#[$attr:meta])* - pub enum $call_type:ident where origin: $origin:ty { - $( - $camelcase:ident, - )* - } - $( $rest:tt )* - ) => { - $(#[$attr])* - #[derive(Clone, PartialEq, Eq)] - #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] - pub enum $call_type { - $( - $camelcase ( $crate::dispatch::CallableCallFor<$camelcase> ) - ,)* - } - __impl_outer_dispatch_common! { $call_type, $($camelcase,)* } - impl $crate::dispatch::Dispatchable for $call_type { - type Origin = $origin; - type Trait = $call_type; - fn dispatch(self, origin: $origin) -> $crate::dispatch::Result { - match self { - $( - $call_type::$camelcase(call) => call.dispatch(origin), - )* - } - } - } - $( - 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_outer_dispatch!{ $($rest)* } - } -} - -/// Implement a meta-dispatch module to dispatch to other dispatchers. -#[macro_export] -#[doc(hidden)] -macro_rules! __impl_outer_dispatch_common { - ( - $call_type:ident, $( $camelcase:ident, )* - ) => { - impl $crate::dispatch::Decode for $call_type { - fn decode(input: &mut I) -> Option { - let input_id = input.read_byte()?; - __impl_decode!(input; input_id; 0; $call_type; $( fn $camelcase ( outer_dispatch_param ); )*) - } - } - - impl $crate::dispatch::Encode for $call_type { - fn encode_to(&self, dest: &mut W) { - __impl_encode!(dest; *self; 0; $call_type; $( fn $camelcase( outer_dispatch_param ); )*) - } - } - - } -} - -/// Implement the `json_metadata` function. -#[macro_export] -#[doc(hidden)] -macro_rules! __dispatch_impl_json_metadata { - ( - $mod_type:ident $trait_instance:ident $trait_name:ident - $($rest:tt)* - ) => { - impl<$trait_instance: $trait_name> $mod_type<$trait_instance> { - pub fn json_metadata() -> &'static str { - concat!(r#"{ "name": ""#, stringify!($mod_type), r#"", "call": "#, - __call_to_json!($($rest)*), " }") - } - } - } -} - -/// Convert the list of calls into their JSON representation, joined by ",". -#[macro_export] -#[doc(hidden)] -macro_rules! __call_to_json { - // WITH AUX - ( - $call_type:ident $origin_type:ty - {$( - $(#[doc = $doc_attr:tt])* - fn $fn_name:ident(origin - $( - , $param_name:ident : $param:ty - )* - ) -> $result:ty; - )*} - ) => { - concat!( - r#"{ "name": ""#, stringify!($call_type), - r#"", "functions": {"#, - __functions_to_json!(""; 0; $origin_type; $( - fn $fn_name(origin $(, $param_name: $param )* ) -> $result; - __function_doc_to_json!(""; $($doc_attr)*); - )*), " } }" - ) - }; -} - -/// Convert a list of functions into their JSON representation, joined by ",". -#[macro_export] -#[doc(hidden)] -macro_rules! __functions_to_json { - // WITHOUT AUX - ( - $prefix_str:tt; - $fn_id:expr; - fn $fn_name:ident( - $($param_name:ident : $param:ty),* - ) -> $result:ty; - $fn_doc:expr; - $($rest:tt)* - ) => { - concat!($prefix_str, " ", - __function_to_json!( - fn $fn_name( - $($param_name : $param),* - ) -> $result; - $fn_doc; - $fn_id; - ), __functions_to_json!(","; $fn_id + 1; $($rest)*) - ) - }; - // WITH AUX - ( - $prefix_str:tt; - $fn_id:expr; - $origin_type:ty; - fn $fn_name:ident(origin - $( - , $param_name:ident : $param:ty - )* - ) -> $result:ty; - $fn_doc:expr; - $($rest:tt)* - ) => { - concat!($prefix_str, " ", - __function_to_json!( - fn $fn_name( - origin: $origin_type - $(, $param_name : $param)* - ) -> $result; - $fn_doc; - $fn_id; - ), __functions_to_json!(","; $fn_id + 1; $origin_type; $($rest)*) - ) - }; - // BASE CASE - ( - $prefix_str:tt; - $fn_id:expr; - $($origin_type:ty;)* - ) => { - "" - } -} - -/// Convert a function into its JSON representation. -#[macro_export] -#[doc(hidden)] -macro_rules! __function_to_json { - ( - fn $fn_name:ident( - $first_param_name:ident : $first_param:ty $(, $param_name:ident : $param:ty)* - ) -> $result:ty; - $fn_doc:tt; - $fn_id:expr; - ) => { - concat!( - r#"""#, stringify!($fn_id), r#"""#, - r#": { "name": ""#, stringify!($fn_name), - r#"", "params": [ "#, - concat!(r#"{ "name": ""#, stringify!($first_param_name), r#"", "type": ""#, stringify!($first_param), r#"" }"# ), - $( - concat!(r#", { "name": ""#, stringify!($param_name), r#"", "type": ""#, stringify!($param), r#"" }"# ), - )* - r#" ], "description": ["#, $fn_doc, " ] }" - ) - }; -} - -/// Convert a function documentation attribute into its JSON representation. -#[macro_export] -#[doc(hidden)] -macro_rules! __function_doc_to_json { - ( - $prefix_str:tt; - $doc_attr:tt - $($rest:tt)* - ) => { - concat!( - $prefix_str, r#" ""#, - $doc_attr, - r#"""#, - __function_doc_to_json!(","; $($rest)*) - ) - }; - ( - $prefix_str:tt; - ) => { - "" - } -} - -#[cfg(test)] -// Do not complain about unused `dispatch` and `dispatch_aux`. -#[allow(dead_code)] -mod tests { - use super::*; - use serde; - use serde_json; - - pub trait Trait { - type Origin; - } - - decl_module! { - pub struct Module for enum Call where origin: T::Origin { - /// Hi, this is a comment. - fn aux_0(origin) -> Result; - fn aux_1(origin, data: i32) -> Result; - fn aux_2(origin, data: i32, data2: String) -> Result; - } - } - - const EXPECTED_METADATA: &str = concat!( - r#"{ "name": "Module", "call": "#, - r#"{ "name": "Call", "functions": { "#, - r#""0": { "name": "aux_0", "params": [ "#, - r#"{ "name": "origin", "type": "T::Origin" }"#, - r#" ], "description": [ " Hi, this is a comment." ] }, "#, - r#""0 + 1": { "name": "aux_1", "params": [ "#, - r#"{ "name": "origin", "type": "T::Origin" }, "#, - r#"{ "name": "data", "type": "i32" }"#, - r#" ], "description": [ ] }, "#, - r#""0 + 1 + 1": { "name": "aux_2", "params": [ "#, - r#"{ "name": "origin", "type": "T::Origin" }, "#, - r#"{ "name": "data", "type": "i32" }, "#, - r#"{ "name": "data2", "type": "String" }"#, - r#" ], "description": [ ] }"#, - r#" } }"#, - r#" }"#, - ); - - impl Module { - fn aux_0(_: T::Origin) -> Result { - unreachable!() - } - - fn aux_1(_: T::Origin, _: i32) -> Result { - unreachable!() - } - - fn aux_2(_: T::Origin, _: i32, _: String) -> Result { - unreachable!() - } - } - - struct TraitImpl {} - - impl Trait for TraitImpl { - type Origin = u32; - } - - #[test] - fn module_json_metadata() { - let metadata = Module::::json_metadata(); - assert_eq!(EXPECTED_METADATA, metadata); - let _: serde::de::IgnoredAny = - serde_json::from_str(metadata).expect("Is valid json syntax"); - } -} diff --git a/substrate/runtime-support/src/event.rs b/substrate/runtime-support/src/event.rs deleted file mode 100644 index a9767f51bf74e..0000000000000 --- a/substrate/runtime-support/src/event.rs +++ /dev/null @@ -1,298 +0,0 @@ -// Copyright 2018 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 . - -/// Implement an `Event`/`RawEvent` for a module. -#[macro_export] -macro_rules! decl_event { - ( - $(#[$attr:meta])* - pub enum Event<$( $evt_generic_param:ident )*> with RawEvent<$( $generic_param:ident ),*> - where $( <$generic:ident as $trait:path>::$trait_type:ident),* { - $( - $(#[doc = $doc_attr:tt])* - $event:ident( $( $param:path ),* ), - )* - } - ) => { - pub type Event<$( $evt_generic_param )*> = RawEvent<$( <$generic as $trait>::$trait_type ),*>; - // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. - #[derive(Clone, PartialEq, Eq, Encode, Decode)] - #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] - $(#[$attr])* - pub enum RawEvent<$( $generic_param ),*> { - $( - $( #[doc = $doc_attr] )* - $event($( $param ),*), - )* - } - impl<$( $generic_param ),*> From> for () { - fn from(_: RawEvent<$( $generic_param ),*>) -> () { () } - } - impl<$( $generic_param ),*> RawEvent<$( $generic_param ),*> { - #[allow(dead_code)] - pub fn event_json_metadata() -> &'static str { - concat!( - "{", - __impl_event_json_metadata!(""; - $( - $event ( $( $param ),* ); - __function_doc_to_json!(""; $($doc_attr)*); - )* - ), - " }" - ) - } - } - }; - ( - $(#[$attr:meta])* - pub enum Event { - $( - $(#[doc = $doc_attr:tt])* - $event:ident, - )* - } - ) => { - // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. - #[derive(Clone, PartialEq, Eq, Encode, Decode)] - #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] - $(#[$attr])* - pub enum Event { - $( - $( #[doc = $doc_attr] )* - $event, - )* - } - impl From for () { - fn from(_: Event) -> () { () } - } - impl Event { - #[allow(dead_code)] - pub fn event_json_metadata() -> &'static str { - concat!( - "{", - __impl_event_json_metadata!(""; - $( - $event; - __function_doc_to_json!(""; $($doc_attr)*); - )* - ), - " }" - ) - } - } - } -} - -#[macro_export] -#[doc(hidden)] -macro_rules! __impl_event_json_metadata { - ( - $prefix_str:expr; - $event:ident( $first_param:path $(, $param:path )* ); - $event_doc:expr; - $( $rest:tt )* - ) => { - concat!($prefix_str, " ", "\"", stringify!($event), r#"": { "params": [ ""#, - stringify!($first_param), "\"" - $(, concat!(", \"", stringify!($param), "\"") )*, r#" ], "description": ["#, - $event_doc, " ] }", - __impl_event_json_metadata!(","; $( $rest )*) - ) - }; - ( - $prefix_str:expr; - $event:ident; - $event_doc:expr; - $( $rest:tt )* - ) => { - concat!($prefix_str, " ", "\"", stringify!($event), - r#"": { "params": null, "description": ["#, $event_doc, " ] }", - __impl_event_json_metadata!(","; $( $rest )*) - ) - }; - ( - $prefix_str:expr; - ) => { - "" - } -} - -#[macro_export] -macro_rules! impl_outer_event { - ($(#[$attr:meta])* pub enum $name:ident for $runtime:ident { $( $module:ident ),* }) => { - // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. - #[derive(Clone, PartialEq, Eq, Encode, Decode)] - #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] - $(#[$attr])* - #[allow(non_camel_case_types)] - pub enum $name { - system(system::Event), - $( - $module($module::Event<$runtime>), - )* - } - impl From for $name { - fn from(x: system::Event) -> Self { - $name::system(x) - } - } - $( - impl From<$module::Event<$runtime>> for $name { - fn from(x: $module::Event<$runtime>) -> Self { - $name::$module(x) - } - } - )* - __impl_outer_event_json_metadata!($runtime; $name; $( $module )*); - } -} - -#[macro_export] -#[doc(hidden)] -macro_rules! __impl_outer_event_json_metadata { - ( - $runtime:ident; - $event_name:ident; - $( $module:ident )* - ) => { - impl $runtime { - #[allow(dead_code)] - pub fn outer_event_json_metadata() -> (&'static str, &'static [(&'static str, fn() -> &'static str)]) { - static METADATA: &[(&str, fn() -> &'static str)] = &[ - ("system", system::Event::event_json_metadata) - $( - , ( - stringify!($module), - $module::Event::<$runtime>::event_json_metadata - ) - )* - ]; - ( - stringify!($event_name), - METADATA - ) - } - } - } -} - -#[cfg(test)] -#[allow(dead_code)] -mod tests { - use serde; - use serde_json; - - mod system { - pub trait Trait { - type Origin; - } - - decl_module! { - pub struct Module for enum Call where origin: T::Origin {} - } - - decl_event!( - pub enum Event { - SystemEvent, - } - ); - } - - mod event_module { - pub trait Trait { - type Origin; - type Balance; - } - - decl_module! { - pub struct Module for enum Call where origin: T::Origin {} - } - - decl_event!( - pub enum Event with RawEvent - where ::Balance - { - /// Hi, I am a comment. - TestEvent(Balance), - } - ); - } - - mod event_module2 { - pub trait Trait { - type Origin; - type Balance; - } - - decl_module! { - pub struct Module for enum Call where origin: T::Origin {} - } - - decl_event!( - pub enum Event with RawEvent - where ::Balance - { - TestEvent(Balance), - } - ); - } - - #[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, Deserialize, Serialize)] - pub struct TestRuntime; - - impl_outer_event! { - pub enum TestEvent for TestRuntime { - event_module, event_module2 - } - } - - impl event_module::Trait for TestRuntime { - type Origin = u32; - type Balance = u32; - } - - impl event_module2::Trait for TestRuntime { - type Origin = u32; - type Balance = u32; - } - - impl system::Trait for TestRuntime { - type Origin = u32; - } - - const EXPECTED_METADATA: (&str, &[(&str, &str)]) = ( - "TestEvent", &[ - ("system", r#"{ "SystemEvent": { "params": null, "description": [ ] } }"#), - ("event_module", r#"{ "TestEvent": { "params": [ "Balance" ], "description": [ " Hi, I am a comment." ] } }"#), - ("event_module2", r#"{ "TestEvent": { "params": [ "Balance" ], "description": [ ] } }"#), - ] - ); - - #[test] - fn outer_event_json_metadata() { - let metadata = TestRuntime::outer_event_json_metadata(); - assert_eq!(EXPECTED_METADATA.0, metadata.0); - assert_eq!(EXPECTED_METADATA.1.len(), metadata.1.len()); - - for (expected, got) in EXPECTED_METADATA.1.iter().zip(metadata.1.iter()) { - assert_eq!(expected.0, got.0); - assert_eq!(expected.1, got.1()); - let _: serde::de::IgnoredAny = - serde_json::from_str(got.1()).expect(&format!("Is valid json syntax: {}", got.1())); - } - } -} diff --git a/substrate/runtime-support/src/hashable.rs b/substrate/runtime-support/src/hashable.rs deleted file mode 100644 index f069e3666179c..0000000000000 --- a/substrate/runtime-support/src/hashable.rs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2017 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 . - -//! Hashable trait. - -use codec::Codec; -use runtime_io::{blake2_256, twox_128, twox_256}; - -pub trait Hashable: Sized { - fn blake2_256(&self) -> [u8; 32]; - fn twox_128(&self) -> [u8; 16]; - fn twox_256(&self) -> [u8; 32]; -} - -impl Hashable for T { - fn blake2_256(&self) -> [u8; 32] { - blake2_256(&self.encode()) - } - fn twox_128(&self) -> [u8; 16] { - twox_128(&self.encode()) - } - fn twox_256(&self) -> [u8; 32] { - twox_256(&self.encode()) - } -} diff --git a/substrate/runtime-support/src/lib.rs b/substrate/runtime-support/src/lib.rs deleted file mode 100644 index 73099360dad47..0000000000000 --- a/substrate/runtime-support/src/lib.rs +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -// tag::description[] -//! Support code for the runtime. -// end::description[] - -#![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(not(feature = "std"), feature(alloc))] - -#[cfg(not(feature = "std"))] -extern crate alloc; - -#[cfg(feature = "std")] -extern crate serde; - -extern crate substrate_runtime_std as rstd; -extern crate substrate_runtime_io as runtime_io; -extern crate substrate_primitives as primitives; - -#[cfg(test)] -#[macro_use] -extern crate pretty_assertions; -#[cfg(test)] -#[macro_use] -extern crate serde_derive; -#[cfg(test)] -extern crate serde_json; -#[cfg(test)] -#[macro_use] -extern crate substrate_codec_derive; - -#[doc(hidden)] -pub extern crate substrate_codec as codec; -pub use self::storage::generator::Storage as GenericStorage; - -#[cfg(feature = "std")] -pub mod alloc { - pub use std::boxed; - pub use std::vec; -} - -#[macro_use] -pub mod dispatch; -#[macro_use] -pub mod storage; -mod hashable; -#[macro_use] -mod event; -#[macro_use] -pub mod metadata; - -pub use self::storage::{StorageVec, StorageList, StorageValue, StorageMap}; -pub use self::hashable::Hashable; -pub use self::dispatch::{Parameter, Dispatchable, Callable, IsSubType}; -pub use runtime_io::print; - - -#[macro_export] -macro_rules! fail { - ( $y:expr ) => {{ - return Err($y); - }} -} - -#[macro_export] -macro_rules! ensure { - ( $x:expr, $y:expr ) => {{ - if !$x { - fail!($y); - } - }} -} - -#[macro_export] -#[cfg(feature = "std")] -macro_rules! assert_noop { - ( $x:expr , $y:expr ) => { - let h = runtime_io::storage_root(); - assert_err!($x, $y); - assert_eq!(h, runtime_io::storage_root()); - } -} - -#[macro_export] -#[cfg(feature = "std")] -macro_rules! assert_err { - ( $x:expr , $y:expr ) => { - assert_eq!($x, Err($y)); - } -} - -#[macro_export] -#[cfg(feature = "std")] -macro_rules! assert_ok { - ( $x:expr ) => { - assert_eq!($x, Ok(())); - }; - ( $x:expr, $y:expr ) => { - assert_eq!($x, Ok($y)); - } -} - -/// The void type - it cannot exist. -// Oh rust, you crack me up... -#[derive(Clone, Eq, PartialEq)] -#[cfg_attr(feature = "std", derive(Debug))] -pub enum Void {} - -#[macro_export] -macro_rules! impl_outer_origin { - ($(#[$attr:meta])* pub enum $name:ident for $trait:ident where system = $system:ident { $( $module: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<$trait>), - $( - $module($module::Origin), - )* - #[allow(dead_code)] - Void($crate::Void) - } - #[allow(dead_code)] - impl $name { - pub const INHERENT: Self = $name::system($system::RawOrigin::Inherent); - pub const ROOT: Self = $name::system($system::RawOrigin::Root); - pub fn signed(by: <$trait as $system::Trait>::AccountId) -> Self { - $name::system($system::RawOrigin::Signed(by)) - } - } - impl From<$system::Origin<$trait>> for $name { - fn from(x: $system::Origin<$trait>) -> Self { - $name::system(x) - } - } - impl Into>> for $name { - fn into(self) -> Option<$system::Origin<$trait>> { - if let $name::system(l) = self { - Some(l) - } else { - None - } - } - } - impl From::AccountId>> for $name { - fn from(x: Option<<$trait as $system::Trait>::AccountId>) -> Self { - <$system::Origin<$trait>>::from(x).into() - } - } - $( - impl From<$module::Origin> for $name { - fn from(x: $module::Origin) -> Self { - $name::$module(x) - } - } - impl Into> for $name { - fn into(self) -> Option<$module::Origin> { - if let $name::$module(l) = self { - Some(l) - } else { - None - } - } - } - )* - }; - ($(#[$attr:meta])* pub enum $name:ident for $trait:ident { $( $module:ident ),* }) => { - impl_outer_origin! { - $(#[$attr])* - pub enum $name for $trait where system = system { - $( $module ),* - } - } - } -} diff --git a/substrate/runtime-support/src/metadata.rs b/substrate/runtime-support/src/metadata.rs deleted file mode 100644 index 12953871d324a..0000000000000 --- a/substrate/runtime-support/src/metadata.rs +++ /dev/null @@ -1,459 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -use codec::{Encode, Output}; -#[cfg(feature = "std")] -use codec::{Decode, Input}; -use alloc; - -/// Make Box available on `std` and `no_std`. -pub type Box = alloc::boxed::Box; -/// Make Vec available on `std` and `no_std`. -pub type Vec = alloc::vec::Vec; - -/// Implements the json metadata support for the given runtime and all its modules. -/// -/// Example: -/// ```compile_fail -/// impl_json_metadata!(for RUNTIME_NAME with modules MODULE0, MODULE2, MODULE3 with Storage); -/// ``` -/// -/// In this example, just `MODULE3` implements the `Storage` trait. -#[macro_export] -macro_rules! impl_json_metadata { - ( - for $runtime:ident with modules - $( $rest:tt )* - ) => { - impl $runtime { - pub fn json_metadata() -> $crate::metadata::Vec<$crate::metadata::JSONMetadata> { - let events = Self::outer_event_json_metadata(); - __impl_json_metadata!($runtime; - $crate::metadata::JSONMetadata::Events { - name: events.0, - events: events.1, - }; - $( $rest )* - ) - } - } - } -} - -/// The metadata of a runtime encoded as JSON. -#[derive(Eq)] -#[cfg_attr(feature = "std", derive(Debug))] -pub enum JSONMetadata { - Events { name: &'static str, events: &'static [(&'static str, fn() -> &'static str)] }, - Module { module: &'static str, prefix: &'static str }, - ModuleWithStorage { module: &'static str, prefix: &'static str, storage: &'static str } -} - -impl Encode for JSONMetadata { - fn encode_to(&self, dest: &mut W) { - match self { - JSONMetadata::Events { name, events } => { - 0i8.encode_to(dest); - name.encode_to(dest); - events.iter().fold(0u32, |count, _| count + 1).encode_to(dest); - events - .iter() - .map(|(module, data)| (module, data())) - .for_each(|val| val.encode_to(dest)); - }, - JSONMetadata::Module { module, prefix } => { - 1i8.encode_to(dest); - prefix.encode_to(dest); - module.encode_to(dest); - }, - JSONMetadata::ModuleWithStorage { module, prefix, storage } => { - 2i8.encode_to(dest); - prefix.encode_to(dest); - module.encode_to(dest); - storage.encode_to(dest); - } - } - } -} - -impl PartialEq for JSONMetadata { - fn eq(&self, other: &JSONMetadata) -> bool { - match (self, other) { - ( - JSONMetadata::Events { name: lname, events: left }, - JSONMetadata::Events { name: rname, events: right } - ) => { - lname == rname && left.iter().zip(right.iter()).fold(true, |res, (l, r)| { - res && l.0 == r.0 && l.1() == r.1() - }) - }, - ( - JSONMetadata::Module { prefix: lpre, module: lmod }, - JSONMetadata::Module { prefix: rpre, module: rmod } - ) => { - lpre == rpre && lmod == rmod - }, - ( - JSONMetadata::ModuleWithStorage { prefix: lpre, module: lmod, storage: lstore }, - JSONMetadata::ModuleWithStorage { prefix: rpre, module: rmod, storage: rstore } - ) => { - lpre == rpre && lmod == rmod && lstore == rstore - }, - _ => false, - } - } -} - -/// Utility struct for making `JSONMetadata` decodeable. -#[derive(Eq, PartialEq, Debug)] -#[cfg(feature = "std")] -pub enum JSONMetadataDecodable { - Events { name: String, events: Vec<(String, String)> }, - Module { module: String, prefix: String }, - ModuleWithStorage { module: String, prefix: String, storage: String } -} - -#[cfg(feature = "std")] -impl JSONMetadataDecodable { - /// Returns the instance as JSON string. - /// The first value of the tuple is the name of the metadata type and the second in the JSON string. - pub fn into_json_string(self) -> (&'static str, String) { - match self { - JSONMetadataDecodable::Events { name, events } => { - ( - "events", - format!( - r#"{{ "name": "{}", "events": {{ {} }} }}"#, name, - events.iter().enumerate() - .fold(String::from(""), |mut json, (i, (name, data))| { - if i > 0 { - json.push_str(", "); - } - json.push_str(&format!(r#""{}": {}"#, name, data)); - json - }) - ) - ) - }, - JSONMetadataDecodable::Module { prefix, module } => { - ("module", format!(r#"{{ "prefix": "{}", "module": {} }}"#, prefix, module)) - }, - JSONMetadataDecodable::ModuleWithStorage { prefix, module, storage } => { - ( - "moduleWithStorage", - format!( - r#"{{ "prefix": "{}", "module": {}, "storage": {} }}"#, - prefix, module, storage - ) - ) - } - } - } -} - -#[cfg(feature = "std")] -impl Decode for JSONMetadataDecodable { - fn decode(input: &mut I) -> Option { - i8::decode(input).and_then(|variant| { - match variant { - 0 => String::decode(input) - .and_then(|name| Vec::<(String, String)>::decode(input).map(|events| (name, events))) - .and_then(|(name, events)| Some(JSONMetadataDecodable::Events { name, events })), - 1 => String::decode(input) - .and_then(|prefix| String::decode(input).map(|v| (prefix, v))) - .and_then(|(prefix, module)| Some(JSONMetadataDecodable::Module { prefix, module })), - 2 => String::decode(input) - .and_then(|prefix| String::decode(input).map(|v| (prefix, v))) - .and_then(|(prefix, module)| String::decode(input).map(|v| (prefix, module, v))) - .and_then(|(prefix, module, storage)| Some(JSONMetadataDecodable::ModuleWithStorage { prefix, module, storage })), - _ => None, - } - }) - } -} - -#[cfg(test)] -impl PartialEq for JSONMetadataDecodable { - fn eq(&self, other: &JSONMetadata) -> bool { - match (self, other) { - ( - JSONMetadataDecodable::Events { name: lname, events: left }, - JSONMetadata::Events { name: rname, events: right } - ) => { - lname == rname && left.iter().zip(right.iter()).fold(true, |res, (l, r)| { - res && l.0 == r.0 && l.1 == r.1() - }) - }, - ( - JSONMetadataDecodable::Module { prefix: lpre, module: lmod }, - JSONMetadata::Module { prefix: rpre, module: rmod } - ) => { - lpre == rpre && lmod == rmod - }, - ( - JSONMetadataDecodable::ModuleWithStorage { prefix: lpre, module: lmod, storage: lstore }, - JSONMetadata::ModuleWithStorage { prefix: rpre, module: rmod, storage: rstore } - ) => { - lpre == rpre && lmod == rmod && lstore == rstore - }, - _ => false, - } - } -} - -#[macro_export] -#[doc(hidden)] -macro_rules! __impl_json_metadata { - ( - $runtime: ident; - $( $metadata:expr ),*; - $mod:ident::$module:ident, - $( $rest:tt )* - ) => { - __impl_json_metadata!( - $runtime; - $( $metadata, )* $crate::metadata::JSONMetadata::Module { - module: $mod::$module::<$runtime>::json_metadata(), prefix: stringify!($mod) - }; - $( $rest )* - ) - }; - ( - $runtime: ident; - $( $metadata:expr ),*; - $mod:ident::$module:ident - ) => { - __impl_json_metadata!( - $runtime; - $( $metadata, )* $crate::metadata::JSONMetadata::Module { - module: $mod::$module::<$runtime>::json_metadata(), prefix: stringify!($mod) - }; - ) - }; - ( - $runtime: ident; - $( $metadata:expr ),*; - $mod:ident::$module:ident with Storage, - $( $rest:tt )* - ) => { - __impl_json_metadata!( - $runtime; - $( $metadata, )* $crate::metadata::JSONMetadata::ModuleWithStorage { - module: $mod::$module::<$runtime>::json_metadata(), prefix: stringify!($mod), - storage: $mod::$module::<$runtime>::store_json_metadata() - }; - $( $rest )* - ) - }; - ( - $runtime: ident; - $( $metadata:expr ),*; - $mod:ident::$module:ident with Storage - ) => { - __impl_json_metadata!( - $runtime; - $( $metadata, )* $crate::metadata::JSONMetadata::ModuleWithStorage { - module: $mod::$module::<$runtime>::json_metadata(), prefix: stringify!($mod), - storage: $mod::$module::<$runtime>::store_json_metadata() - }; - ) - }; - ( - $runtime:ident; - $( $metadata:expr ),*; - ) => { - <[_]>::into_vec($crate::metadata::Box::new([ $( $metadata ),* ])) - }; -} - -#[cfg(test)] -// Do not complain about unused `dispatch` and `dispatch_aux`. -#[allow(dead_code)] -mod tests { - use super::*; - use serde; - use serde_json; - - mod system { - pub trait Trait { - type Origin; - } - - decl_module! { - pub struct Module for enum Call where origin: T::Origin {} - } - - decl_event!( - pub enum Event { - SystemEvent, - } - ); - } - - mod event_module { - use dispatch::Result; - - pub trait Trait { - type Origin; - type Balance; - } - - decl_event!( - pub enum Event with RawEvent - where ::Balance - { - /// Hi, I am a comment. - TestEvent(Balance), - } - ); - - decl_module! { - pub struct Module for enum Call where origin: T::Origin { - fn aux_0(origin) -> Result; - } - } - - impl Module { - fn aux_0(_: T::Origin) -> Result { - unreachable!() - } - } - } - - mod event_module2 { - pub trait Trait { - type Origin; - type Balance; - } - - decl_event!( - pub enum Event with RawEvent - where ::Balance - { - TestEvent(Balance), - } - ); - - decl_module! { - pub struct ModuleWithStorage for enum Call where origin: T::Origin {} - } - - decl_storage! { - trait Store for ModuleWithStorage as TestStorage { - StorageMethod : u32; - } - } - } - - #[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, Deserialize, Serialize)] - pub struct TestRuntime; - - impl_outer_event! { - pub enum TestEvent for TestRuntime { - event_module, event_module2 - } - } - - impl event_module::Trait for TestRuntime { - type Origin = u32; - type Balance = u32; - } - - impl event_module2::Trait for TestRuntime { - type Origin = u32; - type Balance = u32; - } - - impl system::Trait for TestRuntime { - type Origin = u32; - } - - impl_json_metadata!( - for TestRuntime with modules - event_module::Module, - event_module2::ModuleWithStorage with Storage - ); - - fn system_event_json() -> &'static str { - r#"{ "SystemEvent": { "params": null, "description": [ ] } }"# - } - - fn event_module_event_json() -> &'static str { - r#"{ "TestEvent": { "params": [ "Balance" ], "description": [ " Hi, I am a comment." ] } }"# - } - - fn event_module2_event_json() -> &'static str { - r#"{ "TestEvent": { "params": [ "Balance" ], "description": [ ] } }"# - } - - const EXPECTED_METADATA: &[JSONMetadata] = &[ - JSONMetadata::Events { - name: "TestEvent", - events: &[ - ("system", system_event_json), - ("event_module", event_module_event_json), - ("event_module2", event_module2_event_json), - ] - }, - JSONMetadata::Module { - module: concat!( - r#"{ "name": "Module", "call": "#, - r#"{ "name": "Call", "functions": "#, - r#"{ "0": { "name": "aux_0", "params": [ "#, - r#"{ "name": "origin", "type": "T::Origin" } ], "#, - r#""description": [ ] } } } }"# - ), - prefix: "event_module" - }, - JSONMetadata::ModuleWithStorage { - module: r#"{ "name": "ModuleWithStorage", "call": { "name": "Call", "functions": { } } }"#, - prefix: "event_module2", - storage: concat!( - r#"{ "prefix": "TestStorage", "items": { "#, - r#""StorageMethod": { "description": [ ], "modifier": null, "type": "u32" }"#, - r#" } }"# - ) - } - ]; - - #[test] - fn runtime_json_metadata() { - let metadata = TestRuntime::json_metadata(); - assert_eq!(EXPECTED_METADATA, &metadata[..]); - } - - #[test] - fn json_metadata_encode_and_decode() { - let metadata = TestRuntime::json_metadata(); - let metadata_encoded = metadata.encode(); - let metadata_decoded = Vec::::decode(&mut &metadata_encoded[..]); - - assert_eq!(&metadata_decoded.unwrap()[..], &metadata[..]); - } - - #[test] - fn into_json_string_is_valid_json() { - let metadata = TestRuntime::json_metadata(); - let metadata_encoded = metadata.encode(); - let metadata_decoded = Vec::::decode(&mut &metadata_encoded[..]); - - for mdata in metadata_decoded.unwrap().into_iter() { - let json = mdata.into_json_string(); - let _: serde::de::IgnoredAny = - serde_json::from_str(&json.1).expect(&format!("Is valid json syntax: {}", json.1)); - } - } -} diff --git a/substrate/runtime-support/src/storage/generator.rs b/substrate/runtime-support/src/storage/generator.rs deleted file mode 100644 index 7c820cc5ccecf..0000000000000 --- a/substrate/runtime-support/src/storage/generator.rs +++ /dev/null @@ -1,1659 +0,0 @@ -// Copyright 2017 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 . - -//! Strongly typed wrappers around values in storage. -//! -//! This crate exports a macro `storage_items!` and traits describing behavior of generated -//! structs. -//! -//! Three kinds of data types are currently supported: -//! - values -//! - maps -//! - lists -//! -//! # Examples: -//! -//! ```rust -//! #[macro_use] -//! extern crate substrate_runtime_support; -//! -//! type AuthorityId = [u8; 32]; -//! type Balance = u64; -//! pub type SessionKey = [u8; 32]; -//! -//! storage_items! { -//! // public value -//! pub Value: b"putd_key" => SessionKey; -//! // private map. -//! Balances: b"private_map:" => map [AuthorityId => Balance]; -//! // private list. -//! Authorities: b"auth:" => list [AuthorityId]; -//! } -//! -//!# fn main() { } -//! ``` - -use codec; -use rstd::vec::Vec; -#[doc(hidden)] -pub use rstd::borrow::Borrow; -#[doc(hidden)] -pub use rstd::marker::PhantomData; - -/// Abstraction around storage. -pub trait Storage { - /// true if the key exists in storage. - fn exists(&self, key: &[u8]) -> bool; - - /// Load the bytes of a key from storage. Can panic if the type is incorrect. - fn get(&self, key: &[u8]) -> Option; - - /// Load the bytes of a key from storage. Can panic if the type is incorrect. Will panic if - /// it's not there. - fn require(&self, key: &[u8]) -> T { self.get(key).expect("Required values must be in storage") } - - /// Load the bytes of a key from storage. Can panic if the type is incorrect. The type's - /// default is returned if it's not there. - fn get_or_default(&self, key: &[u8]) -> T { self.get(key).unwrap_or_default() } - - /// Put a value in under a key. - fn put(&self, key: &[u8], val: &T); - - /// Remove the bytes of a key from storage. - fn kill(&self, key: &[u8]); - - /// Take a value from storage, deleting it after reading. - fn take(&self, key: &[u8]) -> Option { - let value = self.get(key); - self.kill(key); - value - } - - /// Take a value from storage, deleting it after reading. - fn take_or_panic(&self, key: &[u8]) -> T { self.take(key).expect("Required values must be in storage") } - - /// Take a value from storage, deleting it after reading. - fn take_or_default(&self, key: &[u8]) -> T { self.take(key).unwrap_or_default() } -} - -/// A strongly-typed value kept in storage. -pub trait StorageValue { - /// The type that get/take returns. - type Query; - - /// Get the storage key. - fn key() -> &'static [u8]; - - /// true if the value is defined in storage. - fn exists(storage: &S) -> bool { - storage.exists(Self::key()) - } - - /// Load the value from the provided storage instance. - fn get(storage: &S) -> Self::Query; - - /// Take a value from storage, removing it afterwards. - fn take(storage: &S) -> Self::Query; - - /// Store a value under this key into the provided storage instance. - fn put(val: &T, storage: &S) { - storage.put(Self::key(), val) - } - - /// Mutate this value - fn mutate(f: F, storage: &S); - - /// Clear the storage value. - fn kill(storage: &S) { - storage.kill(Self::key()) - } -} - -/// A strongly-typed list in storage. -pub trait StorageList { - /// Get the prefix key in storage. - fn prefix() -> &'static [u8]; - - /// Get the key used to put the length field. - fn len_key() -> Vec; - - /// Get the storage key used to fetch a value at a given index. - fn key_for(index: u32) -> Vec; - - /// Read out all the items. - fn items(storage: &S) -> Vec; - - /// Set the current set of items. - fn set_items(items: &[T], storage: &S); - - /// Set the item at the given index. - fn set_item(index: u32, item: &T, storage: &S); - - /// Load the value at given index. Returns `None` if the index is out-of-bounds. - fn get(index: u32, storage: &S) -> Option; - - /// Load the length of the list - fn len(storage: &S) -> u32; - - /// Clear the list. - fn clear(storage: &S); -} - -/// A strongly-typed map in storage. -pub trait StorageMap { - /// 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(x: &K) -> Vec; - - /// true if the value is defined in storage. - fn exists(key: &K, storage: &S) -> bool { - storage.exists(&Self::key_for(key)[..]) - } - - /// Load the value associated with the given key from the map. - fn get(key: &K, storage: &S) -> Self::Query; - - /// Take the value under a key. - fn take(key: &K, storage: &S) -> Self::Query; - - /// Store a value to be associated with the given key from the map. - fn insert(key: &K, val: &V, storage: &S) { - storage.put(&Self::key_for(key)[..], val); - } - - /// Remove the value under a key. - fn remove(key: &K, storage: &S) { - storage.kill(&Self::key_for(key)[..]); - } - - /// Mutate the value under a key. - fn mutate(key: &K, f: F, storage: &S); -} - -// TODO: Remove this in favour of `decl_storage` macro. -/// Declares strongly-typed wrappers around codec-compatible types in storage. -#[macro_export] -macro_rules! storage_items { - // simple values - ($name:ident : $key:expr => $ty:ty; $($t:tt)*) => { - __storage_items_internal!(() () (OPTION_TYPE Option<$ty>) (get) (take) $name: $key => $ty); - storage_items!($($t)*); - }; - (pub $name:ident : $key:expr => $ty:ty; $($t:tt)*) => { - __storage_items_internal!((pub) () (OPTION_TYPE Option<$ty>) (get) (take) $name: $key => $ty); - storage_items!($($t)*); - }; - ($name:ident : $key:expr => default $ty:ty; $($t:tt)*) => { - __storage_items_internal!(() () (RAW_TYPE $ty) (get_or_default) (take_or_default) $name: $key => $ty); - storage_items!($($t)*); - }; - (pub $name:ident : $key:expr => default $ty:ty; $($t:tt)*) => { - __storage_items_internal!((pub) () (RAW_TYPE $ty) (get_or_default) (take_or_default) $name: $key => $ty); - storage_items!($($t)*); - }; - ($name:ident : $key:expr => required $ty:ty; $($t:tt)*) => { - __storage_items_internal!(() () (RAW_TYPE $ty) (require) (take_or_panic) $name: $key => $ty); - storage_items!($($t)*); - }; - (pub $name:ident : $key:expr => required $ty:ty; $($t:tt)*) => { - __storage_items_internal!((pub) () (RAW_TYPE $ty) (require) (take_or_panic) $name: $key => $ty); - storage_items!($($t)*); - }; - - ($name:ident get($getfn:ident) : $key:expr => $ty:ty; $($t:tt)*) => { - __storage_items_internal!(() ($getfn) (OPTION_TYPE Option<$ty>) (get) (take) $name: $key => $ty); - storage_items!($($t)*); - }; - (pub $name:ident get($getfn:ident) : $key:expr => $ty:ty; $($t:tt)*) => { - __storage_items_internal!((pub) ($getfn) (OPTION_TYPE Option<$ty>) (get) (take) $name: $key => $ty); - storage_items!($($t)*); - }; - ($name:ident get($getfn:ident) : $key:expr => default $ty:ty; $($t:tt)*) => { - __storage_items_internal!(() ($getfn) (RAW_TYPE $ty) (get_or_default) (take_or_default) $name: $key => $ty); - storage_items!($($t)*); - }; - (pub $name:ident get($getfn:ident) : $key:expr => default $ty:ty; $($t:tt)*) => { - __storage_items_internal!((pub) ($getfn) (RAW_TYPE $ty) (get_or_default) (take_or_default) $name: $key => $ty); - storage_items!($($t)*); - }; - ($name:ident get($getfn:ident) : $key:expr => required $ty:ty; $($t:tt)*) => { - __storage_items_internal!(() ($getfn) (RAW_TYPE $ty) (require) (take_or_panic) $name: $key => $ty); - storage_items!($($t)*); - }; - (pub $name:ident get($getfn:ident) : $key:expr => required $ty:ty; $($t:tt)*) => { - __storage_items_internal!((pub) ($getfn) (RAW_TYPE $ty) (require) (take_or_panic) $name: $key => $ty); - storage_items!($($t)*); - }; - - // maps - ($name:ident : $prefix:expr => map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __storage_items_internal!(() () (OPTION_TYPE Option<$ty>) (get) (take) $name: $prefix => map [$kty => $ty]); - storage_items!($($t)*); - }; - (pub $name:ident : $prefix:expr => map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __storage_items_internal!((pub) () (OPTION_TYPE Option<$ty>) (get) (take) $name: $prefix => map [$kty => $ty]); - storage_items!($($t)*); - }; - ($name:ident : $prefix:expr => default map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __storage_items_internal!(() () (RAW_TYPE $ty) (get_or_default) (take_or_default) $name: $prefix => map [$kty => $ty]); - storage_items!($($t)*); - }; - (pub $name:ident : $prefix:expr => default map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __storage_items_internal!((pub) () (RAW_TYPE $ty) (get_or_default) (take_or_default) $name: $prefix => map [$kty => $ty]); - storage_items!($($t)*); - }; - ($name:ident : $prefix:expr => required map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __storage_items_internal!(() () (RAW_TYPE $ty) (require) (take_or_panic) $name: $prefix => map [$kty => $ty]); - storage_items!($($t)*); - }; - (pub $name:ident : $prefix:expr => required map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __storage_items_internal!((pub) () (RAW_TYPE $ty) (require) (take_or_panic) $name: $prefix => map [$kty => $ty]); - storage_items!($($t)*); - }; - - ($name:ident get($getfn:ident) : $prefix:expr => map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __storage_items_internal!(() ($getfn) (OPTION_TYPE Option<$ty>) (get) (take) $name: $prefix => map [$kty => $ty]); - storage_items!($($t)*); - }; - (pub $name:ident get($getfn:ident) : $prefix:expr => map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __storage_items_internal!((pub) ($getfn) (OPTION_TYPE Option<$ty>) (get) (take) $name: $prefix => map [$kty => $ty]); - storage_items!($($t)*); - }; - ($name:ident get($getfn:ident) : $prefix:expr => default map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __storage_items_internal!(() ($getfn) (RAW_TYPE $ty) (get_or_default) (take_or_default) $name: $prefix => map [$kty => $ty]); - storage_items!($($t)*); - }; - (pub $name:ident get($getfn:ident) : $prefix:expr => default map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __storage_items_internal!((pub) ($getfn) (RAW_TYPE $ty) (get_or_default) (take_or_default) $name: $prefix => map [$kty => $ty]); - storage_items!($($t)*); - }; - ($name:ident get($getfn:ident) : $prefix:expr => required map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __storage_items_internal!(() ($getfn) (RAW_TYPE $ty) (require) (take_or_panic) $name: $prefix => map [$kty => $ty]); - storage_items!($($t)*); - }; - (pub $name:ident get($getfn:ident) : $prefix:expr => required map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __storage_items_internal!((pub) ($getfn) (RAW_TYPE $ty) (require) (take_or_panic) $name: $prefix => map [$kty => $ty]); - storage_items!($($t)*); - }; - - - // lists - ($name:ident : $prefix:expr => list [$ty:ty]; $($t:tt)*) => { - __storage_items_internal!(() $name: $prefix => list [$ty]); - storage_items!($($t)*); - }; - (pub $name:ident : $prefix:expr => list [$ty:ty]; $($t:tt)*) => { - __storage_items_internal!((pub) $name: $prefix => list [$ty]); - storage_items!($($t)*); - }; - () => () -} - -#[macro_export] -#[doc(hidden)] -macro_rules! __handle_wrap_internal { - (RAW_TYPE { $($raw:tt)* } { $($option:tt)* }) => { - $($raw)*; - }; - (OPTION_TYPE { $($raw:tt)* } { $($option:tt)* }) => { - $($option)*; - }; -} - -#[macro_export] -#[doc(hidden)] -macro_rules! __storage_items_internal { - // generator for values. - (($($vis:tt)*) ($get_fn:ident) ($wraptype:ident $gettype:ty) ($getter:ident) ($taker:ident) $name:ident : $key:expr => $ty:ty) => { - __storage_items_internal!{ ($($vis)*) () ($wraptype $gettype) ($getter) ($taker) $name : $key => $ty } - pub fn $get_fn() -> $gettype { <$name as $crate::storage::generator::StorageValue<$ty>> :: get(&$crate::storage::RuntimeStorage) } - }; - (($($vis:tt)*) () ($wraptype:ident $gettype:ty) ($getter:ident) ($taker:ident) $name:ident : $key:expr => $ty:ty) => { - $($vis)* struct $name; - - impl $crate::storage::generator::StorageValue<$ty> for $name { - type Query = $gettype; - - /// Get the storage key. - fn key() -> &'static [u8] { - $key - } - - /// Load the value from the provided storage instance. - fn get(storage: &S) -> Self::Query { - storage.$getter($key) - } - - /// Take a value from storage, removing it afterwards. - fn take(storage: &S) -> Self::Query { - storage.$taker($key) - } - - /// Mutate this value. - fn mutate(f: F, storage: &S) { - let mut val = >::get(storage); - - f(&mut val); - - __handle_wrap_internal!($wraptype { - // raw type case - >::put(&val, storage) - } { - // Option<> type case - match val { - Some(val) => >::put(&val, storage), - None => >::kill(storage), - } - }); - } - } - }; - // generator for maps. - (($($vis:tt)*) ($get_fn:ident) ($wraptype:ident $gettype:ty) ($getter:ident) ($taker:ident) $name:ident : $prefix:expr => map [$kty:ty => $ty:ty]) => { - __storage_items_internal!{ ($($vis)*) () ($wraptype $gettype) ($getter) ($taker) $name : $prefix => map [$kty => $ty] } - pub fn $get_fn>(key: K) -> $gettype { - <$name as $crate::storage::generator::StorageMap<$kty, $ty>> :: get(key.borrow(), &$crate::storage::RuntimeStorage) - } - }; - (($($vis:tt)*) () ($wraptype:ident $gettype:ty) ($getter:ident) ($taker:ident) $name:ident : $prefix:expr => map [$kty:ty => $ty:ty]) => { - $($vis)* struct $name; - - impl $crate::storage::generator::StorageMap<$kty, $ty> for $name { - type Query = $gettype; - - /// Get the prefix key in storage. - fn prefix() -> &'static [u8] { - $prefix - } - - /// Get the storage key used to fetch a value corresponding to a specific key. - fn key_for(x: &$kty) -> Vec { - let mut key = $prefix.to_vec(); - $crate::codec::Encode::encode_to(x, &mut key); - key - } - - /// Load the value associated with the given key from the map. - fn get(key: &$kty, storage: &S) -> Self::Query { - let key = <$name as $crate::storage::generator::StorageMap<$kty, $ty>>::key_for(key); - storage.$getter(&key[..]) - } - - /// Take the value, reading and removing it. - fn take(key: &$kty, storage: &S) -> Self::Query { - let key = <$name as $crate::storage::generator::StorageMap<$kty, $ty>>::key_for(key); - storage.$taker(&key[..]) - } - - /// Mutate the value under a key. - fn mutate(key: &$kty, f: F, storage: &S) { - let mut val = >::take(key, storage); - - f(&mut val); - - __handle_wrap_internal!($wraptype { - // raw type case - >::insert(key, &val, storage) - } { - // Option<> type case - match val { - Some(val) => >::insert(key, &val, storage), - None => >::remove(key, storage), - } - }); - } - } - }; - // generator for lists. - (($($vis:tt)*) $name:ident : $prefix:expr => list [$ty:ty]) => { - $($vis)* struct $name; - - impl $name { - fn clear_item(index: u32, storage: &S) { - if index < <$name as $crate::storage::generator::StorageList<$ty>>::len(storage) { - storage.kill(&<$name as $crate::storage::generator::StorageList<$ty>>::key_for(index)); - } - } - - fn set_len(count: u32, storage: &S) { - (count..<$name as $crate::storage::generator::StorageList<$ty>>::len(storage)).for_each(|i| $name::clear_item(i, storage)); - storage.put(&<$name as $crate::storage::generator::StorageList<$ty>>::len_key(), &count); - } - } - - impl $crate::storage::generator::StorageList<$ty> for $name { - /// Get the prefix key in storage. - fn prefix() -> &'static [u8] { - $prefix - } - - /// Get the key used to put the length field. - // TODO: concat macro should accept byte literals. - fn len_key() -> Vec { - let mut key = $prefix.to_vec(); - key.extend(b"len"); - key - } - - /// Get the storage key used to fetch a value at a given index. - fn key_for(index: u32) -> Vec { - let mut key = $prefix.to_vec(); - $crate::codec::Encode::encode_to(&index, &mut key); - key - } - - /// Read out all the items. - fn items(storage: &S) -> Vec<$ty> { - (0..<$name as $crate::storage::generator::StorageList<$ty>>::len(storage)) - .map(|i| <$name as $crate::storage::generator::StorageList<$ty>>::get(i, storage).expect("all items within length are set; qed")) - .collect() - } - - /// Set the current set of items. - fn set_items(items: &[$ty], storage: &S) { - $name::set_len(items.len() as u32, storage); - items.iter() - .enumerate() - .for_each(|(i, item)| <$name as $crate::storage::generator::StorageList<$ty>>::set_item(i as u32, item, storage)); - } - - fn set_item(index: u32, item: &$ty, storage: &S) { - if index < <$name as $crate::storage::generator::StorageList<$ty>>::len(storage) { - storage.put(&<$name as $crate::storage::generator::StorageList<$ty>>::key_for(index)[..], item); - } - } - - /// Load the value at given index. Returns `None` if the index is out-of-bounds. - fn get(index: u32, storage: &S) -> Option<$ty> { - storage.get(&<$name as $crate::storage::generator::StorageList<$ty>>::key_for(index)[..]) - } - - /// Load the length of the list. - fn len(storage: &S) -> u32 { - storage.get(&<$name as $crate::storage::generator::StorageList<$ty>>::len_key()).unwrap_or_default() - } - - /// Clear the list. - fn clear(storage: &S) { - for i in 0..<$name as $crate::storage::generator::StorageList<$ty>>::len(storage) { - $name::clear_item(i, storage); - } - - storage.kill(&<$name as $crate::storage::generator::StorageList<$ty>>::len_key()[..]) - } - } - }; -} - -// TODO: revisit this idiom once we get `type`s in `impl`s. -/*impl Module { - type Now = super::Now; -}*/ - -/// Declares strongly-typed wrappers around codec-compatible types in storage. -/// -/// For now we implement a convenience trait with pre-specialised associated types, one for each -/// storage item. This allows you to gain access to publicly visisible storage items from a -/// module type. Currently you must disambiguate by using `::Item` rather than -/// the simpler `Module::Item`. Hopefully the rust guys with fix this soon. -#[macro_export] -macro_rules! decl_storage { - ( - trait $storetype:ident for $modulename:ident<$traitinstance:ident: $traittype:ident> as $cratename:ident { - $($t:tt)* - } - ) => { - __decl_storage_items!($cratename $traittype $traitinstance $($t)*); - trait $storetype { - __decl_store_items!($($t)*); - } - impl<$traitinstance: $traittype> $storetype for $modulename<$traitinstance> { - __impl_store_items!($traitinstance $($t)*); - } - impl<$traitinstance: $traittype> $modulename<$traitinstance> { - __impl_store_fns!($traitinstance $($t)*); - __impl_store_json_metadata!($cratename; $($t)*); - } - }; - ( - pub trait $storetype:ident for $modulename:ident<$traitinstance:ident: $traittype:ident> as $cratename:ident { - $($t:tt)* - } - ) => { - __decl_storage_items!($cratename $traittype $traitinstance $($t)*); - pub trait $storetype { - __decl_store_items!($($t)*); - } - impl<$traitinstance: $traittype> $storetype for $modulename<$traitinstance> { - __impl_store_items!($traitinstance $($t)*); - } - impl<$traitinstance: $traittype> $modulename<$traitinstance> { - __impl_store_fns!($traitinstance $($t)*); - } - } -} - -#[macro_export] -#[doc(hidden)] -macro_rules! __decl_storage_items { - // simple values - ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* $name:ident : $ty:ty; $($t:tt)*) => { - __decl_storage_item!(() ($traittype as $traitinstance) () (OPTION_TYPE Option<$ty>) (get) (take) $cratename $name: $ty); - __decl_storage_items!($cratename $traittype $traitinstance $($t)*); - }; - ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* pub $name:ident : $ty:ty; $($t:tt)*) => { - __decl_storage_item!((pub) ($traittype as $traitinstance) () (OPTION_TYPE Option<$ty>) (get) (take) $cratename $name: $ty); - __decl_storage_items!($cratename $traittype $traitinstance $($t)*); - }; - ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* $name:ident : default $ty:ty; $($t:tt)*) => { - __decl_storage_item!(() ($traittype as $traitinstance) () (RAW_TYPE $ty) (get_or_default) (take_or_default) $cratename $name: $ty); - __decl_storage_items!($cratename $traittype $traitinstance $($t)*); - }; - ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* pub $name:ident : default $ty:ty; $($t:tt)*) => { - __decl_storage_item!((pub) ($traittype as $traitinstance) () (RAW_TYPE $ty) (get_or_default) (take_or_default) $cratename $name: $ty); - __decl_storage_items!($cratename $traittype $traitinstance $($t)*); - }; - ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* $name:ident : required $ty:ty; $($t:tt)*) => { - __decl_storage_item!(() ($traittype as $traitinstance) () (RAW_TYPE $ty) (require) (take_or_panic) $cratename $name: $ty); - __decl_storage_items!($cratename $traittype $traitinstance $($t)*); - }; - ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* pub $name:ident : required $ty:ty; $($t:tt)*) => { - __decl_storage_item!((pub) ($traittype as $traitinstance) () (RAW_TYPE $ty) (require) (take_or_panic) $cratename $name: $ty); - __decl_storage_items!($cratename $traittype $traitinstance $($t)*); - }; - - ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : $ty:ty; $($t:tt)*) => { - __decl_storage_item!(() ($traittype as $traitinstance) ($getfn) (OPTION_TYPE Option<$ty>) (get) (take) $cratename $name: $ty); - __decl_storage_items!($cratename $traittype $traitinstance $($t)*); - }; - ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : $ty:ty; $($t:tt)*) => { - __decl_storage_item!((pub) ($traittype as $traitinstance) ($getfn) (OPTION_TYPE Option<$ty>) (get) (take) $cratename $name: $ty); - __decl_storage_items!($cratename $traittype $traitinstance $($t)*); - }; - ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : default $ty:ty; $($t:tt)*) => { - __decl_storage_item!(() ($traittype as $traitinstance) ($getfn) (RAW_TYPE $ty) (get_or_default) (take_or_default) $cratename $name: $ty); - __decl_storage_items!($cratename $traittype $traitinstance $($t)*); - }; - ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : default $ty:ty; $($t:tt)*) => { - __decl_storage_item!((pub) ($traittype as $traitinstance) ($getfn) (RAW_TYPE $ty) (get_or_default) (take_or_default) $cratename $name: $ty); - __decl_storage_items!($cratename $traittype $traitinstance $($t)*); - }; - ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : required $ty:ty; $($t:tt)*) => { - __decl_storage_item!(() ($traittype as $traitinstance) ($getfn) (RAW_TYPE $ty) (require) (take_or_panic) $cratename $name: $ty); - __decl_storage_items!($cratename $traittype $traitinstance $($t)*); - }; - ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : required $ty:ty; $($t:tt)*) => { - __decl_storage_item!((pub) ($traittype as $traitinstance) ($getfn) (RAW_TYPE $ty) (require) (take_or_panic) $cratename $name: $ty); - __decl_storage_items!($cratename $traittype $traitinstance $($t)*); - }; - - // maps - ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* $name:ident : map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __decl_storage_item!(() ($traittype as $traitinstance) () (OPTION_TYPE Option<$ty>) (get) (take) $cratename $name: map [$kty => $ty]); - __decl_storage_items!($cratename $traittype $traitinstance $($t)*); - }; - ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* pub $name:ident : map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __decl_storage_item!((pub) ($traittype as $traitinstance) () (OPTION_TYPE Option<$ty>) (get) (take) $cratename $name: map [$kty => $ty]); - __decl_storage_items!($cratename $traittype $traitinstance $($t)*); - }; - ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* $name:ident : default map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __decl_storage_item!(() ($traittype as $traitinstance) () (RAW_TYPE $ty) (get_or_default) (take_or_default) $cratename $name: map [$kty => $ty]); - __decl_storage_items!($cratename $traittype $traitinstance $($t)*); - }; - ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* pub $name:ident : default map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __decl_storage_item!((pub) ($traittype as $traitinstance) () (RAW_TYPE $ty) (get_or_default) (take_or_default) $cratename $name: map [$kty => $ty]); - __decl_storage_items!($cratename $traittype $traitinstance $($t)*); - }; - ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* $name:ident : required map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __decl_storage_item!(() ($traittype as $traitinstance) () (RAW_TYPE $ty) (require) (take_or_panic) $cratename $name: map [$kty => $ty]); - __decl_storage_items!($cratename $traittype $traitinstance $($t)*); - }; - ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* pub $name:ident : required map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __decl_storage_item!((pub) ($traittype as $traitinstance) () (RAW_TYPE $ty) (require) (take_or_panic) $cratename $name: map [$kty => $ty]); - __decl_storage_items!($cratename $traittype $traitinstance $($t)*); - }; - - ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __decl_storage_item!(() ($traittype as $traitinstance) ($getfn) (OPTION_TYPE Option<$ty>) (get) (take) $cratename $name: map [$kty => $ty]); - __decl_storage_items!($cratename $traittype $traitinstance $($t)*); - }; - ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __decl_storage_item!((pub) ($traittype as $traitinstance) ($getfn) (OPTION_TYPE Option<$ty>) (get) (take) $cratename $name: map [$kty => $ty]); - __decl_storage_items!($cratename $traittype $traitinstance $($t)*); - }; - ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : default map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __decl_storage_item!(() ($traittype as $traitinstance) ($getfn) (RAW_TYPE $ty) (get_or_default) (take_or_default) $cratename $name: map [$kty => $ty]); - __decl_storage_items!($cratename $traittype $traitinstance $($t)*); - }; - ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : default map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __decl_storage_item!((pub) ($traittype as $traitinstance) ($getfn) (RAW_TYPE $ty) (get_or_default) (take_or_default) $cratename $name: map [$kty => $ty]); - __decl_storage_items!($cratename $traittype $traitinstance $($t)*); - }; - ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : required map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __decl_storage_item!(() ($traittype as $traitinstance) ($getfn) (RAW_TYPE $ty) (require) (take_or_panic) $cratename $name: map [$kty => $ty]); - __decl_storage_items!($cratename $traittype $traitinstance $($t)*); - }; - ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : required map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __decl_storage_item!((pub) ($traittype as $traitinstance) ($getfn) (RAW_TYPE $ty) (require) (take_or_panic) $cratename $name: map [$kty => $ty]); - __decl_storage_items!($cratename $traittype $traitinstance $($t)*); - }; - - // exit - ($cratename:ident $traittype:ident $traitinstance:ident) => () -} - -#[macro_export] -#[doc(hidden)] -macro_rules! __decl_storage_item { - // generator for values. - (($($vis:tt)*) ($traittype:ident as $traitinstance:ident) ($get_fn:ident) ($wraptype:ident $gettype:ty) ($getter:ident) ($taker:ident) $cratename:ident $name:ident : $ty:ty) => { - __decl_storage_item!{ ($($vis)*) ($traittype as $traitinstance) () ($wraptype $gettype) ($getter) ($taker) $cratename $name : $ty } - }; - (($($vis:tt)*) ($traittype:ident as $traitinstance:ident) () ($wraptype:ident $gettype:ty) ($getter:ident) ($taker:ident) $cratename:ident $name:ident : $ty:ty) => { - $($vis)* struct $name<$traitinstance: $traittype>($crate::storage::generator::PhantomData<$traitinstance>); - - impl<$traitinstance: $traittype> $crate::storage::generator::StorageValue<$ty> for $name<$traitinstance> { - type Query = $gettype; - - /// Get the storage key. - fn key() -> &'static [u8] { - stringify!($cratename $name).as_bytes() - } - - /// Load the value from the provided storage instance. - fn get(storage: &S) -> Self::Query { - storage.$getter(<$name<$traitinstance> as $crate::storage::generator::StorageValue<$ty>>::key()) - } - - /// Take a value from storage, removing it afterwards. - fn take(storage: &S) -> Self::Query { - storage.$taker(<$name<$traitinstance> as $crate::storage::generator::StorageValue<$ty>>::key()) - } - - /// Mutate the value under a key. - fn mutate(f: F, storage: &S) { - let mut val = >::get(storage); - - f(&mut val); - - __handle_wrap_internal!($wraptype { - // raw type case - >::put(&val, storage) - } { - // Option<> type case - match val { - Some(val) => >::put(&val, storage), - None => >::kill(storage), - } - }) - } - } - }; - // generator for maps. - (($($vis:tt)*) ($traittype:ident as $traitinstance:ident) ($get_fn:ident) ($wraptype:ident $gettype:ty) ($getter:ident) ($taker:ident) $cratename:ident $name:ident : map [$kty:ty => $ty:ty]) => { - __decl_storage_item!{ ($($vis)*) ($traittype as $traitinstance) () ($wraptype $gettype) ($getter) ($taker) $cratename $name : map [$kty => $ty] } - }; - (($($vis:tt)*) ($traittype:ident as $traitinstance:ident) () ($wraptype:ident $gettype:ty) ($getter:ident) ($taker:ident) $cratename:ident $name:ident : map [$kty:ty => $ty:ty]) => { - $($vis)* struct $name<$traitinstance: $traittype>($crate::storage::generator::PhantomData<$traitinstance>); - - impl<$traitinstance: $traittype> $crate::storage::generator::StorageMap<$kty, $ty> for $name<$traitinstance> { - type Query = $gettype; - - /// Get the prefix key in storage. - fn prefix() -> &'static [u8] { - stringify!($cratename $name).as_bytes() - } - - /// Get the storage key used to fetch a value corresponding to a specific key. - fn key_for(x: &$kty) -> Vec { - let mut key = <$name<$traitinstance> as $crate::storage::generator::StorageMap<$kty, $ty>>::prefix().to_vec(); - $crate::codec::Encode::encode_to(x, &mut key); - key - } - - /// Load the value associated with the given key from the map. - fn get(key: &$kty, storage: &S) -> Self::Query { - let key = <$name<$traitinstance> as $crate::storage::generator::StorageMap<$kty, $ty>>::key_for(key); - storage.$getter(&key[..]) - } - - /// Take the value, reading and removing it. - fn take(key: &$kty, storage: &S) -> Self::Query { - let key = <$name<$traitinstance> as $crate::storage::generator::StorageMap<$kty, $ty>>::key_for(key); - storage.$taker(&key[..]) - } - - /// Mutate the value under a key - fn mutate(key: &$kty, f: F, storage: &S) { - let mut val = >::take(key, storage); - - f(&mut val); - - __handle_wrap_internal!($wraptype { - >::insert(key, &val, storage); - } { - match val { - Some(val) => >::insert(key, &val, storage), - None => >::remove(key, storage), - } - }); - } - } - }; -} - -#[macro_export] -#[doc(hidden)] -macro_rules! __decl_store_items { - // simple values - ($(#[$doc:meta])* $name:ident : default $ty:ty; $($t:tt)*) => { - __decl_store_item!($name); __decl_store_items!($($t)*); - }; - ($(#[$doc:meta])* pub $name:ident : default $ty:ty; $($t:tt)*) => { - __decl_store_item!($name); __decl_store_items!($($t)*); - }; - ($(#[$doc:meta])* $name:ident : required $ty:ty; $($t:tt)*) => { - __decl_store_item!($name); __decl_store_items!($($t)*); - }; - ($(#[$doc:meta])* pub $name:ident : required $ty:ty; $($t:tt)*) => { - __decl_store_item!($name); __decl_store_items!($($t)*); - }; - ($(#[$doc:meta])* $name:ident : $ty:ty; $($t:tt)*) => { - __decl_store_item!($name); __decl_store_items!($($t)*); - }; - ($(#[$doc:meta])* pub $name:ident : $ty:ty; $($t:tt)*) => { - __decl_store_item!($name); __decl_store_items!($($t)*); - }; - - ($(#[$doc:meta])* $name:ident get($getfn:ident) : default $ty:ty; $($t:tt)*) => { - __decl_store_item!($name); __decl_store_items!($($t)*); - }; - ($(#[$doc:meta])* pub $name:ident get($getfn:ident) : default $ty:ty; $($t:tt)*) => { - __decl_store_item!($name); __decl_store_items!($($t)*); - }; - ($(#[$doc:meta])* $name:ident get($getfn:ident) : required $ty:ty; $($t:tt)*) => { - __decl_store_item!($name); __decl_store_items!($($t)*); - }; - ($(#[$doc:meta])* pub $name:ident get($getfn:ident) : required $ty:ty; $($t:tt)*) => { - __decl_store_item!($name); __decl_store_items!($($t)*); - }; - ($(#[$doc:meta])* $name:ident get($getfn:ident) : $ty:ty; $($t:tt)*) => { - __decl_store_item!($name); __decl_store_items!($($t)*); - }; - ($(#[$doc:meta])* pub $name:ident get($getfn:ident) : $ty:ty; $($t:tt)*) => { - __decl_store_item!($name); __decl_store_items!($($t)*); - }; - - // maps - ($(#[$doc:meta])* $name:ident : default map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __decl_store_item!($name); __decl_store_items!($($t)*); - }; - ($(#[$doc:meta])* pub $name:ident : default map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __decl_store_item!($name); __decl_store_items!($($t)*); - }; - ($(#[$doc:meta])* $name:ident : required map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __decl_store_item!($name); __decl_store_items!($($t)*); - }; - ($(#[$doc:meta])* pub $name:ident : required map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __decl_store_item!($name); __decl_store_items!($($t)*); - }; - ($(#[$doc:meta])* $name:ident : map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __decl_store_item!($name); __decl_store_items!($($t)*); - }; - ($(#[$doc:meta])* pub $name:ident : map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __decl_store_item!($name); __decl_store_items!($($t)*); - }; - - ($(#[$doc:meta])* $name:ident get($getfn:ident) : default map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __decl_store_item!($name); __decl_store_items!($($t)*); - }; - ($(#[$doc:meta])* pub $name:ident get($getfn:ident) : default map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __decl_store_item!($name); __decl_store_items!($($t)*); - }; - ($(#[$doc:meta])* $name:ident get($getfn:ident) : required map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __decl_store_item!($name); __decl_store_items!($($t)*); - }; - ($(#[$doc:meta])* pub $name:ident get($getfn:ident) : required map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __decl_store_item!($name); __decl_store_items!($($t)*); - }; - ($(#[$doc:meta])* $name:ident get($getfn:ident) : map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __decl_store_item!($name); __decl_store_items!($($t)*); - }; - ($(#[$doc:meta])* pub $name:ident get($getfn:ident) : map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __decl_store_item!($name); __decl_store_items!($($t)*); - }; - - // exit - () => () -} - -#[macro_export] -#[doc(hidden)] -macro_rules! __decl_store_item { - ($name:ident) => { type $name; } -} - -#[macro_export] -#[doc(hidden)] -macro_rules! __impl_store_fns { - // simple values - ($traitinstance:ident $(#[$doc:meta])* $name:ident : default $ty:ty; $($t:tt)*) => { - __impl_store_fns!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* pub $name:ident : default $ty:ty; $($t:tt)*) => { - __impl_store_fns!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* $name:ident : required $ty:ty; $($t:tt)*) => { - __impl_store_fns!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* pub $name:ident : required $ty:ty; $($t:tt)*) => { - __impl_store_fns!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* $name:ident : $ty:ty; $($t:tt)*) => { - __impl_store_fns!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* pub $name:ident : $ty:ty; $($t:tt)*) => { - __impl_store_fns!($traitinstance $($t)*); - }; - - ($traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : default $ty:ty; $($t:tt)*) => { - __impl_store_fn!($traitinstance $name $getfn ($ty) $ty); - __impl_store_fns!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : default $ty:ty; $($t:tt)*) => { - __impl_store_fn!($traitinstance $name $getfn ($ty) $ty); - __impl_store_fns!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : required $ty:ty; $($t:tt)*) => { - __impl_store_fn!($traitinstance $name $getfn ($ty) $ty); - __impl_store_fns!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : required $ty:ty; $($t:tt)*) => { - __impl_store_fn!($traitinstance $name $getfn ($ty) $ty); - __impl_store_fns!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : $ty:ty; $($t:tt)*) => { - __impl_store_fn!($traitinstance $name $getfn (Option<$ty>) $ty); - __impl_store_fns!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : $ty:ty; $($t:tt)*) => { - __impl_store_fn!($traitinstance $name $getfn (Option<$ty>) $ty); - __impl_store_fns!($traitinstance $($t)*); - }; - - // maps - ($traitinstance:ident $(#[$doc:meta])* $name:ident : default map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __impl_store_fns!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* pub $name:ident : default map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __impl_store_fns!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* $name:ident : required map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __impl_store_fns!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* pub $name:ident : required map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __impl_store_fns!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* $name:ident : map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __impl_store_fns!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* pub $name:ident : map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __impl_store_fns!($traitinstance $($t)*); - }; - - ($traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : default map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __impl_store_fn!($traitinstance $name $getfn ($ty) map [$kty => $ty]); - __impl_store_fns!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : default map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __impl_store_fn!($traitinstance $name $getfn ($ty) map [$kty => $ty]); - __impl_store_fns!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : required map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __impl_store_fn!($traitinstance $name $getfn ($ty) map [$kty => $ty]); - __impl_store_fns!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : required map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __impl_store_fn!($traitinstance $name $getfn ($ty) map [$kty => $ty]); - __impl_store_fns!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __impl_store_fn!($traitinstance $name $getfn (Option<$ty>) map [$kty => $ty]); - __impl_store_fns!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __impl_store_fn!($traitinstance $name $getfn (Option<$ty>) map [$kty => $ty]); - __impl_store_fns!($traitinstance $($t)*); - }; - - // exit - ($traitinstance:ident) => () -} - -#[macro_export] -#[doc(hidden)] -macro_rules! __impl_store_fn { - ($traitinstance:ident $name:ident $get_fn:ident ($gettype:ty) $ty:ty) => { - pub fn $get_fn() -> $gettype { - <$name<$traitinstance> as $crate::storage::generator::StorageValue<$ty>> :: get(&$crate::storage::RuntimeStorage) - } - }; - ($traitinstance:ident $name:ident $get_fn:ident ($gettype:ty) map [$kty:ty => $ty:ty]) => { - pub fn $get_fn>(key: K) -> $gettype { - <$name<$traitinstance> as $crate::storage::generator::StorageMap<$kty, $ty>> :: get(key.borrow(), &$crate::storage::RuntimeStorage) - } - } -} - -#[macro_export] -#[doc(hidden)] -macro_rules! __impl_store_items { - // simple values - ($traitinstance:ident $(#[$doc:meta])* $name:ident : default $ty:ty; $($t:tt)*) => { - __impl_store_item!($name $traitinstance); - __impl_store_items!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* pub $name:ident : default $ty:ty; $($t:tt)*) => { - __impl_store_item!($name $traitinstance); - __impl_store_items!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* $name:ident : required $ty:ty; $($t:tt)*) => { - __impl_store_item!($name $traitinstance); - __impl_store_items!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* pub $name:ident : required $ty:ty; $($t:tt)*) => { - __impl_store_item!($name $traitinstance); - __impl_store_items!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* $name:ident : $ty:ty; $($t:tt)*) => { - __impl_store_item!($name $traitinstance); - __impl_store_items!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* pub $name:ident : $ty:ty; $($t:tt)*) => { - __impl_store_item!($name $traitinstance); - __impl_store_items!($traitinstance $($t)*); - }; - - ($traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : default $ty:ty; $($t:tt)*) => { - __impl_store_item!($name $traitinstance); - __impl_store_items!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : default $ty:ty; $($t:tt)*) => { - __impl_store_item!($name $traitinstance); - __impl_store_items!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : required $ty:ty; $($t:tt)*) => { - __impl_store_item!($name $traitinstance); - __impl_store_items!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : required $ty:ty; $($t:tt)*) => { - __impl_store_item!($name $traitinstance); - __impl_store_items!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : $ty:ty; $($t:tt)*) => { - __impl_store_item!($name $traitinstance); - __impl_store_items!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : $ty:ty; $($t:tt)*) => { - __impl_store_item!($name $traitinstance); - __impl_store_items!($traitinstance $($t)*); - }; - - // maps - ($traitinstance:ident $(#[$doc:meta])* $name:ident : default map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __impl_store_item!($name $traitinstance); - __impl_store_items!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* pub $name:ident : default map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __impl_store_item!($name $traitinstance); - __impl_store_items!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* $name:ident : required map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __impl_store_item!($name $traitinstance); - __impl_store_items!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* pub $name:ident : required map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __impl_store_item!($name $traitinstance); - __impl_store_items!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* $name:ident : map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __impl_store_item!($name $traitinstance); - __impl_store_items!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* pub $name:ident : map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __impl_store_item!($name $traitinstance); - __impl_store_items!($traitinstance $($t)*); - }; - - ($traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : default map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __impl_store_item!($name $traitinstance); - __impl_store_items!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : default map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __impl_store_item!($name $traitinstance); - __impl_store_items!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : required map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __impl_store_item!($name $traitinstance); - __impl_store_items!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : required map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __impl_store_item!($name $traitinstance); - __impl_store_items!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __impl_store_item!($name $traitinstance); - __impl_store_items!($traitinstance $($t)*); - }; - ($traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : map [$kty:ty => $ty:ty]; $($t:tt)*) => { - __impl_store_item!($name $traitinstance); - __impl_store_items!($traitinstance $($t)*); - }; - - // exit - ($traitinstance:ident) => () -} - -#[macro_export] -#[doc(hidden)] -macro_rules! __impl_store_item { - ($name:ident $traitinstance:ident) => { type $name = $name<$traitinstance>; } -} - -#[macro_export] -#[doc(hidden)] -macro_rules! __impl_store_json_metadata { - ( - $cratename:ident; - $($rest:tt)* - ) => { - pub fn store_json_metadata() -> &'static str { - concat!(r#"{ "prefix": ""#, stringify!($cratename), r#"", "items": {"#, - __store_functions_to_json!(""; $($rest)*), " } }") - } - } -} - -#[macro_export] -#[doc(hidden)] -macro_rules! __store_functions_to_json { - ( - $prefix_str:tt; - $(#[doc = $doc_attr:tt])* - $name:ident : - default $ty:ty; $($t:tt)* - ) => { - concat!( - __store_function_to_json!($prefix_str, - __function_doc_to_json!(""; $($doc_attr)*), - $name, __store_type_to_json!($ty), default - ), - __store_functions_to_json!(","; $($t)*) - ) - }; - ( - $prefix_str:tt; - $(#[doc = $doc_attr:tt])* - pub $name:ident : - default $ty:ty; $($t:tt)* - ) => { - concat!( - __store_function_to_json!($prefix_str, - __function_doc_to_json!(""; $($doc_attr)*), - $name, __store_type_to_json!($ty), default - ), - __store_functions_to_json!(","; $($t)*) - ) - }; - ( - $prefix_str:tt; - $(#[doc = $doc_attr:tt])* - $name:ident : - required $ty:ty; $($t:tt)* - ) => { - concat!( - __store_function_to_json!($prefix_str, - __function_doc_to_json!(""; $($doc_attr)*), - $name, __store_type_to_json!($ty), required - ), - __store_functions_to_json!(","; $($t)*) - ) - }; - ( - $prefix_str:tt; - $(#[doc = $doc_attr:tt])* - pub $name:ident : - required $ty:ty; $($t:tt)* - ) => { - concat!( - __store_function_to_json!($prefix_str, - __function_doc_to_json!(""; $($doc_attr)*), - $name, __store_type_to_json!($ty), required - ), - __store_functions_to_json!(","; $($t)*) - ) - }; - // simple values - ( - $prefix_str:tt; - $(#[doc = $doc_attr:tt])* - $name:ident : - $ty:ty; $($t:tt)* - ) => { - concat!( - __store_function_to_json!($prefix_str, - __function_doc_to_json!(""; $($doc_attr)*), - $name, __store_type_to_json!($ty) - ), - __store_functions_to_json!(","; $($t)*) - ) - }; - ( - $prefix_str:tt; - $(#[doc = $doc_attr:tt])* - pub $name:ident : - $ty:ty; $($t:tt)* - ) => { - concat!( - __store_function_to_json!($prefix_str, - __function_doc_to_json!(""; $($doc_attr)*), - $name, __store_type_to_json!($ty) - ), - __store_functions_to_json!(","; $($t)*) - ) - }; - - ( - $prefix_str:tt; - $(#[doc = $doc_attr:tt])* - $name:ident get($getfn:ident) : - default $ty:ty; $($t:tt)* - ) => { - concat!( - __store_function_to_json!($prefix_str, - __function_doc_to_json!(""; $($doc_attr)*), - $name, __store_type_to_json!($ty), default - ), - __store_functions_to_json!(","; $($t)*) - ) - }; - ( - $prefix_str:tt; - $(#[doc = $doc_attr:tt])* - pub $name:ident get($getfn:ident) : - default $ty:ty; $($t:tt)* - ) => { - concat!( - __store_function_to_json!($prefix_str, - __function_doc_to_json!(""; $($doc_attr)*), - $name, __store_type_to_json!($ty), default - ), - __store_functions_to_json!(","; $($t)*) - ) - }; - ( - $prefix_str:tt; - $(#[doc = $doc_attr:tt])* - $name:ident get($getfn:ident) : - required $ty:ty; $($t:tt)* - ) => { - concat!( - __store_function_to_json!($prefix_str, - __function_doc_to_json!(""; $($doc_attr)*), - $name, __store_type_to_json!($ty), required - ), - __store_functions_to_json!(","; $($t)*) - ) - }; - ( - $prefix_str:tt; - $(#[doc = $doc_attr:tt])* - pub $name:ident get($getfn:ident) : - required $ty:ty; $($t:tt)* - ) => { - concat!( - __store_function_to_json!($prefix_str, - __function_doc_to_json!(""; $($doc_attr)*), - $name, __store_type_to_json!($ty), required - ), - __store_functions_to_json!(","; $($t)*) - ) - }; - ( - $prefix_str:tt; - $(#[doc = $doc_attr:tt])* - $name:ident get($getfn:ident) : - $ty:ty; $($t:tt)* - ) => { - concat!( - __store_function_to_json!($prefix_str, - __function_doc_to_json!(""; $($doc_attr)*), - $name, __store_type_to_json!($ty) - ), - __store_functions_to_json!(","; $($t)*) - ) - }; - ( - $prefix_str:tt; - $(#[doc = $doc_attr:tt])* - pub $name:ident get($getfn:ident) : - $ty:ty; $($t:tt)* - ) => { - concat!( - __store_function_to_json!($prefix_str, - __function_doc_to_json!(""; $($doc_attr)*), - $name, __store_type_to_json!($ty) - ), - __store_functions_to_json!(","; $($t)*) - ) - }; - - // maps - ( - $prefix_str:tt; - $(#[doc = $doc_attr:tt])* - $name:ident : - default map [$kty:ty => $ty:ty]; $($t:tt)* - ) => { - concat!( - __store_function_to_json!($prefix_str, - __function_doc_to_json!(""; $($doc_attr)*), - $name, __store_type_to_json!($kty, $ty), default - ), - __store_functions_to_json!(","; $($t)*) - ) - }; - ( - $prefix_str:tt; - $(#[doc = $doc_attr:tt])* - pub $name:ident : - default map [$kty:ty => $ty:ty]; $($t:tt)* - ) => { - concat!( - __store_function_to_json!($prefix_str, - __function_doc_to_json!(""; $($doc_attr)*), - $name, __store_type_to_json!($kty, $ty), default - ), - __store_functions_to_json!(","; $($t)*) - ) - }; - ( - $prefix_str:tt; - $(#[doc = $doc_attr:tt])* - $name:ident : - required map [$kty:ty => $ty:ty]; $($t:tt)* - ) => { - concat!( - __store_function_to_json!($prefix_str, - __function_doc_to_json!(""; $($doc_attr)*), - $name, __store_type_to_json!($kty, $ty), required - ), - __store_functions_to_json!(","; $($t)*) - ) - }; - ( - $prefix_str:tt; - $(#[doc = $doc_attr:tt])* - pub $name:ident : - required map [$kty:ty => $ty:ty]; $($t:tt)* - ) => { - concat!( - __store_function_to_json!($prefix_str, - __function_doc_to_json!(""; $($doc_attr)*), - $name, __store_type_to_json!($kty, $ty), required - ), - __store_functions_to_json!(","; $($t)*) - ) - }; - ( - $prefix_str:tt; - $(#[doc = $doc_attr:tt])* - $name:ident : - map [$kty:ty => $ty:ty]; $($t:tt)* - ) => { - concat!( - __store_function_to_json!($prefix_str, - __function_doc_to_json!(""; $($doc_attr)*), - $name, __store_type_to_json!($kty, $ty) - ), - __store_functions_to_json!(","; $($t)*) - ) - }; - ( - $prefix_str:tt; - $(#[doc = $doc_attr:tt])* - pub $name:ident : - map [$kty:ty => $ty:ty]; $($t:tt)* - ) => { - concat!( - __store_function_to_json!($prefix_str, - __function_doc_to_json!(""; $($doc_attr)*), - $name, __store_type_to_json!($kty, $ty) - ), - __store_functions_to_json!(","; $($t)*) - ) - }; - - ( - $prefix_str:tt; - $(#[doc = $doc_attr:tt])* $name:ident get($getfn:ident) : - default map [$kty:ty => $ty:ty]; $($t:tt)* - ) => { - concat!( - __store_function_to_json!($prefix_str, - __function_doc_to_json!(""; $($doc_attr)*), - $name, __store_type_to_json!($kty, $ty), default - ), - __store_functions_to_json!(","; $($t)*) - ) - }; - ( - $prefix_str:tt; - $(#[doc = $doc_attr:tt])* - pub $name:ident get($getfn:ident) : - default map [$kty:ty => $ty:ty]; $($t:tt)* - ) => { - concat!( - __store_function_to_json!($prefix_str, - __function_doc_to_json!(""; $($doc_attr)*), - $name, __store_type_to_json!($kty, $ty), default - ), - __store_functions_to_json!(","; $($t)*) - ) - }; - ( - $prefix_str:tt; - $(#[doc = $doc_attr:tt])* $name:ident get($getfn:ident) : - required map [$kty:ty => $ty:ty]; $($t:tt)* - ) => { - concat!( - __store_function_to_json!($prefix_str, - __function_doc_to_json!(""; $($doc_attr)*), - $name, __store_type_to_json!($kty, $ty), required - ), - __store_functions_to_json!(","; $($t)*) - ) - }; - ( - $prefix_str:tt; - $(#[doc = $doc_attr:tt])* - pub $name:ident get($getfn:ident) : - required map [$kty:ty => $ty:ty]; $($t:tt)* - ) => { - concat!( - __store_function_to_json!($prefix_str, - __function_doc_to_json!(""; $($doc_attr)*), - $name, __store_type_to_json!($kty, $ty), required - ), - __store_functions_to_json!(","; $($t)*) - ) - }; - ( - $prefix_str:tt; - $(#[doc = $doc_attr:tt])* - $name:ident get($getfn:ident) : - map [$kty:ty => $ty:ty]; $($t:tt)* - ) => { - concat!( - __store_function_to_json!($prefix_str, - __function_doc_to_json!(""; $($doc_attr)*), - $name, __store_type_to_json!($kty, $ty) - ), - __store_functions_to_json!(","; $($t)*) - ) - }; - ( - $prefix_str:tt; - $(#[doc = $doc_attr:tt])* - pub $name:ident get($getfn:ident) : - map [$kty:ty => $ty:ty]; $($t:tt)* - ) => { - concat!( - __store_function_to_json!($prefix_str, - __function_doc_to_json!(""; $($doc_attr)*), - $name, __store_type_to_json!($kty, $ty) - ), - __store_functions_to_json!(","; $($t)*) - ) - }; - ($prefix_str:tt;) => { "" } -} - -#[macro_export] -#[doc(hidden)] -macro_rules! __store_function_to_json { - ($prefix_str:tt, $fn_doc:expr, $name:ident, $type:expr, $modifier:ident) => { - __store_function_to_json!($prefix_str; $fn_doc; $name; $type; - concat!("\"", stringify!($modifier), "\"")) - }; - ($prefix_str:tt, $fn_doc:expr, $name:ident, $type:expr) => { - __store_function_to_json!($prefix_str; $fn_doc; $name; $type; "null") - }; - ($prefix_str:tt; $fn_doc:expr; $name:ident; $type:expr; $modifier:expr) => { - concat!($prefix_str, " \"", stringify!($name), "\": { ", - r#""description": ["#, $fn_doc, " ], ", - r#""modifier": "#, $modifier, r#", "type": "#, $type, r#" }"# - ) - } -} - -#[macro_export] -#[doc(hidden)] -macro_rules! __store_type_to_json { - ($name:ty) => { - concat!("\"", stringify!($name), "\"") - }; - ($key: ty, $value:ty) => { - concat!(r#"{ "key": ""#, stringify!($key), r#"", "value": ""#, - stringify!($value), "\" }") - } -} - -#[cfg(test)] -// Do not complain about unused `dispatch` and `dispatch_aux`. -#[allow(dead_code)] -mod tests { - use std::collections::HashMap; - use std::cell::RefCell; - use codec::Codec; - use super::*; - use serde; - use serde_json; - - impl Storage for RefCell, Vec>> { - fn exists(&self, key: &[u8]) -> bool { - self.borrow_mut().get(key).is_some() - } - - fn get(&self, key: &[u8]) -> Option { - self.borrow_mut().get(key).map(|v| T::decode(&mut &v[..]).unwrap()) - } - - fn put(&self, key: &[u8], val: &T) { - self.borrow_mut().insert(key.to_owned(), val.encode()); - } - - fn kill(&self, key: &[u8]) { - self.borrow_mut().remove(key); - } - } - - storage_items! { - Value: b"a" => u32; - List: b"b:" => list [u64]; - Map: b"c:" => map [u32 => [u8; 32]]; - } - - #[test] - fn value() { - let storage = RefCell::new(HashMap::new()); - assert!(Value::get(&storage).is_none()); - Value::put(&100_000, &storage); - assert_eq!(Value::get(&storage), Some(100_000)); - Value::kill(&storage); - assert!(Value::get(&storage).is_none()); - } - - #[test] - fn list() { - let storage = RefCell::new(HashMap::new()); - assert_eq!(List::len(&storage), 0); - assert!(List::items(&storage).is_empty()); - - List::set_items(&[0, 2, 4, 6, 8], &storage); - assert_eq!(List::items(&storage), &[0, 2, 4, 6, 8]); - assert_eq!(List::len(&storage), 5); - - List::set_item(2, &10, &storage); - assert_eq!(List::items(&storage), &[0, 2, 10, 6, 8]); - assert_eq!(List::len(&storage), 5); - - List::clear(&storage); - assert_eq!(List::len(&storage), 0); - assert!(List::items(&storage).is_empty()); - } - - #[test] - fn map() { - let storage = RefCell::new(HashMap::new()); - assert!(Map::get(&5, &storage).is_none()); - Map::insert(&5, &[1; 32], &storage); - assert_eq!(Map::get(&5, &storage), Some([1; 32])); - assert_eq!(Map::take(&5, &storage), Some([1; 32])); - assert!(Map::get(&5, &storage).is_none()); - assert!(Map::get(&999, &storage).is_none()); - } - - pub trait Trait { - type Origin; - } - - decl_module! { - pub struct Module for enum Call where origin: T::Origin {} - } - - decl_storage! { - trait Store for Module as TestStorage { - /// Hello, this is doc! - U32 : u32; - GETU32 get(u32_getter): u32; - pub PUBU32 : u32; - pub GETPUBU32 get(pub_u32_getter): u32; - U32Default : default u32; - GETU32Default get(get_u32_default): default u32; - pub PUBU32Default : default u32; - pub GETPUBU32Default get(pub_get_u32_default): default u32; - U32Required : required u32; - GETU32Required get(get_u32_required): required u32; - pub PUBU32Required : required u32; - pub GETPUBU32Required get(pub_get_u32_required): required u32; - - MAPU32 : map [ u32 => String ]; - /// Hello, this is doc! - /// Hello, this is doc 2! - GETMAPU32 get(map_u32_getter): map [ u32 => String ]; - pub PUBMAPU32 : map [ u32 => String ]; - pub GETPUBMAPU32 get(map_pub_u32_getter): map [ u32 => String ]; - MAPU32Default : default map [ u32 => String ]; - GETMAPU32Default get(map_get_u32_default): default map [ u32 => String ]; - pub PUBMAPU32Default : default map [ u32 => String ]; - pub GETPUBMAPU32Default get(map_pub_get_u32_default): default map [ u32 => String ]; - MAPU32Required : required map [ u32 => String ]; - GETMAPU32Required get(map_get_u32_required): required map [ u32 => String ]; - pub PUBMAPU32Required : required map [ u32 => String ]; - pub GETPUBMAPU32Required get(map_pub_get_u32_required): required map [ u32 => String ]; - - } - } - - struct TraitImpl {} - - impl Trait for TraitImpl { - type Origin = u32; - } - - const EXPECTED_METADATA: &str = concat!( - r#"{ "prefix": "TestStorage", "items": { "#, - r#""U32": { "description": [ " Hello, this is doc!" ], "modifier": null, "type": "u32" }, "#, - r#""GETU32": { "description": [ ], "modifier": null, "type": "u32" }, "#, - r#""PUBU32": { "description": [ ], "modifier": null, "type": "u32" }, "#, - r#""GETPUBU32": { "description": [ ], "modifier": null, "type": "u32" }, "#, - r#""U32Default": { "description": [ ], "modifier": "default", "type": "u32" }, "#, - r#""GETU32Default": { "description": [ ], "modifier": "default", "type": "u32" }, "#, - r#""PUBU32Default": { "description": [ ], "modifier": "default", "type": "u32" }, "#, - r#""GETPUBU32Default": { "description": [ ], "modifier": "default", "type": "u32" }, "#, - r#""U32Required": { "description": [ ], "modifier": "required", "type": "u32" }, "#, - r#""GETU32Required": { "description": [ ], "modifier": "required", "type": "u32" }, "#, - r#""PUBU32Required": { "description": [ ], "modifier": "required", "type": "u32" }, "#, - r#""GETPUBU32Required": { "description": [ ], "modifier": "required", "type": "u32" }, "#, - r#""MAPU32": { "description": [ ], "modifier": null, "type": { "key": "u32", "value": "String" } }, "#, - r#""GETMAPU32": { "description": [ " Hello, this is doc!", " Hello, this is doc 2!" ], "modifier": null, "type": { "key": "u32", "value": "String" } }, "#, - r#""PUBMAPU32": { "description": [ ], "modifier": null, "type": { "key": "u32", "value": "String" } }, "#, - r#""GETPUBMAPU32": { "description": [ ], "modifier": null, "type": { "key": "u32", "value": "String" } }, "#, - r#""MAPU32Default": { "description": [ ], "modifier": "default", "type": { "key": "u32", "value": "String" } }, "#, - r#""GETMAPU32Default": { "description": [ ], "modifier": "default", "type": { "key": "u32", "value": "String" } }, "#, - r#""PUBMAPU32Default": { "description": [ ], "modifier": "default", "type": { "key": "u32", "value": "String" } }, "#, - r#""GETPUBMAPU32Default": { "description": [ ], "modifier": "default", "type": { "key": "u32", "value": "String" } }, "#, - r#""MAPU32Required": { "description": [ ], "modifier": "required", "type": { "key": "u32", "value": "String" } }, "#, - r#""GETMAPU32Required": { "description": [ ], "modifier": "required", "type": { "key": "u32", "value": "String" } }, "#, - r#""PUBMAPU32Required": { "description": [ ], "modifier": "required", "type": { "key": "u32", "value": "String" } }, "#, - r#""GETPUBMAPU32Required": { "description": [ ], "modifier": "required", "type": { "key": "u32", "value": "String" } }"#, - " } }" - ); - - #[test] - fn store_json_metadata() { - let metadata = Module::::store_json_metadata(); - assert_eq!(EXPECTED_METADATA, metadata); - let _: serde::de::IgnoredAny = - serde_json::from_str(metadata).expect("Is valid json syntax"); - } -} - -#[cfg(test)] -// Do not complain about unused `dispatch` and `dispatch_aux`. -#[allow(dead_code)] -mod test2 { - pub trait Trait { - type Origin; - } - - decl_module! { - pub struct Module for enum Call where origin: T::Origin {} - } - - type PairOf = (T, T); - - decl_storage! { - trait Store for Module as TestStorage { - SingleDef : default u32; - PairDef : default PairOf; - Single : u32; - Pair : (u32, u32); - } - } - - struct TraitImpl {} - - impl Trait for TraitImpl { - type Origin = u32; - } -} diff --git a/substrate/runtime-support/src/storage/mod.rs b/substrate/runtime-support/src/storage/mod.rs deleted file mode 100644 index 7b5665bf9bca8..0000000000000 --- a/substrate/runtime-support/src/storage/mod.rs +++ /dev/null @@ -1,609 +0,0 @@ -// Copyright 2017 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 . - -//! Stuff to do with the runtime's storage. - -use rstd::prelude::*; -use rstd::borrow::Borrow; -use runtime_io::{self, twox_128}; -use codec::{Codec, Decode, KeyedVec, Input}; - -#[macro_use] -pub mod generator; - -// TODO: consider using blake256 to avoid possible preimage attack. - -struct IncrementalInput<'a> { - key: &'a [u8], - pos: usize, -} - -impl<'a> Input for IncrementalInput<'a> { - fn read(&mut self, into: &mut [u8]) -> usize { - let len = runtime_io::read_storage(self.key, into, self.pos).unwrap_or(0); - let read = ::rstd::cmp::min(len, into.len()); - self.pos += read; - read - } -} - - /// Return the value of the item in storage under `key`, or `None` if there is no explicit entry. -pub fn get(key: &[u8]) -> Option { - let key = twox_128(key); - runtime_io::read_storage(&key[..], &mut [0; 0][..], 0).map(|_| { - let mut input = IncrementalInput { - key: &key[..], - pos: 0, - }; - Decode::decode(&mut input).expect("storage is not null, therefore must be a valid type") - }) -} - -/// Return the value of the item in storage under `key`, or the type's default if there is no -/// explicit entry. -pub fn get_or_default(key: &[u8]) -> T { - get(key).unwrap_or_else(Default::default) -} - -/// Return the value of the item in storage under `key`, or `default_value` if there is no -/// explicit entry. -pub fn get_or(key: &[u8], default_value: T) -> T { - get(key).unwrap_or(default_value) -} - -/// Return the value of the item in storage under `key`, or `default_value()` if there is no -/// explicit entry. -pub fn get_or_else T>(key: &[u8], default_value: F) -> T { - get(key).unwrap_or_else(default_value) -} - -/// Put `value` in storage under `key`. -pub fn put(key: &[u8], value: &T) { - value.using_encoded(|slice| runtime_io::set_storage(&twox_128(key)[..], slice)); -} - -/// Remove `key` from storage, returning its value if it had an explicit entry or `None` otherwise. -pub fn take(key: &[u8]) -> Option { - let r = get(key); - if r.is_some() { - kill(key); - } - r -} - -/// Remove `key` from storage, returning its value, or, if there was no explicit entry in storage, -/// the default for its type. -pub fn take_or_default(key: &[u8]) -> T { - take(key).unwrap_or_else(Default::default) -} - -/// Return the value of the item in storage under `key`, or `default_value` if there is no -/// explicit entry. Ensure there is no explicit entry on return. -pub fn take_or(key: &[u8], default_value: T) -> T { - take(key).unwrap_or(default_value) -} - -/// Return the value of the item in storage under `key`, or `default_value()` if there is no -/// explicit entry. Ensure there is no explicit entry on return. -pub fn take_or_else T>(key: &[u8], default_value: F) -> T { - take(key).unwrap_or_else(default_value) -} - -/// Check to see if `key` has an explicit entry in storage. -pub fn exists(key: &[u8]) -> bool { - runtime_io::exists_storage(&twox_128(key)[..]) -} - -/// Ensure `key` has no explicit entry in storage. -pub fn kill(key: &[u8]) { - runtime_io::clear_storage(&twox_128(key)[..]); -} - -/// Get a Vec of bytes from storage. -pub fn get_raw(key: &[u8]) -> Option> { - runtime_io::storage(&twox_128(key)[..]) -} - -/// Put a raw byte slice into storage. -pub fn put_raw(key: &[u8], value: &[u8]) { - runtime_io::set_storage(&twox_128(key)[..], value) -} - -/// The underlying runtime storage. -pub struct RuntimeStorage; - -impl ::GenericStorage for RuntimeStorage { - fn exists(&self, key: &[u8]) -> bool { - super::storage::exists(key) - } - - /// Load the bytes of a key from storage. Can panic if the type is incorrect. - fn get(&self, key: &[u8]) -> Option { - super::storage::get(key) - } - - /// Put a value in under a key. - fn put(&self, key: &[u8], val: &T) { - super::storage::put(key, val) - } - - /// Remove the bytes of a key from storage. - fn kill(&self, key: &[u8]) { - super::storage::kill(key) - } - - /// Take a value from storage, deleting it after reading. - fn take(&self, key: &[u8]) -> Option { - super::storage::take(key) - } -} - -/// A trait for working with macro-generated storage values under the substrate storage API. -pub trait StorageValue { - /// The type that get/take return. - type Query; - - /// Get the storage key. - fn key() -> &'static [u8]; - - /// Does the value (explicitly) exist in storage? - fn exists() -> bool; - - /// Load the value from the provided storage instance. - fn get() -> Self::Query; - - /// Store a value under this key into the provided storage instance. - fn put>(val: Arg); - - /// Mutate the value - fn mutate(f: F); - - /// Clear the storage value. - fn kill(); - - /// Take a value from storage, removing it afterwards. - fn take() -> Self::Query; -} - -impl StorageValue for U where U: generator::StorageValue { - type Query = U::Query; - - fn key() -> &'static [u8] { - >::key() - } - fn exists() -> bool { - U::exists(&RuntimeStorage) - } - fn get() -> Self::Query { - U::get(&RuntimeStorage) - } - fn put>(val: Arg) { - U::put(val.borrow(), &RuntimeStorage) - } - fn mutate(f: F) { - U::mutate(f, &RuntimeStorage) - } - fn kill() { - U::kill(&RuntimeStorage) - } - fn take() -> Self::Query { - U::take(&RuntimeStorage) - } -} - -/// A strongly-typed list in storage. -pub trait StorageList { - /// Get the prefix key in storage. - fn prefix() -> &'static [u8]; - - /// Get the key used to store the length field. - fn len_key() -> Vec; - - /// Get the storage key used to fetch a value at a given index. - fn key_for(index: u32) -> Vec; - - /// Read out all the items. - fn items() -> Vec; - - /// Set the current set of items. - fn set_items(items: &[T]); - - /// Set the item at the given index. - fn set_item>(index: u32, val: Arg); - - /// Load the value at given index. Returns `None` if the index is out-of-bounds. - fn get(index: u32) -> Option; - - /// Load the length of the list - fn len() -> u32; - - /// Clear the list. - fn clear(); -} - -impl StorageList for U where U: generator::StorageList { - fn prefix() -> &'static [u8] { - >::prefix() - } - - fn len_key() -> Vec { - >::len_key() - } - - fn key_for(index: u32) -> Vec { - >::key_for(index) - } - - fn items() -> Vec { - U::items(&RuntimeStorage) - } - - fn set_items(items: &[T]) { - U::set_items(items, &RuntimeStorage) - } - - fn set_item>(index: u32, val: Arg) { - U::set_item(index, val.borrow(), &RuntimeStorage) - } - - fn get(index: u32) -> Option { - U::get(index, &RuntimeStorage) - } - - fn len() -> u32 { - U::len(&RuntimeStorage) - } - - fn clear() { - U::clear(&RuntimeStorage) - } -} - -/// A strongly-typed map in storage. -pub trait StorageMap { - /// The type that get/take return. - 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>(key: KeyArg) -> Vec; - - /// Does the value (explicitly) exist in storage? - fn exists>(key: KeyArg) -> bool; - - /// Load the value associated with the given key from the map. - fn get>(key: KeyArg) -> Self::Query; - - /// Store a value to be associated with the given key from the map. - fn insert, ValArg: Borrow>(key: KeyArg, val: ValArg); - - /// Remove the value under a key. - fn remove>(key: KeyArg); - - /// Mutate the value under a key. - fn mutate, F: FnOnce(&mut Self::Query)>(key: KeyArg, f: F); - - /// Take the value under a key. - fn take>(key: KeyArg) -> Self::Query; -} - -impl StorageMap for U where U: generator::StorageMap { - type Query = U::Query; - - fn prefix() -> &'static [u8] { - >::prefix() - } - - fn key_for>(key: KeyArg) -> Vec { - >::key_for(key.borrow()) - } - - fn exists>(key: KeyArg) -> bool { - U::exists(key.borrow(), &RuntimeStorage) - } - - fn get>(key: KeyArg) -> Self::Query { - U::get(key.borrow(), &RuntimeStorage) - } - - fn insert, ValArg: Borrow>(key: KeyArg, val: ValArg) { - U::insert(key.borrow(), val.borrow(), &RuntimeStorage) - } - - fn remove>(key: KeyArg) { - U::remove(key.borrow(), &RuntimeStorage) - } - - fn mutate, F: FnOnce(&mut Self::Query)>(key: KeyArg, f: F) { - U::mutate(key.borrow(), f, &RuntimeStorage) - } - - fn take>(key: KeyArg) -> Self::Query { - U::take(key.borrow(), &RuntimeStorage) - } -} - -/// A trait to conveniently store a vector of storable data. -pub trait StorageVec { - type Item: Default + Sized + Codec; - const PREFIX: &'static [u8]; - - /// Get the current set of items. - fn items() -> Vec { - (0..Self::count()).into_iter().map(Self::item).collect() - } - - /// Set the current set of items. - fn set_items(items: I) - where - I: IntoIterator, - T: Borrow, - { - let mut count: u32 = 0; - - for i in items.into_iter() { - put(&count.to_keyed_vec(Self::PREFIX), i.borrow()); - count = count.checked_add(1).expect("exceeded runtime storage capacity"); - } - - Self::set_count(count); - } - - /// Push an item. - fn push(item: &Self::Item) { - let len = Self::count(); - put(&len.to_keyed_vec(Self::PREFIX), item); - Self::set_count(len + 1); - } - - fn set_item(index: u32, item: &Self::Item) { - if index < Self::count() { - put(&index.to_keyed_vec(Self::PREFIX), item); - } - } - - fn clear_item(index: u32) { - if index < Self::count() { - kill(&index.to_keyed_vec(Self::PREFIX)); - } - } - - fn item(index: u32) -> Self::Item { - get_or_default(&index.to_keyed_vec(Self::PREFIX)) - } - - fn set_count(count: u32) { - (count..Self::count()).for_each(Self::clear_item); - put(&b"len".to_keyed_vec(Self::PREFIX), &count); - } - - fn count() -> u32 { - get_or_default(&b"len".to_keyed_vec(Self::PREFIX)) - } -} - -pub mod unhashed { - use rstd::borrow::Borrow; - use super::{runtime_io, Codec, Decode, KeyedVec, Vec, IncrementalInput}; - - /// Return the value of the item in storage under `key`, or `None` if there is no explicit entry. - pub fn get(key: &[u8]) -> Option { - runtime_io::read_storage(key, &mut [0; 0][..], 0).map(|_| { - let mut input = IncrementalInput { - key, - pos: 0, - }; - Decode::decode(&mut input).expect("storage is not null, therefore must be a valid type") - }) - } - - /// Return the value of the item in storage under `key`, or the type's default if there is no - /// explicit entry. - pub fn get_or_default(key: &[u8]) -> T { - get(key).unwrap_or_else(Default::default) - } - - /// Return the value of the item in storage under `key`, or `default_value` if there is no - /// explicit entry. - pub fn get_or(key: &[u8], default_value: T) -> T { - get(key).unwrap_or(default_value) - } - - /// Return the value of the item in storage under `key`, or `default_value()` if there is no - /// explicit entry. - pub fn get_or_else T>(key: &[u8], default_value: F) -> T { - get(key).unwrap_or_else(default_value) - } - - /// Put `value` in storage under `key`. - pub fn put(key: &[u8], value: &T) { - value.using_encoded(|slice| runtime_io::set_storage(key, slice)); - } - - /// Remove `key` from storage, returning its value if it had an explicit entry or `None` otherwise. - pub fn take(key: &[u8]) -> Option { - let r = get(key); - if r.is_some() { - kill(key); - } - r - } - - /// Remove `key` from storage, returning its value, or, if there was no explicit entry in storage, - /// the default for its type. - pub fn take_or_default(key: &[u8]) -> T { - take(key).unwrap_or_else(Default::default) - } - - /// Return the value of the item in storage under `key`, or `default_value` if there is no - /// explicit entry. Ensure there is no explicit entry on return. - pub fn take_or(key: &[u8], default_value: T) -> T { - take(key).unwrap_or(default_value) - } - - /// Return the value of the item in storage under `key`, or `default_value()` if there is no - /// explicit entry. Ensure there is no explicit entry on return. - pub fn take_or_else T>(key: &[u8], default_value: F) -> T { - take(key).unwrap_or_else(default_value) - } - - /// Check to see if `key` has an explicit entry in storage. - pub fn exists(key: &[u8]) -> bool { - runtime_io::read_storage(key, &mut [0;0][..], 0).is_some() - } - - /// Ensure `key` has no explicit entry in storage. - pub fn kill(key: &[u8]) { - runtime_io::clear_storage(key); - } - - /// Ensure keys with the given `prefix` have no entries in storage. - pub fn kill_prefix(prefix: &[u8]) { - runtime_io::clear_prefix(prefix); - } - - /// Get a Vec of bytes from storage. - pub fn get_raw(key: &[u8]) -> Option> { - runtime_io::storage(key) - } - - /// Put a raw byte slice into storage. - pub fn put_raw(key: &[u8], value: &[u8]) { - runtime_io::set_storage(key, value) - } - - /// A trait to conveniently store a vector of storable data. - pub trait StorageVec { - type Item: Default + Sized + Codec; - const PREFIX: &'static [u8]; - - /// Get the current set of items. - fn items() -> Vec { - (0..Self::count()).into_iter().map(Self::item).collect() - } - - /// Set the current set of items. - fn set_items(items: I) - where - I: IntoIterator, - T: Borrow, - { - let mut count: u32 = 0; - - for i in items.into_iter() { - put(&count.to_keyed_vec(Self::PREFIX), i.borrow()); - count = count.checked_add(1).expect("exceeded runtime storage capacity"); - } - - Self::set_count(count); - } - - fn set_item(index: u32, item: &Self::Item) { - if index < Self::count() { - put(&index.to_keyed_vec(Self::PREFIX), item); - } - } - - fn clear_item(index: u32) { - if index < Self::count() { - kill(&index.to_keyed_vec(Self::PREFIX)); - } - } - - fn item(index: u32) -> Self::Item { - get_or_default(&index.to_keyed_vec(Self::PREFIX)) - } - - fn set_count(count: u32) { - (count..Self::count()).for_each(Self::clear_item); - put(&b"len".to_keyed_vec(Self::PREFIX), &count); - } - - fn count() -> u32 { - get_or_default(&b"len".to_keyed_vec(Self::PREFIX)) - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use runtime_io::{twox_128, TestExternalities, with_externalities}; - - #[test] - fn integers_can_be_stored() { - let mut t = TestExternalities::new(); - with_externalities(&mut t, || { - let x = 69u32; - put(b":test", &x); - let y: u32 = get(b":test").unwrap(); - assert_eq!(x, y); - }); - with_externalities(&mut t, || { - let x = 69426942i64; - put(b":test", &x); - let y: i64 = get(b":test").unwrap(); - assert_eq!(x, y); - }); - } - - #[test] - fn bools_can_be_stored() { - let mut t = TestExternalities::new(); - with_externalities(&mut t, || { - let x = true; - put(b":test", &x); - let y: bool = get(b":test").unwrap(); - assert_eq!(x, y); - }); - - with_externalities(&mut t, || { - let x = false; - put(b":test", &x); - let y: bool = get(b":test").unwrap(); - assert_eq!(x, y); - }); - } - - #[test] - fn vecs_can_be_retrieved() { - let mut t = TestExternalities::new(); - with_externalities(&mut t, || { - runtime_io::set_storage(&twox_128(b":test"), b"\x0b\0\0\0Hello world"); - let x = b"Hello world".to_vec(); - let y = get::>(b":test").unwrap(); - assert_eq!(x, y); - - }); - } - - #[test] - fn vecs_can_be_stored() { - let mut t = TestExternalities::new(); - let x = b"Hello world".to_vec(); - - with_externalities(&mut t, || { - put(b":test", &x); - }); - - with_externalities(&mut t, || { - let y: Vec = get(b":test").unwrap(); - assert_eq!(x, y); - }); - } -} diff --git a/substrate/runtime/README.adoc b/substrate/runtime/README.adoc deleted file mode 100644 index 616c12568a076..0000000000000 --- a/substrate/runtime/README.adoc +++ /dev/null @@ -1,6 +0,0 @@ - -= Runtime - -Set of libs for the substrate runtime. - -TODO: Add READMEs to packages. diff --git a/substrate/runtime/balances/Cargo.toml b/substrate/runtime/balances/Cargo.toml deleted file mode 100644 index 5a86a99c11f30..0000000000000 --- a/substrate/runtime/balances/Cargo.toml +++ /dev/null @@ -1,36 +0,0 @@ -[package] -name = "substrate-runtime-balances" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -hex-literal = "0.1.0" -serde = { version = "1.0", default_features = false } -serde_derive = { version = "1.0", optional = true } -safe-mix = { version = "1.0", default_features = false} -substrate-keyring = { path = "../../keyring", optional = true } -substrate-codec = { path = "../../codec", default_features = false } -substrate-codec-derive = { path = "../../codec/derive", default_features = false } -substrate-primitives = { path = "../../primitives", default_features = false } -substrate-runtime-std = { path = "../../runtime-std", default_features = false } -substrate-runtime-io = { path = "../../runtime-io", default_features = false } -substrate-runtime-support = { path = "../../runtime-support", default_features = false } -substrate-runtime-primitives = { path = "../primitives", default_features = false } -substrate-runtime-system = { path = "../system", default_features = false } - -[features] -default = ["std"] -std = [ - "serde/std", - "serde_derive", - "safe-mix/std", - "substrate-keyring", - "substrate-codec/std", - "substrate-codec-derive/std", - "substrate-primitives/std", - "substrate-runtime-std/std", - "substrate-runtime-io/std", - "substrate-runtime-support/std", - "substrate-runtime-primitives/std", - "substrate-runtime-system/std", -] diff --git a/substrate/runtime/balances/src/address.rs b/substrate/runtime/balances/src/address.rs deleted file mode 100644 index 01fb25439507b..0000000000000 --- a/substrate/runtime/balances/src/address.rs +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2017 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 . - -//! Address type that is union of index and id for an account. - -#[cfg(feature = "std")] -use std::fmt; -use super::{Member, Decode, Encode, As, Input, Output}; - -/// A vetted and verified extrinsic from the external world. -#[derive(PartialEq, Eq, Clone)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug, Hash))] -pub enum Address where - AccountId: Member, - AccountIndex: Member, -{ - /// It's an account ID (pubkey). - #[cfg_attr(feature = "std", serde(deserialize_with="AccountId::deserialize"))] - Id(AccountId), - /// It's an account index. - #[cfg_attr(feature = "std", serde(deserialize_with="AccountIndex::deserialize"))] - Index(AccountIndex), -} - -#[cfg(feature = "std")] -impl fmt::Display for Address where - AccountId: Member, - AccountIndex: Member, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(f, "{:?}", self) - } -} - -impl From for Address where - AccountId: Member, - AccountIndex: Member, -{ - fn from(a: AccountId) -> Self { - Address::Id(a) - } -} - -fn need_more_than(a: T, b: T) -> Option { - if a < b { Some(a) } else { None } -} - -impl Decode for Address where - AccountId: Member + Decode, - AccountIndex: Member + Decode + PartialOrd + Ord + As + As + As + Copy, -{ - fn decode(input: &mut I) -> Option { - Some(match input.read_byte()? { - x @ 0x00...0xef => Address::Index(As::sa(x)), - 0xfc => Address::Index(As::sa(need_more_than(0xef, u16::decode(input)?)?)), - 0xfd => Address::Index(As::sa(need_more_than(0xffff, u32::decode(input)?)?)), - 0xfe => Address::Index(need_more_than(As::sa(0xffffffffu32), Decode::decode(input)?)?), - 0xff => Address::Id(Decode::decode(input)?), - _ => return None, - }) - } -} - -impl Encode for Address where - AccountId: Member + Encode, - AccountIndex: Member + Encode + PartialOrd + Ord + As + As + As + Copy, -{ - fn encode_to(&self, dest: &mut T) { - match *self { - Address::Id(ref i) => { - dest.push_byte(255); - dest.push(i); - } - Address::Index(i) if i > As::sa(0xffffffffu32) => { - dest.push_byte(254); - dest.push(&i); - } - Address::Index(i) if i > As::sa(0xffffu32) => { - dest.push_byte(253); - dest.push(&As::::as_(i)); - } - Address::Index(i) if i >= As::sa(0xf0u32) => { - dest.push_byte(252); - dest.push(&As::::as_(i)); - } - Address::Index(i) => dest.push_byte(As::::as_(i)), - } - } -} - -impl Default for Address where - AccountId: Member + Default, - AccountIndex: Member, -{ - fn default() -> Self { - Address::Id(Default::default()) - } -} diff --git a/substrate/runtime/balances/src/genesis_config.rs b/substrate/runtime/balances/src/genesis_config.rs deleted file mode 100644 index 1b88353c632e3..0000000000000 --- a/substrate/runtime/balances/src/genesis_config.rs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2017 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 . - -//! Build a balances genesis block. - -#![cfg(feature = "std")] - -use std::collections::HashMap; -use rstd::prelude::*; -use codec::Encode; -use runtime_support::{StorageValue, StorageMap}; -use primitives::traits::{Zero, As}; -use substrate_primitives::Blake2Hasher; -use {runtime_io, primitives}; -use super::{Trait, ENUM_SET_SIZE, EnumSet, NextEnumSet, CreationFee, TransferFee, - ReclaimRebate, ExistentialDeposit, TransactionByteFee, TransactionBaseFee, TotalIssuance, - FreeBalance}; - -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -#[serde(deny_unknown_fields)] -pub struct GenesisConfig { - pub balances: Vec<(T::AccountId, T::Balance)>, - pub transaction_base_fee: T::Balance, - pub transaction_byte_fee: T::Balance, - pub transfer_fee: T::Balance, - pub creation_fee: T::Balance, - pub reclaim_rebate: T::Balance, - pub existential_deposit: T::Balance, -} - -impl Default for GenesisConfig { - fn default() -> Self { - GenesisConfig { - balances: vec![], - transaction_base_fee: T::Balance::sa(0), - transaction_byte_fee: T::Balance::sa(0), - transfer_fee: T::Balance::sa(0), - creation_fee: T::Balance::sa(0), - existential_deposit: T::Balance::sa(0), - reclaim_rebate: T::Balance::sa(0), - } - } -} - -impl primitives::BuildStorage for GenesisConfig { - fn build_storage(self) -> ::std::result::Result, Vec>, String> { - let total_issuance: T::Balance = self.balances.iter().fold(Zero::zero(), |acc, &(_, n)| acc + n); - - let mut r: runtime_io::TestExternalities = map![ - Self::hash(>::key()).to_vec() => T::AccountIndex::sa(self.balances.len() / ENUM_SET_SIZE).encode(), - Self::hash(>::key()).to_vec() => self.transaction_base_fee.encode(), - Self::hash(>::key()).to_vec() => self.transaction_byte_fee.encode(), - Self::hash(>::key()).to_vec() => self.transfer_fee.encode(), - Self::hash(>::key()).to_vec() => self.creation_fee.encode(), - Self::hash(>::key()).to_vec() => self.existential_deposit.encode(), - Self::hash(>::key()).to_vec() => self.reclaim_rebate.encode(), - Self::hash(>::key()).to_vec() => total_issuance.encode() - ]; - - let ids: Vec<_> = self.balances.iter().map(|x| x.0.clone()).collect(); - for i in 0..(ids.len() + ENUM_SET_SIZE - 1) / ENUM_SET_SIZE { - r.insert(Self::hash(&>::key_for(T::AccountIndex::sa(i))).to_vec(), - ids[i * ENUM_SET_SIZE..ids.len().min((i + 1) * ENUM_SET_SIZE)].to_owned().encode()); - } - for (who, value) in self.balances.into_iter() { - r.insert(Self::hash(&>::key_for(who)).to_vec(), value.encode()); - } - Ok(r.into()) - } -} diff --git a/substrate/runtime/balances/src/lib.rs b/substrate/runtime/balances/src/lib.rs deleted file mode 100644 index 3451b55f6aa47..0000000000000 --- a/substrate/runtime/balances/src/lib.rs +++ /dev/null @@ -1,660 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! Balances: Handles balances. - -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(feature = "std")] -extern crate serde; - -#[cfg(feature = "std")] -#[macro_use] -extern crate serde_derive; - -#[macro_use] -extern crate substrate_runtime_support as runtime_support; - -#[cfg_attr(feature = "std", macro_use)] -extern crate substrate_runtime_std as rstd; - -#[macro_use] -extern crate substrate_codec_derive; - -extern crate substrate_codec as codec; -extern crate substrate_primitives; -extern crate substrate_runtime_io as runtime_io; -extern crate substrate_runtime_primitives as primitives; -extern crate substrate_runtime_system as system; - -use rstd::prelude::*; -use rstd::{cmp, result}; -use codec::{Encode, Decode, Codec, Input, Output}; -use runtime_support::{StorageValue, StorageMap, Parameter}; -use runtime_support::dispatch::Result; -use primitives::traits::{Zero, One, SimpleArithmetic, OnFinalise, MakePayment, - As, Lookup, Member, CheckedAdd, CheckedSub}; -use address::Address as RawAddress; -use system::{ensure_signed, ensure_root}; - -mod mock; - -pub mod address; -mod tests; -mod genesis_config; - -#[cfg(feature = "std")] -pub use genesis_config::GenesisConfig; - -/// Number of account IDs stored per enum set. -const ENUM_SET_SIZE: usize = 64; - -/// The byte to identify intention to reclaim an existing account index. -const RECLAIM_INDEX_MAGIC: usize = 0x69; - -pub type Address = RawAddress<::AccountId, ::AccountIndex>; - -/// The account with the given id was killed. -pub trait OnFreeBalanceZero { - /// The account was the given id was killed. - fn on_free_balance_zero(who: &AccountId); -} - -impl OnFreeBalanceZero for () { - fn on_free_balance_zero(_who: &AccountId) {} -} -impl< - AccountId, - X: OnFreeBalanceZero, - Y: OnFreeBalanceZero, -> OnFreeBalanceZero for (X, Y) { - fn on_free_balance_zero(who: &AccountId) { - X::on_free_balance_zero(who); - Y::on_free_balance_zero(who); - } -} - -/// Trait for a hook to get called when some balance has been minted, causing dilution. -pub trait OnDilution { - /// Some `portion` of the total balance just "grew" by `minted`. `portion` is the pre-growth - /// amount (it doesn't take account of the recent growth). - fn on_dilution(minted: Balance, portion: Balance); -} - -impl OnDilution for () { - fn on_dilution(_minted: Balance, _portion: Balance) {} -} - -/// Determinator for whether a given account is able to transfer balance. -pub trait EnsureAccountLiquid { - /// Returns `Ok` iff the account is able to transfer funds normally. `Err(...)` - /// with the reason why not otherwise. - fn ensure_account_liquid(who: &AccountId) -> Result; -} - -impl EnsureAccountLiquid for () { - fn ensure_account_liquid(_who: &AccountId) -> Result { Ok(()) } -} - -pub trait Trait: system::Trait { - /// The balance of an account. - type Balance: Parameter + SimpleArithmetic + Codec + Default + Copy + As + As + As; - /// Type used for storing an account's index; implies the maximum number of accounts the system - /// can hold. - type AccountIndex: Parameter + Member + Codec + SimpleArithmetic + As + As + As + As + As + Copy; - /// A function which is invoked when the free-balance has fallen below the existential deposit and - /// has been reduced to zero. - /// - /// Gives a chance to clean up resources associated with the given account. - type OnFreeBalanceZero: OnFreeBalanceZero; - - /// A function that returns true iff a given account can transfer its funds to another account. - type EnsureAccountLiquid: EnsureAccountLiquid; - - /// The overarching event type. - type Event: From> + Into<::Event>; -} - -decl_module! { - pub struct Module for enum Call where origin: T::Origin { - fn transfer(origin, dest: RawAddress, value: T::Balance) -> Result; - fn set_balance(origin, who: RawAddress, free: T::Balance, reserved: T::Balance) -> Result; - } -} - -decl_event!( - pub enum Event with RawEvent - where ::AccountId, ::AccountIndex, ::Balance { - /// A new account was created. - NewAccount(AccountId, AccountIndex, NewAccountOutcome), - /// An account was reaped. - ReapedAccount(AccountId), - /// Transfer succeeded (from, to, value, fees). - Transfer(AccountId, AccountId, Balance, Balance), - } -); - -decl_storage! { - trait Store for Module as Balances { - /// The total amount of stake on the system. - pub TotalIssuance get(total_issuance): required T::Balance; - /// The minimum amount allowed to keep an account open. - pub ExistentialDeposit get(existential_deposit): required T::Balance; - /// The amount credited to a destination's account whose index was reclaimed. - pub ReclaimRebate get(reclaim_rebate): required T::Balance; - /// The fee required to make a transfer. - pub TransferFee get(transfer_fee): required T::Balance; - /// The fee required to create an account. At least as big as ReclaimRebate. - pub CreationFee get(creation_fee): required T::Balance; - - /// The next free enumeration set. - pub NextEnumSet get(next_enum_set): required T::AccountIndex; - /// The enumeration sets. - pub EnumSet get(enum_set): default map [ T::AccountIndex => Vec ]; - - /// The 'free' balance of a given account. - /// - /// This is the only balance that matters in terms of most operations on tokens. It is - /// alone used to determine the balance when in the contract execution environment. When this - /// balance falls below the value of `ExistentialDeposit`, then the 'current account' is - /// deleted: specifically `FreeBalance`. Furthermore, `OnFreeBalanceZero` callback - /// is invoked, giving a chance to external modules to cleanup data associated with - /// the deleted account. - /// - /// `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): default map [ T::AccountId => T::Balance ]; - - /// The amount of the balance of a given account that is exterally reserved; this can still get - /// slashed, but gets slashed last of all. - /// - /// This balance is a 'reserve' balance that other subsystems use in order to set aside tokens - /// that are still 'owned' by the account holder, but which are unspendable. (This is different - /// and wholly unrelated to the `Bondage` system used in the staking module.) - /// - /// When this balance falls below the value of `ExistentialDeposit`, then this 'reserve account' - /// is deleted: specifically, `ReservedBalance`. - /// - /// `system::AccountNonce` is also deleted if `FreeBalance` is also zero (it also gets - /// collapsed to zero if it ever becomes less than `ExistentialDeposit`. - pub ReservedBalance get(reserved_balance): default map [ T::AccountId => T::Balance ]; - - - // Payment stuff. - - /// The fee to be paid for making a transaction; the base. - pub TransactionBaseFee get(transaction_base_fee): required T::Balance; - /// The fee to be paid for making a transaction; the per-byte portion. - pub TransactionByteFee get(transaction_byte_fee): required T::Balance; - } -} - -/// Whatever happened about the hint given when creating the new account. -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] -#[derive(Encode, Decode, PartialEq, Eq, Clone, Copy)] -pub enum NewAccountOutcome { - NoHint, - GoodHint, - BadHint, -} - -/// Outcome of a balance update. -pub enum UpdateBalanceOutcome { - /// Account balance was simply updated. - Updated, - /// The update has led to killing of the account. - AccountKilled, -} - -impl Module { - - /// Deposit one of this module's events. - fn deposit_event(event: Event) { - >::deposit_event(::Event::from(event).into()); - } - - // PUBLIC IMMUTABLES - - /// The combined balance of `who`. - pub fn total_balance(who: &T::AccountId) -> T::Balance { - Self::free_balance(who) + Self::reserved_balance(who) - } - - /// Some result as `slash(who, value)` (but without the side-effects) assuming there are no - /// balance changes in the meantime and only the reserved balance is not taken into account. - pub fn can_slash(who: &T::AccountId, value: T::Balance) -> bool { - Self::free_balance(who) >= value - } - - /// Same result as `reserve(who, value)` (but without the side-effects) assuming there - /// are no balance changes in the meantime. - pub fn can_reserve(who: &T::AccountId, value: T::Balance) -> bool { - if T::EnsureAccountLiquid::ensure_account_liquid(who).is_ok() { - Self::free_balance(who) >= value - } else { - false - } - } - - /// Lookup an T::AccountIndex to get an Id, if there's one there. - pub fn lookup_index(index: T::AccountIndex) -> Option { - let enum_set_size = Self::enum_set_size(); - let set = Self::enum_set(index / enum_set_size); - let i: usize = (index % enum_set_size).as_(); - set.get(i).map(|x| x.clone()) - } - - /// `true` if the account `index` is ready for reclaim. - pub fn can_reclaim(try_index: T::AccountIndex) -> bool { - let enum_set_size = Self::enum_set_size(); - let try_set = Self::enum_set(try_index / enum_set_size); - let i = (try_index % enum_set_size).as_(); - i < try_set.len() && Self::total_balance(&try_set[i]).is_zero() - } - - /// Lookup an address to get an Id, if there's one there. - pub fn lookup_address(a: address::Address) -> Option { - match a { - address::Address::Id(i) => Some(i), - address::Address::Index(i) => Self::lookup_index(i), - } - } - - // PUBLIC DISPATCH - - /// Transfer some liquid free balance to another staker. - pub fn transfer(origin: T::Origin, dest: Address, value: T::Balance) -> Result { - let transactor = ensure_signed(origin)?; - - let dest = Self::lookup(dest)?; - let from_balance = Self::free_balance(&transactor); - let would_create = from_balance.is_zero(); - let fee = if would_create { Self::creation_fee() } else { Self::transfer_fee() }; - let liability = value + fee; - - let new_from_balance = match from_balance.checked_sub(&liability) { - Some(b) => b, - None => return Err("balance too low to send value"), - }; - if would_create && value < Self::existential_deposit() { - return Err("value too low to create account"); - } - T::EnsureAccountLiquid::ensure_account_liquid(&transactor)?; - - let to_balance = Self::free_balance(&dest); - // NOTE: total stake being stored in the same type means that this could never overflow - // but better to be safe than sorry. - let new_to_balance = match to_balance.checked_add(&value) { - Some(b) => b, - None => return Err("destination balance too high to receive value"), - }; - - if transactor != dest { - Self::set_free_balance(&transactor, new_from_balance); - Self::decrease_total_stake_by(fee); - Self::set_free_balance_creating(&dest, new_to_balance); - Self::deposit_event(RawEvent::Transfer(transactor, dest, value, fee)); - } - - Ok(()) - } - - /// Set the balances of a given account. - fn set_balance(origin: T::Origin, who: Address, free: T::Balance, reserved: T::Balance) -> Result { - ensure_root(origin)?; - let who = Self::lookup(who)?; - Self::set_free_balance(&who, free); - Self::set_reserved_balance(&who, reserved); - Ok(()) - } - - // PUBLIC MUTABLES (DANGEROUS) - - /// Set the free balance of an account to some new value. - /// - /// Will enforce ExistentialDeposit law, anulling the account as needed. - /// In that case it will return `AccountKilled`. - pub fn set_reserved_balance(who: &T::AccountId, balance: T::Balance) -> UpdateBalanceOutcome { - if balance < Self::existential_deposit() { - >::insert(who, balance); - Self::on_reserved_too_low(who); - UpdateBalanceOutcome::AccountKilled - } else { - >::insert(who, balance); - UpdateBalanceOutcome::Updated - } - } - - /// Set the free balance of an account to some new value. Will enforce ExistentialDeposit - /// law anulling the account as needed. - /// - /// Doesn't do any preparatory work for creating a new account, so should only be used when it - /// is known that the account already exists. - /// - /// Returns if the account was successfully updated or update has led to killing of the account. - pub fn set_free_balance(who: &T::AccountId, balance: T::Balance) -> UpdateBalanceOutcome { - // Commented out for no - but consider it instructive. - // assert!(!Self::total_balance(who).is_zero()); - if balance < Self::existential_deposit() { - >::insert(who, balance); - Self::on_free_too_low(who); - UpdateBalanceOutcome::AccountKilled - } else { - >::insert(who, balance); - UpdateBalanceOutcome::Updated - } - } - - /// Set the free balance on an account to some new value. - /// - /// Same as [`set_free_balance`], but will create a new account. - /// - /// Returns if the account was successfully updated or update has led to killing of the account. - /// - /// [`set_free_balance`]: #method.set_free_balance - pub fn set_free_balance_creating(who: &T::AccountId, balance: T::Balance) -> UpdateBalanceOutcome { - let ed = >::existential_deposit(); - // If the balance is too low, then the account is reaped. - // NOTE: There are two balances for every account: `reserved_balance` and - // `free_balance`. This contract subsystem only cares about the latter: whenever - // the term "balance" is used *here* it should be assumed to mean "free balance" - // in the rest of the module. - // Free balance can never be less than ED. If that happens, it gets reduced to zero - // and the account information relevant to this subsystem is deleted (i.e. the - // account is reaped). - // NOTE: This is orthogonal to the `Bondage` value that an account has, a high - // value of which makes even the `free_balance` unspendable. - // TODO: enforce this for the other balance-altering functions. - if balance < ed { - Self::set_free_balance(who, balance); - UpdateBalanceOutcome::AccountKilled - } else { - if !>::exists(who) { - let outcome = Self::new_account(&who, balance); - let credit = match outcome { - NewAccountOutcome::GoodHint => balance + >::reclaim_rebate(), - _ => balance, - }; - Self::set_free_balance(who, credit); - Self::increase_total_stake_by(credit - balance); - } else { - Self::set_free_balance(who, balance); - } - - UpdateBalanceOutcome::Updated - } - } - - /// Adds up to `value` to the free balance of `who`. If `who` doesn't exist, it is created. - /// - /// This is a sensitive function since it circumvents any fees associated with account - /// setup. Ensure it is only called by trusted code. - /// - /// NOTE: This assumes that the total stake remains unchanged after this operation. If - /// you mean to actually mint value into existence, then use `reward` instead. - pub fn increase_free_balance_creating(who: &T::AccountId, value: T::Balance) -> UpdateBalanceOutcome { - Self::set_free_balance_creating(who, Self::free_balance(who) + value) - } - - /// Deducts up to `value` from the combined balance of `who`, preferring to deduct from the - /// free balance. This function cannot fail. - /// - /// As much funds up to `value` will be deducted as possible. If this is less than `value`, - /// then `Some(remaining)` will be returned. Full completion is given by `None`. - pub fn slash(who: &T::AccountId, value: T::Balance) -> Option { - let free_balance = Self::free_balance(who); - let free_slash = cmp::min(free_balance, value); - Self::set_free_balance(who, free_balance - free_slash); - Self::decrease_total_stake_by(free_slash); - if free_slash < value { - Self::slash_reserved(who, value - free_slash) - } else { - None - } - } - - /// Adds up to `value` to the free balance of `who`. - /// - /// If `who` doesn't exist, nothing is done and an Err returned. - pub fn reward(who: &T::AccountId, value: T::Balance) -> Result { - if Self::total_balance(who).is_zero() { - return Err("beneficiary account must pre-exist"); - } - Self::set_free_balance(who, Self::free_balance(who) + value); - Self::increase_total_stake_by(value); - Ok(()) - } - - /// Moves `value` from balance to reserved balance. - /// - /// If the free balance is lower than `value`, then no funds will be moved and an `Err` will - /// be returned to notify of this. This is different behaviour to `unreserve`. - pub fn reserve(who: &T::AccountId, value: T::Balance) -> Result { - let b = Self::free_balance(who); - if b < value { - return Err("not enough free funds") - } - T::EnsureAccountLiquid::ensure_account_liquid(who)?; - Self::set_reserved_balance(who, Self::reserved_balance(who) + value); - Self::set_free_balance(who, b - value); - Ok(()) - } - - /// Moves up to `value` from reserved balance to balance. This function cannot fail. - /// - /// As much funds up to `value` will be deducted as possible. If this is less than `value`, - /// then `Some(remaining)` will be returned. Full completion is given by `None`. - /// NOTE: This is different to `reserve`. - pub fn unreserve(who: &T::AccountId, value: T::Balance) -> Option { - let b = Self::reserved_balance(who); - let actual = cmp::min(b, value); - Self::set_free_balance(who, Self::free_balance(who) + actual); - Self::set_reserved_balance(who, b - actual); - if actual == value { - None - } else { - Some(value - actual) - } - } - - /// Deducts up to `value` from reserved balance of `who`. This function cannot fail. - /// - /// As much funds up to `value` will be deducted as possible. If this is less than `value`, - /// then `Some(remaining)` will be returned. Full completion is given by `None`. - pub fn slash_reserved(who: &T::AccountId, value: T::Balance) -> Option { - let b = Self::reserved_balance(who); - let slash = cmp::min(b, value); - Self::set_reserved_balance(who, b - slash); - Self::decrease_total_stake_by(slash); - if value == slash { - None - } else { - Some(value - slash) - } - } - - /// Moves up to `value` from reserved balance of account `slashed` to free balance of account - /// `beneficiary`. `beneficiary` must exist for this to succeed. If it does not, `Err` will be - /// returned. - /// - /// As much funds up to `value` will be moved as possible. If this is less than `value`, then - /// `Ok(Some(remaining))` will be returned. Full completion is given by `Ok(None)`. - pub fn repatriate_reserved( - slashed: &T::AccountId, - beneficiary: &T::AccountId, - value: T::Balance - ) -> result::Result, &'static str> { - if Self::total_balance(beneficiary).is_zero() { - return Err("beneficiary account must pre-exist"); - } - let b = Self::reserved_balance(slashed); - let slash = cmp::min(b, value); - Self::set_free_balance(beneficiary, Self::free_balance(beneficiary) + slash); - Self::set_reserved_balance(slashed, b - slash); - if value == slash { - Ok(None) - } else { - Ok(Some(value - slash)) - } - } - - fn enum_set_size() -> T::AccountIndex { - T::AccountIndex::sa(ENUM_SET_SIZE) - } - - /// Register a new account (with existential balance). - fn new_account(who: &T::AccountId, balance: T::Balance) -> NewAccountOutcome { - let enum_set_size = Self::enum_set_size(); - let next_set_index = Self::next_enum_set(); - let reclaim_index_magic = T::AccountIndex::sa(RECLAIM_INDEX_MAGIC); - let reclaim_index_modulus = T::AccountIndex::sa(256usize); - let quantization = T::AccountIndex::sa(256usize); - - // A little easter-egg for reclaiming dead indexes.. - let ret = { - // we quantise the number of accounts so it stays constant over a reasonable - // period of time. - let quantized_account_count: T::AccountIndex = (next_set_index * enum_set_size / quantization + One::one()) * quantization; - // then modify the starting balance to be modulo this to allow it to potentially - // identify an account index for reuse. - let maybe_try_index = balance % >::sa(quantized_account_count * reclaim_index_modulus); - let maybe_try_index = As::::as_(maybe_try_index); - - // this identifier must end with magic byte 0x69 to trigger this check (a minor - // optimisation to ensure we don't check most unintended account creations). - if maybe_try_index % reclaim_index_modulus == reclaim_index_magic { - // reuse is probably intended. first, remove magic byte. - let try_index = maybe_try_index / reclaim_index_modulus; - - // then check to see if this balance identifies a dead account index. - let set_index = try_index / enum_set_size; - let mut try_set = Self::enum_set(set_index); - let item_index = (try_index % enum_set_size).as_(); - if item_index < try_set.len() { - if Self::total_balance(&try_set[item_index]).is_zero() { - // yup - this index refers to a dead account. can be reused. - try_set[item_index] = who.clone(); - >::insert(set_index, try_set); - - Self::deposit_event(RawEvent::NewAccount(who.clone(), try_index, NewAccountOutcome::GoodHint)); - - return NewAccountOutcome::GoodHint - } - } - NewAccountOutcome::BadHint - } else { - NewAccountOutcome::NoHint - } - }; - - // insert normally as a back up - let mut set_index = next_set_index; - // defensive only: this loop should never iterate since we keep NextEnumSet up to date later. - let mut set = loop { - let set = Self::enum_set(set_index); - if set.len() < ENUM_SET_SIZE { - break set; - } - set_index += One::one(); - }; - - let index = T::AccountIndex::sa(set_index.as_() * ENUM_SET_SIZE + set.len()); - - // update set. - set.push(who.clone()); - - // keep NextEnumSet up to date - if set.len() == ENUM_SET_SIZE { - >::put(set_index + One::one()); - } - - // write set. - >::insert(set_index, set); - - Self::deposit_event(RawEvent::NewAccount(who.clone(), index, ret)); - - ret - } - - fn reap_account(who: &T::AccountId) { - >::remove(who); - Self::deposit_event(RawEvent::ReapedAccount(who.clone())); - } - - /// Kill an account's free portion. - fn on_free_too_low(who: &T::AccountId) { - Self::decrease_total_stake_by(Self::free_balance(who)); - >::remove(who); - - T::OnFreeBalanceZero::on_free_balance_zero(who); - - if Self::reserved_balance(who).is_zero() { - Self::reap_account(who); - } - } - - /// Kill an account's reserved portion. - fn on_reserved_too_low(who: &T::AccountId) { - Self::decrease_total_stake_by(Self::reserved_balance(who)); - >::remove(who); - - if Self::free_balance(who).is_zero() { - Self::reap_account(who); - } - } - - /// Increase TotalIssuance by Value. - pub fn increase_total_stake_by(value: T::Balance) { - if let Some(v) = >::total_issuance().checked_add(&value) { - >::put(v); - } - } - /// Decrease TotalIssuance by Value. - pub fn decrease_total_stake_by(value: T::Balance) { - if let Some(v) = >::total_issuance().checked_sub(&value) { - >::put(v); - } - } -} - -impl OnFinalise for Module { - fn on_finalise(_n: T::BlockNumber) { - } -} - -impl Lookup for Module { - type Source = address::Address; - type Target = T::AccountId; - fn lookup(a: Self::Source) -> result::Result { - match a { - address::Address::Id(i) => Ok(i), - address::Address::Index(i) => >::lookup_index(i).ok_or("invalid account index"), - } - } -} - -impl MakePayment for Module { - fn make_payment(transactor: &T::AccountId, encoded_len: usize) -> Result { - let b = Self::free_balance(transactor); - let transaction_fee = Self::transaction_base_fee() + Self::transaction_byte_fee() * >::sa(encoded_len as u64); - if b < transaction_fee + Self::existential_deposit() { - return Err("not enough funds for transaction fee"); - } - Self::set_free_balance(transactor, b - transaction_fee); - Self::decrease_total_stake_by(transaction_fee); - Ok(()) - } -} diff --git a/substrate/runtime/balances/src/mock.rs b/substrate/runtime/balances/src/mock.rs deleted file mode 100644 index d85a27715819d..0000000000000 --- a/substrate/runtime/balances/src/mock.rs +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! Test utilities - -#![cfg(test)] - -use primitives::BuildStorage; -use primitives::testing::{Digest, Header}; -use substrate_primitives::{H256, Blake2Hasher}; -use runtime_io; -use {GenesisConfig, Module, Trait, system}; - -impl_outer_origin!{ - pub enum Origin for Runtime {} -} - -// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. -#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] -pub struct Runtime; -impl system::Trait for Runtime { - type Origin = Origin; - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Hashing = ::primitives::traits::BlakeTwo256; - type Digest = Digest; - type AccountId = u64; - type Header = Header; - type Event = (); -} -impl Trait for Runtime { - type Balance = u64; - type AccountIndex = u64; - type OnFreeBalanceZero = (); - type EnsureAccountLiquid = (); - type Event = (); -} - -pub fn new_test_ext(ext_deposit: u64, monied: bool) -> runtime_io::TestExternalities { - let mut t = system::GenesisConfig::::default().build_storage().unwrap(); - let balance_factor = if ext_deposit > 0 { - 256 - } else { - 1 - }; - t.extend(GenesisConfig::{ - balances: if monied { - vec![(1, 10 * balance_factor), (2, 20 * balance_factor), (3, 30 * balance_factor), (4, 40 * balance_factor)] - } else { - vec![(10, balance_factor), (20, balance_factor)] - }, - transaction_base_fee: 0, - transaction_byte_fee: 0, - existential_deposit: ext_deposit, - transfer_fee: 0, - creation_fee: 0, - reclaim_rebate: 0, - }.build_storage().unwrap()); - t.into() -} - -pub type System = system::Module; -pub type Balances = Module; diff --git a/substrate/runtime/balances/src/tests.rs b/substrate/runtime/balances/src/tests.rs deleted file mode 100644 index 7279474a1f9e0..0000000000000 --- a/substrate/runtime/balances/src/tests.rs +++ /dev/null @@ -1,324 +0,0 @@ -// Copyright 2017 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 runtime_io::with_externalities; -use mock::{Balances, System, Runtime, new_test_ext}; - -#[test] -fn reward_should_work() { - with_externalities(&mut new_test_ext(0, true), || { - assert_eq!(Balances::total_balance(&1), 10); - assert_ok!(Balances::reward(&1, 10)); - assert_eq!(Balances::total_balance(&1), 20); - assert_eq!(>::get(), 110); - }); -} - -#[test] -fn indexing_lookup_should_work() { - with_externalities(&mut new_test_ext(10, true), || { - assert_eq!(Balances::lookup_index(0), Some(1)); - assert_eq!(Balances::lookup_index(1), Some(2)); - assert_eq!(Balances::lookup_index(2), Some(3)); - assert_eq!(Balances::lookup_index(3), Some(4)); - assert_eq!(Balances::lookup_index(4), None); - }); -} - -#[test] -fn default_indexing_on_new_accounts_should_work() { - with_externalities(&mut new_test_ext(10, true), || { - assert_eq!(Balances::lookup_index(4), None); - assert_ok!(Balances::transfer(Some(1).into(), 5.into(), 10)); - assert_eq!(Balances::lookup_index(4), Some(5)); - }); -} - -#[test] -fn dust_account_removal_should_work() { - with_externalities(&mut new_test_ext(256 * 10, true), || { - System::inc_account_nonce(&2); - assert_eq!(System::account_nonce(&2), 1); - assert_eq!(Balances::total_balance(&2), 256 * 20); - - assert_ok!(Balances::transfer(Some(2).into(), 5.into(), 256 * 10 + 1)); // index 1 (account 2) becomes zombie - assert_eq!(Balances::total_balance(&2), 0); - assert_eq!(Balances::total_balance(&5), 256 * 10 + 1); - assert_eq!(System::account_nonce(&2), 0); - }); -} - -#[test] -fn reclaim_indexing_on_new_accounts_should_work() { - with_externalities(&mut new_test_ext(256 * 1, true), || { - assert_eq!(Balances::lookup_index(1), Some(2)); - assert_eq!(Balances::lookup_index(4), None); - assert_eq!(Balances::total_balance(&2), 256 * 20); - - assert_ok!(Balances::transfer(Some(2).into(), 5.into(), 256 * 20)); // account 2 becomes zombie freeing index 1 for reclaim) - assert_eq!(Balances::total_balance(&2), 0); - - assert_ok!(Balances::transfer(Some(5).into(), 6.into(), 256 * 1 + 0x69)); // account 6 takes index 1. - assert_eq!(Balances::total_balance(&6), 256 * 1 + 0x69); - assert_eq!(Balances::lookup_index(1), Some(6)); - }); -} - -#[test] -fn reserved_balance_should_prevent_reclaim_count() { - with_externalities(&mut new_test_ext(256 * 1, true), || { - System::inc_account_nonce(&2); - assert_eq!(Balances::lookup_index(1), Some(2)); - assert_eq!(Balances::lookup_index(4), None); - assert_eq!(Balances::total_balance(&2), 256 * 20); - - assert_ok!(Balances::reserve(&2, 256 * 19 + 1)); // account 2 becomes mostly reserved - assert_eq!(Balances::free_balance(&2), 0); // "free" account deleted." - assert_eq!(Balances::total_balance(&2), 256 * 19 + 1); // reserve still exists. - assert_eq!(System::account_nonce(&2), 1); - - assert_ok!(Balances::transfer(Some(4).into(), 5.into(), 256 * 1 + 0x69)); // account 4 tries to take index 1 for account 5. - assert_eq!(Balances::total_balance(&5), 256 * 1 + 0x69); - assert_eq!(Balances::lookup_index(1), Some(2)); // but fails. - assert_eq!(System::account_nonce(&2), 1); - - assert_eq!(Balances::slash(&2, 256 * 18 + 2), None); // account 2 gets slashed - assert_eq!(Balances::total_balance(&2), 0); // "free" account deleted." - assert_eq!(System::account_nonce(&2), 0); - - assert_ok!(Balances::transfer(Some(4).into(), 6.into(), 256 * 1 + 0x69)); // account 4 tries to take index 1 again for account 6. - assert_eq!(Balances::total_balance(&6), 256 * 1 + 0x69); - assert_eq!(Balances::lookup_index(1), Some(6)); // and succeeds. - }); -} - -#[test] -fn balance_works() { - with_externalities(&mut new_test_ext(0, false), || { - Balances::set_free_balance(&1, 42); - assert_eq!(Balances::free_balance(&1), 42); - assert_eq!(Balances::reserved_balance(&1), 0); - assert_eq!(Balances::total_balance(&1), 42); - assert_eq!(Balances::free_balance(&2), 0); - assert_eq!(Balances::reserved_balance(&2), 0); - assert_eq!(Balances::total_balance(&2), 0); - }); -} - -#[test] -fn balance_transfer_works() { - with_externalities(&mut new_test_ext(0, false), || { - Balances::set_free_balance(&1, 111); - Balances::increase_total_stake_by(111); - assert_ok!(Balances::transfer(Some(1).into(), 2.into(), 69)); - assert_eq!(Balances::total_balance(&1), 42); - assert_eq!(Balances::total_balance(&2), 69); - }); -} - -#[test] -fn reserving_balance_should_work() { - with_externalities(&mut new_test_ext(0, false), || { - Balances::set_free_balance(&1, 111); - - assert_eq!(Balances::total_balance(&1), 111); - assert_eq!(Balances::free_balance(&1), 111); - assert_eq!(Balances::reserved_balance(&1), 0); - - assert_ok!(Balances::reserve(&1, 69)); - - assert_eq!(Balances::total_balance(&1), 111); - assert_eq!(Balances::free_balance(&1), 42); - assert_eq!(Balances::reserved_balance(&1), 69); - }); -} - -#[test] -fn balance_transfer_when_reserved_should_not_work() { - with_externalities(&mut new_test_ext(0, false), || { - Balances::set_free_balance(&1, 111); - assert_ok!(Balances::reserve(&1, 69)); - assert_noop!(Balances::transfer(Some(1).into(), 2.into(), 69), "balance too low to send value"); - }); -} - -#[test] -fn deducting_balance_should_work() { - with_externalities(&mut new_test_ext(0, false), || { - Balances::set_free_balance(&1, 111); - assert_ok!(Balances::reserve(&1, 69)); - assert_eq!(Balances::free_balance(&1), 42); - }); -} - -#[test] -fn refunding_balance_should_work() { - with_externalities(&mut new_test_ext(0, false), || { - Balances::set_free_balance(&1, 42); - Balances::set_reserved_balance(&1, 69); - Balances::unreserve(&1, 69); - assert_eq!(Balances::free_balance(&1), 111); - assert_eq!(Balances::reserved_balance(&1), 0); - }); -} - -#[test] -fn slashing_balance_should_work() { - with_externalities(&mut new_test_ext(0, false), || { - Balances::set_free_balance(&1, 111); - Balances::increase_total_stake_by(111); - assert_ok!(Balances::reserve(&1, 69)); - assert!(Balances::slash(&1, 69).is_none()); - assert_eq!(Balances::free_balance(&1), 0); - assert_eq!(Balances::reserved_balance(&1), 42); - assert_eq!(>::get(), 44); - }); -} - -#[test] -fn slashing_incomplete_balance_should_work() { - with_externalities(&mut new_test_ext(0, false), || { - Balances::set_free_balance(&1, 42); - Balances::increase_total_stake_by(42); - assert_ok!(Balances::reserve(&1, 21)); - assert!(Balances::slash(&1, 69).is_some()); - assert_eq!(Balances::free_balance(&1), 0); - assert_eq!(Balances::reserved_balance(&1), 0); - assert_eq!(>::get(), 2); - }); -} - -#[test] -fn unreserving_balance_should_work() { - with_externalities(&mut new_test_ext(0, false), || { - Balances::set_free_balance(&1, 111); - assert_ok!(Balances::reserve(&1, 111)); - Balances::unreserve(&1, 42); - assert_eq!(Balances::reserved_balance(&1), 69); - assert_eq!(Balances::free_balance(&1), 42); - }); -} - -#[test] -fn slashing_reserved_balance_should_work() { - with_externalities(&mut new_test_ext(0, false), || { - Balances::set_free_balance(&1, 111); - Balances::increase_total_stake_by(111); - assert_ok!(Balances::reserve(&1, 111)); - assert!(Balances::slash_reserved(&1, 42).is_none()); - assert_eq!(Balances::reserved_balance(&1), 69); - assert_eq!(Balances::free_balance(&1), 0); - assert_eq!(>::get(), 71); - }); -} - -#[test] -fn slashing_incomplete_reserved_balance_should_work() { - with_externalities(&mut new_test_ext(0, false), || { - Balances::set_free_balance(&1, 111); - Balances::increase_total_stake_by(111); - assert_ok!(Balances::reserve(&1, 42)); - assert!(Balances::slash_reserved(&1, 69).is_some()); - assert_eq!(Balances::free_balance(&1), 69); - assert_eq!(Balances::reserved_balance(&1), 0); - assert_eq!(>::get(), 71); - }); -} - -#[test] -fn transferring_reserved_balance_should_work() { - with_externalities(&mut new_test_ext(0, false), || { - Balances::set_free_balance(&1, 110); - Balances::set_free_balance(&2, 1); - assert_ok!(Balances::reserve(&1, 110)); - assert_ok!(Balances::repatriate_reserved(&1, &2, 41), None); - assert_eq!(Balances::reserved_balance(&1), 69); - assert_eq!(Balances::free_balance(&1), 0); - assert_eq!(Balances::reserved_balance(&2), 0); - assert_eq!(Balances::free_balance(&2), 42); - }); -} - -#[test] -fn transferring_reserved_balance_to_nonexistent_should_fail() { - with_externalities(&mut new_test_ext(0, false), || { - Balances::set_free_balance(&1, 111); - assert_ok!(Balances::reserve(&1, 111)); - assert_noop!(Balances::repatriate_reserved(&1, &2, 42), "beneficiary account must pre-exist"); - }); -} - -#[test] -fn transferring_incomplete_reserved_balance_should_work() { - with_externalities(&mut new_test_ext(0, false), || { - Balances::set_free_balance(&1, 110); - Balances::set_free_balance(&2, 1); - assert_ok!(Balances::reserve(&1, 41)); - assert!(Balances::repatriate_reserved(&1, &2, 69).unwrap().is_some()); - assert_eq!(Balances::reserved_balance(&1), 0); - assert_eq!(Balances::free_balance(&1), 69); - assert_eq!(Balances::reserved_balance(&2), 0); - assert_eq!(Balances::free_balance(&2), 42); - }); -} - -#[test] -fn transferring_too_high_value_should_not_panic() { - with_externalities(&mut new_test_ext(0, false), || { - >::insert(1, u64::max_value()); - >::insert(2, 1); - - assert_err!( - Balances::transfer(Some(1).into(), 2.into(), u64::max_value()), - "destination balance too high to receive value" - ); - - assert_eq!(Balances::free_balance(&1), u64::max_value()); - assert_eq!(Balances::free_balance(&2), 1); - }); -} - -#[test] -fn account_removal_on_free_too_low() { - with_externalities(&mut new_test_ext(100, false), || { - // Setup two accounts with free balance above the exsistential threshold. - { - Balances::set_free_balance(&1, 110); - Balances::increase_total_stake_by(110); - - Balances::set_free_balance(&2, 110); - Balances::increase_total_stake_by(110); - - assert_eq!(>::get(), 732); - } - - // Transfer funds from account 1 of such amount that after this transfer - // the balance of account 1 will be below the exsistential threshold. - // This should lead to the removal of all balance of this account. - assert_ok!(Balances::transfer(Some(1).into(), 2.into(), 20)); - - // Verify free balance removal of account 1. - assert_eq!(Balances::free_balance(&1), 0); - - // Verify that TotalIssuance tracks balance removal when free balance is too low. - assert_eq!(>::get(), 642); - }); -} diff --git a/substrate/runtime/consensus/Cargo.toml b/substrate/runtime/consensus/Cargo.toml deleted file mode 100644 index dfdebbddc118f..0000000000000 --- a/substrate/runtime/consensus/Cargo.toml +++ /dev/null @@ -1,32 +0,0 @@ -[package] -name = "substrate-runtime-consensus" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -hex-literal = "0.1.0" -serde = { version = "1.0", default_features = false } -serde_derive = { version = "1.0", optional = true } -substrate-codec = { path = "../../codec", default_features = false } -substrate-codec-derive = { path = "../../codec/derive", default_features = false } -substrate-primitives = { path = "../../primitives", default_features = false } -substrate-runtime-std = { path = "../../runtime-std", default_features = false } -substrate-runtime-io = { path = "../../runtime-io", default_features = false } -substrate-runtime-support = { path = "../../runtime-support", default_features = false } -substrate-runtime-primitives = { path = "../primitives", default_features = false } -substrate-runtime-system = { path = "../system", default_features = false } - -[features] -default = ["std"] -std = [ - "serde/std", - "serde_derive", - "substrate-codec/std", - "substrate-codec-derive/std", - "substrate-primitives/std", - "substrate-runtime-std/std", - "substrate-runtime-io/std", - "substrate-runtime-support/std", - "substrate-runtime-primitives/std", - "substrate-runtime-system/std", -] diff --git a/substrate/runtime/consensus/src/lib.rs b/substrate/runtime/consensus/src/lib.rs deleted file mode 100644 index 07ff10d0714b6..0000000000000 --- a/substrate/runtime/consensus/src/lib.rs +++ /dev/null @@ -1,267 +0,0 @@ -// Copyright 2017 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 . - -//! Conensus module for runtime; manages the authority set ready for the native code. - -#![cfg_attr(not(feature = "std"), no_std)] - -#[allow(unused_imports)] -#[macro_use] -extern crate substrate_runtime_std as rstd; - -#[macro_use] -extern crate substrate_runtime_support as runtime_support; - -#[cfg(feature = "std")] -extern crate serde; - -#[cfg(feature = "std")] -#[macro_use] -extern crate serde_derive; - -#[macro_use] -extern crate substrate_codec_derive; - -extern crate substrate_runtime_io as runtime_io; -extern crate substrate_runtime_primitives as primitives; -extern crate substrate_codec as codec; -extern crate substrate_runtime_system as system; -extern crate substrate_primitives; - -use rstd::prelude::*; -use runtime_support::{storage, Parameter}; -use runtime_support::dispatch::Result; -use runtime_support::storage::StorageValue; -use runtime_support::storage::unhashed::StorageVec; -use primitives::traits::{MaybeSerializeDebug, OnFinalise, Member, DigestItem}; -use primitives::bft::MisbehaviorReport; -use system::{ensure_signed, ensure_inherent, ensure_root}; - -#[cfg(any(feature = "std", test))] -use substrate_primitives::Blake2Hasher; -#[cfg(any(feature = "std", test))] -use std::collections::HashMap; - -pub const AUTHORITY_AT: &'static [u8] = b":auth:"; -pub const AUTHORITY_COUNT: &'static [u8] = b":auth:len"; - -struct AuthorityStorageVec(rstd::marker::PhantomData); -impl StorageVec for AuthorityStorageVec { - type Item = S; - const PREFIX: &'static [u8] = AUTHORITY_AT; -} - -pub const CODE: &'static [u8] = b":code"; - -pub type KeyValue = (Vec, Vec); - -pub trait OnOfflineValidator { - fn on_offline_validator(validator_index: usize); -} - -impl OnOfflineValidator for () { - fn on_offline_validator(_validator_index: usize) {} -} - -pub type Log = RawLog< - ::SessionKey, ->; - -/// A logs in this module. -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] -#[derive(Encode, Decode, PartialEq, Eq, Clone)] -pub enum RawLog { - /// Authorities set has been changed. Contains the new set of authorities. - AuthoritiesChange(Vec), -} - -impl DigestItem for RawLog { - type AuthorityId = SessionKey; - - /// Try to cast the log entry as AuthoritiesChange log entry. - fn as_authorities_change(&self) -> Option<&[SessionKey]> { - match *self { - RawLog::AuthoritiesChange(ref item) => Some(&item), - } - } -} - -// Implementation for tests outside of this crate. -impl From> for u64 { - fn from(log: RawLog) -> u64 { - match log { - RawLog::AuthoritiesChange(_) => 1, - } - } -} - -pub trait Trait: system::Trait { - /// The allowed extrinsic position for `note_offline` inherent. - const NOTE_OFFLINE_POSITION: u32; - - /// Type for all log entries of this module. - type Log: From> + Into>; - - type SessionKey: Parameter + Default + MaybeSerializeDebug; - type OnOfflineValidator: OnOfflineValidator; -} - -decl_storage! { - trait Store for Module as Consensus { - // Authorities set actual at the block execution start. IsSome only if - // the set has been changed. - OriginalAuthorities: Vec; - } -} - -decl_module! { - pub struct Module for enum Call where origin: T::Origin { - fn report_misbehavior(origin, report: MisbehaviorReport) -> Result; - fn note_offline(origin, offline_val_indices: Vec) -> Result; - fn remark(origin, remark: Vec) -> Result; - fn set_code(origin, new: Vec) -> Result; - fn set_storage(origin, items: Vec) -> Result; - } -} - -impl Module { - /// Get the current set of authorities. These are the session keys. - pub fn authorities() -> Vec { - AuthorityStorageVec::::items() - } - - /// Set the new code. - fn set_code(origin: T::Origin, new: Vec) -> Result { - ensure_root(origin)?; - storage::unhashed::put_raw(CODE, &new); - Ok(()) - } - - /// Set some items of storage. - fn set_storage(origin: T::Origin, items: Vec) -> Result { - ensure_root(origin)?; - for i in &items { - storage::unhashed::put_raw(&i.0, &i.1); - } - Ok(()) - } - - /// Report some misbehaviour. - fn report_misbehavior(origin: T::Origin, _report: MisbehaviorReport) -> Result { - ensure_signed(origin)?; - // TODO. - Ok(()) - } - - /// Note the previous block's validator missed their opportunity to propose a block. This only comes in - /// if 2/3+1 of the validators agree that no proposal was submitted. It's only relevant - /// for the previous block. - fn note_offline(origin: T::Origin, offline_val_indices: Vec) -> Result { - ensure_inherent(origin)?; - assert!( - >::extrinsic_index() == Some(T::NOTE_OFFLINE_POSITION), - "note_offline extrinsic must be at position {} in the block", - T::NOTE_OFFLINE_POSITION - ); - - for validator_index in offline_val_indices.into_iter() { - T::OnOfflineValidator::on_offline_validator(validator_index as usize); - } - - Ok(()) - } - - /// Make some on-chain remark. - fn remark(origin: T::Origin, _remark: Vec) -> Result { - ensure_signed(origin)?; - Ok(()) - } - - /// Set the current set of authorities' session keys. - /// - /// Called by `next_session` only. - pub fn set_authorities(authorities: &[T::SessionKey]) { - let current_authorities = AuthorityStorageVec::::items(); - if current_authorities != authorities { - Self::save_original_authorities(Some(current_authorities)); - AuthorityStorageVec::::set_items(authorities); - } - } - - /// Set a single authority by index. - pub fn set_authority(index: u32, key: &T::SessionKey) { - let current_authority = AuthorityStorageVec::::item(index); - if current_authority != *key { - Self::save_original_authorities(None); - AuthorityStorageVec::::set_item(index, key); - } - } - - /// Save original authorities set. - fn save_original_authorities(current_authorities: Option>) { - if OriginalAuthorities::::get().is_some() { - // if we have already saved original set before, do not overwrite - return; - } - - >::put(current_authorities.unwrap_or_else(|| - AuthorityStorageVec::::items())); - } -} - -/// Finalization hook for the consensus module. -impl OnFinalise for Module { - fn on_finalise(_n: T::BlockNumber) { - if let Some(_) = >::take() { - // TODO: call Self::deposit_log - } - } -} - -#[cfg(any(feature = "std", test))] -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -#[serde(deny_unknown_fields)] -pub struct GenesisConfig { - pub authorities: Vec, - #[serde(with = "substrate_primitives::bytes")] - pub code: Vec, -} - -#[cfg(any(feature = "std", test))] -impl Default for GenesisConfig { - fn default() -> Self { - GenesisConfig { - authorities: vec![], - code: vec![], - } - } -} - -#[cfg(any(feature = "std", test))] -impl primitives::BuildStorage for GenesisConfig -{ - fn build_storage(self) -> ::std::result::Result, Vec>, String> { - use codec::{Encode, KeyedVec}; - let auth_count = self.authorities.len() as u32; - let mut r: runtime_io::TestExternalities = self.authorities.into_iter().enumerate().map(|(i, v)| - ((i as u32).to_keyed_vec(AUTHORITY_AT), v.encode()) - ).collect(); - r.insert(AUTHORITY_COUNT.to_vec(), auth_count.encode()); - r.insert(CODE.to_vec(), self.code); - Ok(r.into()) - } -} diff --git a/substrate/runtime/contract/Cargo.toml b/substrate/runtime/contract/Cargo.toml deleted file mode 100644 index a53cb5e886c6b..0000000000000 --- a/substrate/runtime/contract/Cargo.toml +++ /dev/null @@ -1,41 +0,0 @@ -[package] -name = "substrate-runtime-contract" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -serde = { version = "1.0", default_features = false } -serde_derive = { version = "1.0", optional = true } -substrate-codec = { path = "../../codec", default_features = false } -substrate-primitives = { path = "../../primitives", default_features = false } -substrate-runtime-primitives = { path = "../../runtime/primitives", default_features = false } -substrate-runtime-io = { path = "../../runtime-io", default_features = false } -substrate-runtime-std = { path = "../../runtime-std", default_features = false } -substrate-runtime-sandbox = { path = "../../runtime-sandbox", default_features = false } -substrate-runtime-support = { path = "../../runtime-support", default_features = false } -substrate-runtime-system = { path = "../../runtime/system", default_features = false } -substrate-runtime-balances = { path = "../balances", default_features = false } -parity-wasm = { version = "0.31", default_features = false } -pwasm-utils = { version = "0.3", default_features = false } - -[dev-dependencies] -wabt = "0.4" -assert_matches = "1.1" - -[features] -default = ["std"] -std = [ - "serde_derive", - "serde/std", - "substrate-codec/std", - "substrate-primitives/std", - "substrate-runtime-primitives/std", - "substrate-runtime-io/std", - "substrate-runtime-std/std", - "substrate-runtime-balances/std", - "substrate-runtime-sandbox/std", - "substrate-runtime-support/std", - "substrate-runtime-system/std", - "parity-wasm/std", - "pwasm-utils/std", -] diff --git a/substrate/runtime/contract/src/account_db.rs b/substrate/runtime/contract/src/account_db.rs deleted file mode 100644 index d7ff094003c97..0000000000000 --- a/substrate/runtime/contract/src/account_db.rs +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright 2018 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 . - -//! Auxilliaries to help with managing partial changes to accounts state. - -use super::{CodeOf, StorageOf, Trait}; -use double_map::StorageDoubleMap; -use rstd::cell::RefCell; -use rstd::collections::btree_map::{BTreeMap, Entry}; -use rstd::prelude::*; -use runtime_support::StorageMap; -use {balances, system}; - -pub struct ChangeEntry { - balance: Option, - code: Option>, - storage: BTreeMap, Option>>, -} - -// Cannot derive(Default) since it erroneously bounds T by Default. -impl Default for ChangeEntry { - fn default() -> Self { - ChangeEntry { - balance: Default::default(), - code: Default::default(), - storage: Default::default(), - } - } -} - -pub type ChangeSet = BTreeMap<::AccountId, ChangeEntry>; - -pub trait AccountDb { - fn get_storage(&self, account: &T::AccountId, location: &[u8]) -> Option>; - fn get_code(&self, account: &T::AccountId) -> Vec; - fn get_balance(&self, account: &T::AccountId) -> T::Balance; - - fn commit(&mut self, change_set: ChangeSet); -} - -pub struct DirectAccountDb; -impl AccountDb for DirectAccountDb { - fn get_storage(&self, account: &T::AccountId, location: &[u8]) -> Option> { - >::get(account.clone(), location.to_vec()) - } - fn get_code(&self, account: &T::AccountId) -> Vec { - >::get(account) - } - fn get_balance(&self, account: &T::AccountId) -> T::Balance { - balances::Module::::free_balance(account) - } - fn commit(&mut self, s: ChangeSet) { - for (address, changed) in s.into_iter() { - if let Some(balance) = changed.balance { - if let balances::UpdateBalanceOutcome::AccountKilled = - balances::Module::::set_free_balance_creating(&address, balance) - { - // Account killed. This will ultimately lead to calling `OnFreeBalanceZero` callback - // which will make removal of CodeOf and StorageOf for this account. - // In order to avoid writing over the deleted properties we `continue` here. - continue; - } - } - if let Some(code) = changed.code { - >::insert(&address, &code); - } - for (k, v) in changed.storage.into_iter() { - if let Some(value) = v { - >::insert(address.clone(), k, value); - } else { - >::remove(address.clone(), k); - } - } - } - } -} - -pub struct OverlayAccountDb<'a, T: Trait + 'a> { - local: RefCell>, - underlying: &'a AccountDb, -} -impl<'a, T: Trait> OverlayAccountDb<'a, T> { - pub fn new(underlying: &'a AccountDb) -> OverlayAccountDb<'a, T> { - OverlayAccountDb { - local: RefCell::new(ChangeSet::new()), - underlying, - } - } - - pub fn into_change_set(self) -> ChangeSet { - self.local.into_inner() - } - - pub fn set_storage( - &mut self, - account: &T::AccountId, - location: Vec, - value: Option>, - ) { - self.local - .borrow_mut() - .entry(account.clone()) - .or_insert(Default::default()) - .storage - .insert(location, value); - } - pub fn set_code(&mut self, account: &T::AccountId, code: Vec) { - self.local - .borrow_mut() - .entry(account.clone()) - .or_insert(Default::default()) - .code = Some(code); - } - pub fn set_balance(&mut self, account: &T::AccountId, balance: T::Balance) { - self.local - .borrow_mut() - .entry(account.clone()) - .or_insert(Default::default()) - .balance = Some(balance); - } -} - -impl<'a, T: Trait> AccountDb for OverlayAccountDb<'a, T> { - fn get_storage(&self, account: &T::AccountId, location: &[u8]) -> Option> { - self.local - .borrow() - .get(account) - .and_then(|a| a.storage.get(location)) - .cloned() - .unwrap_or_else(|| self.underlying.get_storage(account, location)) - } - fn get_code(&self, account: &T::AccountId) -> Vec { - self.local - .borrow() - .get(account) - .and_then(|a| a.code.clone()) - .unwrap_or_else(|| self.underlying.get_code(account)) - } - fn get_balance(&self, account: &T::AccountId) -> T::Balance { - self.local - .borrow() - .get(account) - .and_then(|a| a.balance) - .unwrap_or_else(|| self.underlying.get_balance(account)) - } - fn commit(&mut self, s: ChangeSet) { - let mut local = self.local.borrow_mut(); - - for (address, changed) in s.into_iter() { - match local.entry(address) { - Entry::Occupied(e) => { - let mut value = e.into_mut(); - if changed.balance.is_some() { - value.balance = changed.balance; - } - if changed.code.is_some() { - value.code = changed.code; - } - value.storage.extend(changed.storage.into_iter()); - } - Entry::Vacant(e) => { - e.insert(changed); - } - } - } - } -} diff --git a/substrate/runtime/contract/src/double_map.rs b/substrate/runtime/contract/src/double_map.rs deleted file mode 100644 index 6867d2a5c697c..0000000000000 --- a/substrate/runtime/contract/src/double_map.rs +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2017 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 implementation of double map backed by storage. -//! -//! This implementation is somewhat specialized to the tracking of the storage of accounts. - -use rstd::prelude::*; -use codec::{Codec, Encode}; -use runtime_support::storage::unhashed; -use runtime_io::{blake2_256, twox_128}; - -/// Returns only a first part of the storage key. -/// -/// Hashed by XX. -fn first_part_of_key(k1: M::Key1) -> [u8; 16] { - let mut raw_prefix = Vec::new(); - raw_prefix.extend(M::PREFIX); - raw_prefix.extend(Encode::encode(&k1)); - twox_128(&raw_prefix) -} - -/// Returns a compound key that consist of the two parts: (prefix, `k1`) and `k2`. -/// -/// The first part is hased by XX and then concatenated with a blake2 hash of `k2`. -fn full_key(k1: M::Key1, k2: M::Key2) -> Vec { - let first_part = first_part_of_key::(k1); - let second_part = blake2_256(&Encode::encode(&k2)); - - let mut k = Vec::new(); - k.extend(&first_part); - k.extend(&second_part); - k -} - -/// An implementation of a map with a two keys. -/// -/// It provides an important ability to efficiently remove all entries -/// that have a common first key. -/// -/// # Mapping of keys to a storage path -/// -/// The storage key (i.e. the key under which the `Value` will be stored) is created from two parts. -/// The first part is a XX hash of a concatenation of the `PREFIX` and `Key1`. And the second part -/// is a blake2 hash of a `Key2`. -/// -/// Blake2 is used for `Key2` is because it will be used as a key for contract's storage and -/// thus will be susceptible for a untrusted input. -pub trait StorageDoubleMap { - type Key1: Codec; - type Key2: Codec; - type Value: Codec + Default; - - const PREFIX: &'static [u8]; - - /// Insert an entry into this map. - fn insert(k1: Self::Key1, k2: Self::Key2, val: Self::Value) { - unhashed::put(&full_key::(k1, k2)[..], &val); - } - - /// Remove an entry from this map. - fn remove(k1: Self::Key1, k2: Self::Key2) { - unhashed::kill(&full_key::(k1, k2)[..]); - } - - /// Get an entry from this map. - /// - /// If there is entry stored under the given keys, returns `None`. - fn get(k1: Self::Key1, k2: Self::Key2) -> Option { - unhashed::get(&full_key::(k1, k2)[..]) - } - - /// Removes all entries that shares the `k1` as the first key. - fn remove_prefix(k1: Self::Key1) { - unhashed::kill_prefix(&first_part_of_key::(k1)) - } -} diff --git a/substrate/runtime/contract/src/exec.rs b/substrate/runtime/contract/src/exec.rs deleted file mode 100644 index ba3e0ff9676ec..0000000000000 --- a/substrate/runtime/contract/src/exec.rs +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -use super::{CodeOf, MaxDepth, ContractAddressFor, Module, Trait}; -use account_db::{AccountDb, OverlayAccountDb}; -use gas::GasMeter; -use vm; - -use rstd::prelude::*; -use runtime_primitives::traits::{Zero, CheckedAdd, CheckedSub}; -use runtime_support::{StorageMap, StorageValue}; -use balances::{self, EnsureAccountLiquid}; - -pub struct CreateReceipt { - pub address: T::AccountId, -} - -pub struct CallReceipt { - pub return_data: Vec, -} - -pub struct ExecutionContext<'a, T: Trait + 'a> { - // typically should be dest - pub self_account: T::AccountId, - pub overlay: OverlayAccountDb<'a, T>, - pub depth: usize, -} - -impl<'a, T: Trait> ExecutionContext<'a, T> { - /// Make a call to the specified address. - pub fn call( - &mut self, - caller: T::AccountId, - dest: T::AccountId, - value: T::Balance, - gas_meter: &mut GasMeter, - data: &[u8], - ) -> Result { - if self.depth == >::get() as usize { - return Err("reached maximum depth, cannot make a call"); - } - - let call_base_fee = >::call_base_fee(); - if gas_meter.charge(call_base_fee).is_out_of_gas() { - return Err("not enough gas to pay base call fee"); - } - - let dest_code = >::get(&dest); - - let (exec_result, change_set) = { - let mut overlay = OverlayAccountDb::new(&self.overlay); - - if value > T::Balance::zero() { - transfer( - gas_meter, - false, - &self.self_account, - &dest, - value, - &mut overlay, - )?; - } - - let mut nested = ExecutionContext { - overlay: overlay, - self_account: dest.clone(), - depth: self.depth + 1, - }; - let exec_result = if !dest_code.is_empty() { - vm::execute( - &dest_code, - data, - &mut CallContext { - ctx: &mut nested, - _caller: caller, - }, - gas_meter, - ).map_err(|_| "vm execute returned error while call")? - } else { - // that was a plain transfer - vm::ExecutionResult { - return_data: Vec::new(), - } - }; - - (exec_result, nested.overlay.into_change_set()) - }; - - self.overlay.commit(change_set); - - Ok(CallReceipt { - return_data: exec_result.return_data, - }) - } - - pub fn create( - &mut self, - caller: T::AccountId, - endowment: T::Balance, - gas_meter: &mut GasMeter, - ctor: &[u8], - data: &[u8], - ) -> Result, &'static str> { - if self.depth == >::get() as usize { - return Err("reached maximum depth, cannot create"); - } - - let create_base_fee = >::create_base_fee(); - if gas_meter.charge(create_base_fee).is_out_of_gas() { - return Err("not enough gas to pay base create fee"); - } - - let dest = T::DetermineContractAddress::contract_address_for(ctor, data, &self.self_account); - if >::exists(&dest) { - // TODO: Is it enough? - return Err("contract already exists"); - } - - let change_set = { - let mut overlay = OverlayAccountDb::new(&self.overlay); - - if endowment > T::Balance::zero() { - transfer( - gas_meter, - true, - &self.self_account, - &dest, - endowment, - &mut overlay, - )?; - } - - let mut nested = ExecutionContext { - overlay: overlay, - self_account: dest.clone(), - depth: self.depth + 1, - }; - let exec_result = { - vm::execute( - ctor, - data, - &mut CallContext { - ctx: &mut nested, - _caller: caller, - }, - gas_meter, - ).map_err(|_| "vm execute returned error while create")? - }; - - nested.overlay.set_code(&dest, exec_result.return_data); - nested.overlay.into_change_set() - }; - - self.overlay.commit(change_set); - - Ok(CreateReceipt { - address: dest, - }) - } -} - -fn transfer( - gas_meter: &mut GasMeter, - contract_create: bool, - transactor: &T::AccountId, - dest: &T::AccountId, - value: T::Balance, - overlay: &mut OverlayAccountDb, -) -> Result<(), &'static str> { - let would_create = overlay.get_balance(transactor).is_zero(); - - let fee: T::Balance = if contract_create { - >::contract_fee() - } else { - if would_create { - >::creation_fee() - } else { - >::transfer_fee() - } - }; - - if gas_meter.charge_by_balance(fee).is_out_of_gas() { - return Err("not enough gas to pay transfer fee"); - } - - let from_balance = overlay.get_balance(transactor); - let new_from_balance = match from_balance.checked_sub(&value) { - Some(b) => b, - None => return Err("balance too low to send value"), - }; - if would_create && value < >::existential_deposit() { - return Err("value too low to create account"); - } - ::EnsureAccountLiquid::ensure_account_liquid(transactor)?; - - let to_balance = overlay.get_balance(dest); - let new_to_balance = match to_balance.checked_add(&value) { - Some(b) => b, - None => return Err("destination balance too high to receive value"), - }; - - if transactor != dest { - overlay.set_balance(transactor, new_from_balance); - overlay.set_balance(dest, new_to_balance); - } - - Ok(()) -} - -struct CallContext<'a, 'b: 'a, T: Trait + 'b> { - ctx: &'a mut ExecutionContext<'b, T>, - _caller: T::AccountId, -} - -impl<'a, 'b: 'a, T: Trait + 'b> vm::Ext for CallContext<'a, 'b, T> { - type T = T; - - fn get_storage(&self, key: &[u8]) -> Option> { - self.ctx.overlay.get_storage(&self.ctx.self_account, key) - } - - fn set_storage(&mut self, key: &[u8], value: Option>) { - self.ctx - .overlay - .set_storage(&self.ctx.self_account, key.to_vec(), value) - } - - fn create( - &mut self, - code: &[u8], - endowment: T::Balance, - gas_meter: &mut GasMeter, - data: &[u8], - ) -> Result, ()> { - let caller = self.ctx.self_account.clone(); - self.ctx - .create(caller, endowment, gas_meter, code, &data) - .map_err(|_| ()) - } - - fn call( - &mut self, - to: &T::AccountId, - value: T::Balance, - gas_meter: &mut GasMeter, - data: &[u8], - ) -> Result { - let caller = self.ctx.self_account.clone(); - self.ctx - .call(caller, to.clone(), value, gas_meter, data) - .map_err(|_| ()) - } -} diff --git a/substrate/runtime/contract/src/gas.rs b/substrate/runtime/contract/src/gas.rs deleted file mode 100644 index 9d1978f7a527c..0000000000000 --- a/substrate/runtime/contract/src/gas.rs +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright 2018 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 {Trait, Module, GasSpent}; -use runtime_primitives::traits::{As, CheckedMul, CheckedSub, Zero}; -use runtime_support::StorageValue; -use balances; - -#[must_use] -#[derive(Debug, PartialEq, Eq)] -pub enum GasMeterResult { - Proceed, - OutOfGas, -} - -impl GasMeterResult { - pub fn is_out_of_gas(&self) -> bool { - match *self { - GasMeterResult::OutOfGas => true, - GasMeterResult::Proceed => false, - } - } -} - -pub struct GasMeter { - limit: T::Gas, - /// Amount of gas left from initial gas limit. Can reach zero. - gas_left: T::Gas, - gas_price: T::Balance, -} -impl GasMeter { - #[cfg(test)] - pub fn with_limit(gas_limit: T::Gas, gas_price: T::Balance) -> GasMeter { - GasMeter { - limit: gas_limit, - gas_left: gas_limit, - gas_price, - } - } - - /// Account for used gas. - /// - /// Returns `OutOfGas` if there is not enough gas or addition of the specified - /// amount of gas has lead to overflow. On success returns `Proceed`. - /// - /// NOTE that `amount` is always consumed, i.e. if there is not enough gas - /// then the counter will be set to zero. - pub fn charge(&mut self, amount: T::Gas) -> GasMeterResult { - let new_value = match self.gas_left.checked_sub(&amount) { - None => None, - Some(val) if val.is_zero() => None, - Some(val) => Some(val), - }; - - // We always consume the gas even if there is not enough gas. - self.gas_left = new_value.unwrap_or_else(Zero::zero); - - match new_value { - Some(_) => GasMeterResult::Proceed, - None => GasMeterResult::OutOfGas, - } - } - - /// Account for used gas expressed in balance units. - /// - /// Same as [`charge`], but amount to be charged is converted from units of balance to - /// units of gas. - /// - /// [`charge`]: #method.charge - pub fn charge_by_balance(&mut self, amount: T::Balance) -> GasMeterResult { - let amount_in_gas: T::Balance = amount / self.gas_price; - let amount_in_gas: T::Gas = >::sa(amount_in_gas); - self.charge(amount_in_gas) - } - - /// Allocate some amount of gas and perform some work with - /// a newly created nested gas meter. - /// - /// Invokes `f` with either the gas meter that has `amount` gas left or - /// with `None`, if this gas meter has not enough gas to allocate given `amount`. - /// - /// All unused gas in the nested gas meter is returned to this gas meter. - pub fn with_nested>) -> R>( - &mut self, - amount: T::Gas, - f: F, - ) -> R { - // NOTE that it is ok to allocate all available gas since it still ensured - // by `charge` that it doesn't reach zero. - if self.gas_left < amount { - f(None) - } else { - self.gas_left = self.gas_left - amount; - let mut nested = GasMeter { - limit: amount, - gas_left: amount, - gas_price: self.gas_price, - }; - - let r = f(Some(&mut nested)); - - self.gas_left = self.gas_left + nested.gas_left; - - r - } - } - - /// Returns how much gas left from the initial budget. - pub fn gas_left(&self) -> T::Gas { - self.gas_left - } - - /// Returns how much gas was spent. - fn spent(&self) -> T::Gas { - self.limit - self.gas_left - } -} - -/// Buy the given amount of gas. -/// -/// Cost is calculated by multiplying the gas cost (taken from the storage) by the `gas_limit`. -/// The funds are deducted from `transactor`. -pub fn buy_gas( - transactor: &T::AccountId, - gas_limit: T::Gas, -) -> Result, &'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(); - if gas_limit > gas_available { - return Err("block gas limit is reached"); - } - - // Buy the specified amount of gas. - let gas_price = >::gas_price(); - let b = >::free_balance(transactor); - let cost = >::as_(gas_limit.clone()) - .checked_mul(&gas_price) - .ok_or("overflow multiplying gas limit by price")?; - if b < cost + >::existential_deposit() { - return Err("not enough funds for transaction fee"); - } - >::set_free_balance(transactor, b - cost); - >::decrease_total_stake_by(cost); - Ok(GasMeter { - limit: gas_limit, - gas_left: gas_limit, - gas_price, - }) -} - -/// Refund the unused gas. -pub fn refund_unused_gas(transactor: &T::AccountId, gas_meter: GasMeter) { - // Increase total spent gas. - // This cannot overflow, since `gas_spent` is never greater than `block_gas_limit`, which - // also has T::Gas type. - let gas_spent = >::gas_spent() + gas_meter.spent(); - >::put(gas_spent); - - // Refund gas left by the price it was bought. - let b = >::free_balance(transactor); - let refund = >::as_(gas_meter.gas_left) * gas_meter.gas_price; - >::set_free_balance(transactor, b + refund); - >::increase_total_stake_by(refund); -} diff --git a/substrate/runtime/contract/src/genesis_config.rs b/substrate/runtime/contract/src/genesis_config.rs deleted file mode 100644 index 2d9a50573a672..0000000000000 --- a/substrate/runtime/contract/src/genesis_config.rs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2018 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 . - -//! Build the contract module part of the genesis block storage. - -#![cfg(feature = "std")] - -use {Trait, ContractFee, CallBaseFee, CreateBaseFee, GasPrice, MaxDepth, BlockGasLimit}; - -use runtime_primitives; -use runtime_io::{self, twox_128}; -use runtime_support::StorageValue; -use codec::Encode; -use std::collections::HashMap; -use substrate_primitives::Blake2Hasher; - -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -#[serde(deny_unknown_fields)] -pub struct GenesisConfig { - pub contract_fee: T::Balance, - pub call_base_fee: T::Gas, - pub create_base_fee: T::Gas, - pub gas_price: T::Balance, - pub max_depth: u32, - pub block_gas_limit: T::Gas, -} - -impl runtime_primitives::BuildStorage for GenesisConfig { - fn build_storage(self) -> ::std::result::Result, Vec>, String> { - let r: runtime_io::TestExternalities = map![ - twox_128(>::key()).to_vec() => self.contract_fee.encode(), - twox_128(>::key()).to_vec() => self.call_base_fee.encode(), - twox_128(>::key()).to_vec() => self.create_base_fee.encode(), - twox_128(>::key()).to_vec() => self.gas_price.encode(), - twox_128(>::key()).to_vec() => self.max_depth.encode(), - twox_128(>::key()).to_vec() => self.block_gas_limit.encode() - ]; - Ok(r.into()) - } -} diff --git a/substrate/runtime/contract/src/lib.rs b/substrate/runtime/contract/src/lib.rs deleted file mode 100644 index 8114d2651607e..0000000000000 --- a/substrate/runtime/contract/src/lib.rs +++ /dev/null @@ -1,278 +0,0 @@ -// Copyright 2018 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 . - -//! Smart-contract module for runtime; Allows deployment and execution of smart-contracts -//! expressed in WebAssembly. -//! -//! This module provides an ability to create smart-contract accounts and send them messages. -//! A smart-contract is an account with associated code and storage. When such an account receives a message, -//! the code associated with that account gets executed. -//! -//! The code is allowed to alter the storage entries of the associated account, -//! create smart-contracts or send messages to existing smart-contracts. -//! -//! For any actions invoked by the smart-contracts fee must be paid. The fee is paid in gas. -//! Gas is bought upfront up to the, specified in transaction, limit. Any unused gas is refunded -//! after the transaction (regardless of the execution outcome). If all gas is used, -//! then changes made for the specific call or create are reverted (including balance transfers). -//! -//! Failures are typically not cascading. That, for example, means that if contract A calls B and B errors -//! somehow, then A can decide if it should proceed or error. -//! -//! # Interaction with the system -//! -//! ## Finalization -//! -//! This module requires performing some finalization steps at the end of the block. If not performed -//! the module will have incorrect behavior. -//! -//! Call [`Module::execute`] at the end of the block. The order in relation to -//! the other module doesn't matter. -//! -//! ## Account killing -//! -//! When `staking` module determines that account is dead (e.g. account's balance fell below -//! exsistential deposit) then it reaps the account. That will lead to deletion of the associated -//! code and storage of the account. -//! -//! [`Module::execute`]: struct.Module.html#impl-OnFinalise - -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(feature = "std")] -#[macro_use] -extern crate serde_derive; - -#[cfg(feature = "std")] -extern crate serde; - -extern crate parity_wasm; -extern crate pwasm_utils; - -extern crate substrate_codec as codec; -extern crate substrate_runtime_io as runtime_io; -extern crate substrate_runtime_sandbox as sandbox; - -#[macro_use] -extern crate substrate_runtime_std as rstd; - -extern crate substrate_runtime_balances as balances; -extern crate substrate_runtime_system as system; - -#[macro_use] -extern crate substrate_runtime_support as runtime_support; - -extern crate substrate_runtime_primitives as runtime_primitives; -extern crate substrate_primitives; - -#[cfg(test)] -#[macro_use] -extern crate assert_matches; - -#[cfg(test)] -extern crate wabt; - -mod account_db; -mod double_map; -mod exec; -mod vm; -mod gas; - -mod genesis_config; - -#[cfg(test)] -mod tests; - -#[cfg(feature = "std")] -pub use genesis_config::GenesisConfig; -use exec::ExecutionContext; -use account_db::{AccountDb, OverlayAccountDb}; -use double_map::StorageDoubleMap; - -use rstd::prelude::*; -use codec::Codec; -use runtime_primitives::traits::{As, SimpleArithmetic, OnFinalise}; -use runtime_support::dispatch::Result; -use runtime_support::{Parameter, StorageMap, StorageValue}; -use system::ensure_signed; - -pub trait Trait: balances::Trait { - /// Function type to get the contract address given the creator. - type DetermineContractAddress: ContractAddressFor; - - // As is needed for wasm-utils - type Gas: Parameter + Default + Codec + SimpleArithmetic + Copy + As + As + As; -} - -pub trait ContractAddressFor { - fn contract_address_for(code: &[u8], data: &[u8], origin: &AccountId) -> AccountId; -} - -decl_module! { - /// Contracts module. - pub struct Module for enum Call where origin: T::Origin { - // TODO: Change AccountId to staking::Address - fn call( - origin, - dest: T::AccountId, - value: T::Balance, - gas_limit: T::Gas, - data: Vec - ) -> Result; - - fn create( - origin, - value: T::Balance, - gas_limit: T::Gas, - ctor: Vec, - data: Vec - ) -> Result; - } -} - -decl_storage! { - trait Store for Module as Contract { - /// The fee required to create a contract. At least as big as staking's ReclaimRebate. - ContractFee get(contract_fee): required T::Balance; - /// The fee charged for a call into a contract. - CallBaseFee get(call_base_fee): required T::Gas; - /// The fee charged for a create of a contract. - CreateBaseFee get(create_base_fee): required T::Gas; - /// The price of one unit of gas. - GasPrice get(gas_price): required T::Balance; - /// The maximum nesting level of a call/create stack. - MaxDepth get(max_depth): required u32; - /// The maximum amount of gas that could be expended per block. - BlockGasLimit get(block_gas_limit): required T::Gas; - /// Gas spent so far in this block. - GasSpent get(gas_spent): default T::Gas; - - /// The code associated with an account. - pub CodeOf: default map [ T::AccountId => Vec ]; // TODO Vec values should be optimised to not do a length prefix. - } -} - -// TODO: consider storing upper-bound for contract's gas limit in fixed-length runtime -// code in contract itself and use that. - -/// The storage items associated with an account/key. -/// -/// TODO: keys should also be able to take AsRef to ensure Vecs can be passed as &[u8] -pub(crate) struct StorageOf(::rstd::marker::PhantomData); -impl double_map::StorageDoubleMap for StorageOf { - const PREFIX: &'static [u8] = b"con:sto:"; - type Key1 = T::AccountId; - type Key2 = Vec; - type Value = Vec; -} - -impl Module { - /// Make a call to a specified account, optionally transferring some balance. - fn call( - origin: ::Origin, - dest: T::AccountId, - value: T::Balance, - gas_limit: T::Gas, - data: Vec, - ) -> Result { - let origin = ensure_signed(origin)?; - - // Pay for the gas upfront. - // - // NOTE: it is very important to avoid any state changes before - // paying for the gas. - let mut gas_meter = gas::buy_gas::(&origin, gas_limit)?; - - let mut ctx = ExecutionContext { - self_account: origin.clone(), - depth: 0, - overlay: OverlayAccountDb::::new(&account_db::DirectAccountDb), - }; - let result = ctx.call(origin.clone(), dest, value, &mut gas_meter, &data); - - if let Ok(_) = result { - // Commit all changes that made it thus far into the persistant storage. - account_db::DirectAccountDb.commit(ctx.overlay.into_change_set()); - } - - // Refund cost of the unused gas. - // - // NOTE: this should go after the commit to the storage, since the storage changes - // can alter the balance of the caller. - gas::refund_unused_gas::(&origin, gas_meter); - - result.map(|_| ()) - } - - /// Create a new contract, optionally transfering some balance to the created account. - /// - /// Creation is executed as follows:ExecutionContext - /// - /// - the destination address is computed based on the sender and hash of the code. - /// - account is created at the computed address. - /// - the `ctor_code` is executed in the context of the newly created account. Buffer returned - /// after the execution is saved as the `code` of the account. That code will be invoked - /// upon any message received by this account. - fn create( - origin: ::Origin, - endowment: T::Balance, - gas_limit: T::Gas, - ctor_code: Vec, - data: Vec, - ) -> Result { - let origin = ensure_signed(origin)?; - - // Pay for the gas upfront. - // - // NOTE: it is very important to avoid any state changes before - // paying for the gas. - let mut gas_meter = gas::buy_gas::(&origin, gas_limit)?; - - let mut ctx = ExecutionContext { - self_account: origin.clone(), - depth: 0, - overlay: OverlayAccountDb::::new(&account_db::DirectAccountDb), - }; - let result = ctx.create(origin.clone(), endowment, &mut gas_meter, &ctor_code, &data); - - if let Ok(_) = result { - // Commit all changes that made it thus far into the persistant storage. - account_db::DirectAccountDb.commit(ctx.overlay.into_change_set()); - } - - // Refund cost of the unused gas. - // - // NOTE: this should go after the commit to the storage, since the storage changes - // can alter the balance of the caller. - gas::refund_unused_gas::(&origin, gas_meter); - - result.map(|_| ()) - } -} - -impl balances::OnFreeBalanceZero for Module { - fn on_free_balance_zero(who: &T::AccountId) { - >::remove(who); - >::remove_prefix(who.clone()); - } -} - -/// Finalization hook for the smart-contract module. -impl OnFinalise for Module { - fn on_finalise(_n: T::BlockNumber) { - >::kill(); - } -} diff --git a/substrate/runtime/contract/src/tests.rs b/substrate/runtime/contract/src/tests.rs deleted file mode 100644 index 17f5a4d88aac9..0000000000000 --- a/substrate/runtime/contract/src/tests.rs +++ /dev/null @@ -1,670 +0,0 @@ -// Copyright 2018 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 double_map::StorageDoubleMap; -use runtime_io::with_externalities; -use runtime_primitives::testing::{Digest, H256, Header}; -use runtime_primitives::traits::{BlakeTwo256}; -use runtime_primitives::BuildStorage; -use runtime_support::StorageMap; -use substrate_primitives::Blake2Hasher; -use wabt; -use { - runtime_io, balances, system, CodeOf, ContractAddressFor, - GenesisConfig, Module, StorageOf, Trait, -}; - -impl_outer_origin! { - pub enum Origin for Test {} -} - -#[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 Digest = Digest; - type AccountId = u64; - type Header = Header; - type Event = (); -} -impl balances::Trait for Test { - type Balance = u64; - type AccountIndex = u64; - type OnFreeBalanceZero = Contract; - type EnsureAccountLiquid = (); - type Event = (); -} -impl Trait for Test { - type Gas = u64; - type DetermineContractAddress = DummyContractAddressFor; -} - -type Balances = balances::Module; -type Contract = Module; - -pub struct DummyContractAddressFor; -impl ContractAddressFor for DummyContractAddressFor { - fn contract_address_for(_code: &[u8], _data: &[u8], origin: &u64) -> u64 { - origin + 1 - } -} - -struct ExtBuilder { - existential_deposit: u64, - gas_price: u64, - block_gas_limit: u64, -} -impl Default for ExtBuilder { - fn default() -> Self { - Self { - existential_deposit: 0, - gas_price: 2, - block_gas_limit: 100_000_000, - } - } -} -impl ExtBuilder { - fn existential_deposit(mut self, existential_deposit: u64) -> Self { - self.existential_deposit = existential_deposit; - self - } - fn gas_price(mut self, gas_price: u64) -> Self { - self.gas_price = gas_price; - self - } - fn block_gas_limit(mut self, block_gas_limit: u64) -> Self { - self.block_gas_limit = block_gas_limit; - self - } - fn build(self) -> runtime_io::TestExternalities { - let mut t = system::GenesisConfig::::default() - .build_storage() - .unwrap(); - t.extend( - balances::GenesisConfig:: { - balances: vec![], - transaction_base_fee: 0, - transaction_byte_fee: 0, - existential_deposit: self.existential_deposit, - transfer_fee: 0, - creation_fee: 0, - reclaim_rebate: 0, - }.build_storage() - .unwrap(), - ); - t.extend( - GenesisConfig:: { - 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, - }.build_storage() - .unwrap(), - ); - t.into() - } -} - -const CODE_TRANSFER: &str = r#" -(module - ;; ext_call( - ;; callee_ptr: u32, - ;; callee_len: u32, - ;; gas: u64, - ;; value_ptr: u32, - ;; value_len: u32, - ;; input_data_ptr: u32, - ;; input_data_len: u32 - ;; ) -> u32 - (import "env" "ext_call" (func $ext_call (param i32 i32 i64 i32 i32 i32 i32) (result i32))) - (import "env" "memory" (memory 1 1)) - (func (export "call") - (drop - (call $ext_call - (i32.const 4) ;; Pointer to "callee" address. - (i32.const 8) ;; Length of "callee" address. - (i64.const 0) ;; How much gas to devote for the execution. 0 = all. - (i32.const 12) ;; Pointer to the buffer with value to transfer - (i32.const 8) ;; Length of the buffer with value to transfer. - (i32.const 0) ;; Pointer to input data buffer address - (i32.const 0) ;; Length of input data buffer - ) - ) - ) - ;; Destination AccountId to transfer the funds. - ;; Represented by u64 (8 bytes long) in little endian. - (data (i32.const 4) "\09\00\00\00\00\00\00\00") - ;; Amount of value to transfer. - ;; Represented by u64 (8 bytes long) in little endian. - (data (i32.const 12) "\06\00\00\00\00\00\00\00") -) -"#; - -#[test] -fn contract_transfer() { - const CONTRACT_SHOULD_TRANSFER_VALUE: u64 = 6; - const CONTRACT_SHOULD_TRANSFER_TO: u64 = 9; - - let code_transfer = wabt::wat2wasm(CODE_TRANSFER).unwrap(); - - with_externalities(&mut ExtBuilder::default().build(), || { - >::insert(1, code_transfer.to_vec()); - - Balances::set_free_balance(&0, 100_000_000); - Balances::increase_total_stake_by(100_000_000); - Balances::set_free_balance(&1, 11); - Balances::increase_total_stake_by(11); - - assert_ok!(Contract::call(Origin::signed(0), 1, 3, 100_000, Vec::new())); - - assert_eq!( - Balances::free_balance(&0), - // 3 - value sent with the transaction - // 2 * 10 - gas used by the contract (10) multiplied by gas price (2) - // 2 * 135 - base gas fee for call (by transaction) - // 2 * 135 - base gas fee for call (by the contract) - 100_000_000 - 3 - (2 * 10) - (2 * 135) - (2 * 135), - ); - assert_eq!( - Balances::free_balance(&1), - 11 + 3 - CONTRACT_SHOULD_TRANSFER_VALUE, - ); - assert_eq!( - Balances::free_balance(&CONTRACT_SHOULD_TRANSFER_TO), - CONTRACT_SHOULD_TRANSFER_VALUE, - ); - }); -} - -#[test] -fn contract_transfer_oog() { - const CONTRACT_SHOULD_TRANSFER_TO: u64 = 9; - - let code_transfer = wabt::wat2wasm(CODE_TRANSFER).unwrap(); - - with_externalities(&mut ExtBuilder::default().build(), || { - >::insert(1, code_transfer.to_vec()); - - Balances::set_free_balance(&0, 100_000_000); - Balances::increase_total_stake_by(100_000_000); - Balances::set_free_balance(&1, 11); - Balances::increase_total_stake_by(11); - - assert_ok!(Contract::call(Origin::signed(0), 1, 3, 135 + 135 + 7, Vec::new())); - - assert_eq!( - Balances::free_balance(&0), - // 3 - value sent with the transaction - // 2 * 7 - gas used by the contract (7) multiplied by gas price (2) - // 2 * 135 - base gas fee for call (by transaction) - // 2 * 135 - base gas fee for call (by contract) - 100_000_000 - 3 - (2 * 7) - (2 * 135) - (2 * 135), - ); - - // Transaction level transfer should succeed. - assert_eq!(Balances::free_balance(&1), 14); - // But `ext_call` should not. - assert_eq!(Balances::free_balance(&CONTRACT_SHOULD_TRANSFER_TO), 0); - }); -} - -#[test] -fn contract_transfer_max_depth() { - const CONTRACT_SHOULD_TRANSFER_TO: u64 = 9; - - let code_transfer = wabt::wat2wasm(CODE_TRANSFER).unwrap(); - - with_externalities(&mut ExtBuilder::default().build(), || { - >::insert(CONTRACT_SHOULD_TRANSFER_TO, code_transfer.to_vec()); - - Balances::set_free_balance(&0, 100_000_000); - Balances::increase_total_stake_by(100_000_000); - Balances::set_free_balance(&CONTRACT_SHOULD_TRANSFER_TO, 11); - Balances::increase_total_stake_by(11); - - assert_ok!(Contract::call(Origin::signed(0), CONTRACT_SHOULD_TRANSFER_TO, 3, 100_000, Vec::new())); - - assert_eq!( - Balances::free_balance(&0), - // 3 - value sent with the transaction - // 2 * 10 * 100 - gas used by the contract (10) multiplied by gas price (2) - // multiplied by max depth (100). - // 2 * 135 * 100 - base gas fee for call (by transaction) multiplied by max depth (100). - 100_000_000 - 3 - (2 * 10 * 100) - (2 * 135 * 100), - ); - assert_eq!(Balances::free_balance(&CONTRACT_SHOULD_TRANSFER_TO), 14); - }); -} - -/// Convert a byte slice to a string with hex values. -/// -/// Each value is preceeded with a `\` character. -fn escaped_bytestring(bytes: &[u8]) -> String { - use std::fmt::Write; - let mut result = String::new(); - for b in bytes { - write!(result, "\\{:02x}", b).unwrap(); - } - result -} - -/// Create a constructor for the specified code. -/// -/// When constructor is executed, it will call `ext_return` with code that -/// specified in `child_bytecode`. -fn code_ctor(child_bytecode: &[u8]) -> String { - format!( - r#" -(module - ;; ext_return(data_ptr: u32, data_len: u32) -> ! - (import "env" "ext_return" (func $ext_return (param i32 i32))) - (import "env" "memory" (memory 1 1)) - (func (export "call") - (call $ext_return - (i32.const 4) - (i32.const {code_len}) - ) - ;; ext_return is diverging, i.e. doesn't return. - unreachable - ) - (data (i32.const 4) "{escaped_bytecode}") -) -"#, - escaped_bytecode = escaped_bytestring(child_bytecode), - code_len = child_bytecode.len(), - ) -} - -/// Returns code that uses `ext_create` runtime call. -/// -/// Takes bytecode of the contract that needs to be deployed. -fn code_create(constructor: &[u8]) -> String { - format!( - r#" -(module - ;; ext_create( - ;; code_ptr: u32, - ;; code_len: u32, - ;; gas: u64, - ;; value_ptr: u32, - ;; value_len: u32, - ;; input_data_ptr: u32, - ;; input_data_len: u32, - ;; ) -> u32 - (import "env" "ext_create" (func $ext_create (param i32 i32 i64 i32 i32 i32 i32) (result i32))) - (import "env" "memory" (memory 1 1)) - (func (export "call") - (drop - (call $ext_create - (i32.const 12) ;; Pointer to `code` - (i32.const {code_len}) ;; Length of `code` - (i64.const 0) ;; How much gas to devote for the execution. 0 = all. - (i32.const 4) ;; Pointer to the buffer with value to transfer - (i32.const 8) ;; Length of the buffer with value to transfer - (i32.const 0) ;; Pointer to input data buffer address - (i32.const 0) ;; Length of input data buffer - ) - ) - ) - ;; Amount of value to transfer. - ;; Represented by u64 (8 bytes long) in little endian. - (data (i32.const 4) "\03\00\00\00\00\00\00\00") - ;; Embedded wasm code. - (data (i32.const 12) "{escaped_constructor}") -) -"#, - escaped_constructor = escaped_bytestring(constructor), - code_len = constructor.len(), - ) -} - -#[test] -fn contract_create() { - let code_transfer = wabt::wat2wasm(CODE_TRANSFER).unwrap(); - let code_ctor_transfer = wabt::wat2wasm(&code_ctor(&code_transfer)).unwrap(); - let code_create = wabt::wat2wasm(&code_create(&code_ctor_transfer)).unwrap(); - - with_externalities(&mut ExtBuilder::default().build(), || { - Balances::set_free_balance(&0, 100_000_000); - Balances::increase_total_stake_by(100_000_000); - Balances::set_free_balance(&1, 0); - Balances::set_free_balance(&9, 30); - Balances::increase_total_stake_by(30); - - >::insert(1, code_create.to_vec()); - - // When invoked, the contract at address `1` must create a contract with 'transfer' code. - assert_ok!(Contract::call(Origin::signed(0), 1, 11, 100_000, Vec::new())); - - let derived_address = ::DetermineContractAddress::contract_address_for( - &code_ctor_transfer, - &[], - &1, - ); - - // 11 - value sent with the transaction - // 2 * 139 - gas spent by the deployer contract (139) multiplied by gas price (2) - // 2 * 135 - base gas fee for call (top level) - // 2 * 175 - base gas fee for create (by contract) - // ((21 / 2) * 2) - price per account creation - let expected_gas_after_create = - 100_000_000 - 11 - (2 * 139) - (2 * 135) - (2 * 175) - ((21 / 2) * 2); - assert_eq!(Balances::free_balance(&0), expected_gas_after_create); - assert_eq!(Balances::free_balance(&1), 8); - assert_eq!(Balances::free_balance(&derived_address), 3); - - // Initiate transfer to the newly created contract. - assert_ok!(Contract::call(Origin::signed(0), derived_address, 22, 100_000, Vec::new())); - - assert_eq!( - Balances::free_balance(&0), - // 22 - value sent with the transaction - // (2 * 10) - gas used by the contract - // (2 * 135) - base gas fee for call (top level) - // (2 * 135) - base gas fee for call (by transfer contract) - expected_gas_after_create - 22 - (2 * 10) - (2 * 135) - (2 * 135), - ); - assert_eq!(Balances::free_balance(&derived_address), 22 - 3); - assert_eq!(Balances::free_balance(&9), 36); - }); -} - -#[test] -fn top_level_create() { - let code_transfer = wabt::wat2wasm(CODE_TRANSFER).unwrap(); - let code_ctor_transfer = wabt::wat2wasm(&code_ctor(&code_transfer)).unwrap(); - - with_externalities(&mut ExtBuilder::default().gas_price(3).build(), || { - let derived_address = ::DetermineContractAddress::contract_address_for( - &code_ctor_transfer, - &[], - &0, - ); - - Balances::set_free_balance(&0, 100_000_000); - Balances::increase_total_stake_by(100_000_000); - Balances::set_free_balance(&derived_address, 30); - Balances::increase_total_stake_by(30); - - assert_ok!(Contract::create( - Origin::signed(0), - 11, - 100_000, - code_ctor_transfer.clone(), - Vec::new(), - )); - - // 11 - value sent with the transaction - // (3 * 129) - gas spent by the ctor - // (3 * 175) - base gas fee for create (175) (top level) multipled by gas price (3) - // ((21 / 3) * 3) - price for contract creation - assert_eq!( - Balances::free_balance(&0), - 100_000_000 - 11 - (3 * 129) - (3 * 175) - ((21 / 3) * 3) - ); - assert_eq!(Balances::free_balance(&derived_address), 30 + 11); - - assert_eq!(>::get(&derived_address), code_transfer); - }); -} - -const CODE_NOP: &'static str = r#" -(module - (func (export "call") - nop - ) -) -"#; - -#[test] -fn refunds_unused_gas() { - let code_nop = wabt::wat2wasm(CODE_NOP).unwrap(); - - with_externalities(&mut ExtBuilder::default().build(), || { - >::insert(1, code_nop.to_vec()); - - Balances::set_free_balance(&0, 100_000_000); - Balances::increase_total_stake_by(100_000_000); - - assert_ok!(Contract::call(Origin::signed(0), 1, 0, 100_000, Vec::new())); - - assert_eq!(Balances::free_balance(&0), 100_000_000 - 4 - (2 * 135)); - }); -} - -#[test] -fn call_with_zero_value() { - with_externalities(&mut ExtBuilder::default().build(), || { - >::insert(1, vec![]); - - Balances::set_free_balance(&0, 100_000_000); - Balances::increase_total_stake_by(100_000_000); - - assert_ok!(Contract::call(Origin::signed(0), 1, 0, 100_000, Vec::new())); - - assert_eq!(Balances::free_balance(&0), 100_000_000 - (2 * 135)); - }); -} - -#[test] -fn create_with_zero_endowment() { - let code_nop = wabt::wat2wasm(CODE_NOP).unwrap(); - - with_externalities(&mut ExtBuilder::default().build(), || { - Balances::set_free_balance(&0, 100_000_000); - Balances::increase_total_stake_by(100_000_000); - - assert_ok!(Contract::create(Origin::signed(0), 0, 100_000, code_nop, Vec::new())); - - assert_eq!( - Balances::free_balance(&0), - // 4 - for the gas spent by the constructor - // 2 * 175 - base gas fee for create (175) multiplied by gas price (2) (top level) - 100_000_000 - 4 - (2 * 175), - ); - }); -} - -#[test] -fn account_removal_removes_storage() { - with_externalities( - &mut ExtBuilder::default().existential_deposit(100).build(), - || { - // Setup two accounts with free balance above than exsistential threshold. - { - Balances::set_free_balance(&1, 110); - Balances::increase_total_stake_by(110); - >::insert(1, b"foo".to_vec(), b"1".to_vec()); - >::insert(1, b"bar".to_vec(), b"2".to_vec()); - - Balances::set_free_balance(&2, 110); - Balances::increase_total_stake_by(110); - >::insert(2, b"hello".to_vec(), b"3".to_vec()); - >::insert(2, b"world".to_vec(), b"4".to_vec()); - } - - // Transfer funds from account 1 of such amount that after this transfer - // the balance of account 1 is will be below than exsistential threshold. - // - // This should lead to the removal of all storage associated with this account. - assert_ok!(Balances::transfer(Origin::signed(1), 2.into(), 20)); - - // Verify that all entries from account 1 is removed, while - // entries from account 2 is in place. - { - assert_eq!(>::get(1, b"foo".to_vec()), None); - assert_eq!(>::get(1, b"bar".to_vec()), None); - - assert_eq!( - >::get(2, b"hello".to_vec()), - Some(b"3".to_vec()) - ); - assert_eq!( - >::get(2, b"world".to_vec()), - Some(b"4".to_vec()) - ); - } - }, - ); -} - -const CODE_UNREACHABLE: &'static str = r#" -(module - (func (export "call") - nop - unreachable - ) -) -"#; - -#[test] -fn top_level_call_refunds_even_if_fails() { - let code_unreachable = wabt::wat2wasm(CODE_UNREACHABLE).unwrap(); - with_externalities(&mut ExtBuilder::default().gas_price(4).build(), || { - >::insert(1, code_unreachable.to_vec()); - - Balances::set_free_balance(&0, 100_000_000); - Balances::increase_total_stake_by(100_000_000); - - assert_err!( - Contract::call(Origin::signed(0), 1, 0, 100_000, Vec::new()), - "vm execute returned error while call" - ); - - assert_eq!(Balances::free_balance(&0), 100_000_000 - (4 * 3) - (4 * 135)); - }); -} - -const CODE_LOOP: &'static str = r#" -(module - (func (export "call") - (loop - (br 0) - ) - ) -) -"#; - -#[test] -fn block_gas_limit() { - let code_loop = wabt::wat2wasm(CODE_LOOP).unwrap(); - with_externalities( - &mut ExtBuilder::default().block_gas_limit(100_000).build(), - || { - >::insert(1, code_loop.to_vec()); - - Balances::set_free_balance(&0, 100_000_000); - Balances::increase_total_stake_by(100_000_000); - - // Spend 50_000 units of gas (OOG). - assert_err!( - Contract::call(Origin::signed(0), 1, 0, 50_000, Vec::new()), - "vm execute returned error while call" - ); - - // Ensure we can't spend more gas than available in block gas limit. - assert_err!( - Contract::call(Origin::signed(0), 1, 0, 50_001, Vec::new()), - "block gas limit is reached" - ); - - // However, we can spend another 50_000 - assert_err!( - Contract::call(Origin::signed(0), 1, 0, 50_000, Vec::new()), - "vm execute returned error while call" - ); - }, - ); -} - -const CODE_INPUT_DATA: &'static str = r#" -(module - (import "env" "ext_input_size" (func $ext_input_size (result i32))) - (import "env" "ext_input_copy" (func $ext_input_copy (param i32 i32 i32))) - (import "env" "memory" (memory 1 1)) - - (func (export "call") - (block $fail - ;; fail if ext_input_size != 4 - (br_if $fail - (i32.ne - (i32.const 4) - (call $ext_input_size) - ) - ) - - (call $ext_input_copy - (i32.const 0) - (i32.const 0) - (i32.const 4) - ) - - - (br_if $fail - (i32.ne - (i32.load8_u (i32.const 0)) - (i32.const 0) - ) - ) - (br_if $fail - (i32.ne - (i32.load8_u (i32.const 1)) - (i32.const 1) - ) - ) - (br_if $fail - (i32.ne - (i32.load8_u (i32.const 2)) - (i32.const 2) - ) - ) - (br_if $fail - (i32.ne - (i32.load8_u (i32.const 3)) - (i32.const 3) - ) - ) - - (return) - ) - unreachable - ) -) -"#; - -#[test] -fn input_data() { - let code_input_data = wabt::wat2wasm(CODE_INPUT_DATA).unwrap(); - with_externalities( - &mut ExtBuilder::default().build(), - || { - >::insert(1, code_input_data.to_vec()); - - Balances::set_free_balance(&0, 100_000_000); - Balances::increase_total_stake_by(100_000_000); - - assert_ok!(Contract::call(Origin::signed(0), 1, 0, 50_000, vec![0, 1, 2, 3])); - - // all asserts are made within contract code itself. - }, - ); -} diff --git a/substrate/runtime/contract/src/vm/env_def/macros.rs b/substrate/runtime/contract/src/vm/env_def/macros.rs deleted file mode 100644 index 40651749eb55b..0000000000000 --- a/substrate/runtime/contract/src/vm/env_def/macros.rs +++ /dev/null @@ -1,285 +0,0 @@ -// Copyright 2018 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 . - -//! Definition of macros that hides boilerplate of defining external environment -//! for a wasm module. -//! -//! Typically you should use `define_env` macro. - -#[macro_export] -macro_rules! convert_args { - () => (vec![]); - ( $( $t:ty ),* ) => ( vec![ $( { use $crate::vm::env_def::ConvertibleToWasm; <$t>::VALUE_TYPE }, )* ] ); -} - -#[macro_export] -macro_rules! gen_signature { - ( ( $( $params: ty ),* ) ) => ( - { - FunctionType::new(convert_args!($($params),*), None) - } - ); - - ( ( $( $params: ty ),* ) -> $returns: ty ) => ( - { - FunctionType::new(convert_args!($($params),*), Some({ - use $crate::vm::env_def::ConvertibleToWasm; <$returns>::VALUE_TYPE - })) - } - ); -} - -/// Unmarshall arguments and then execute `body` expression and return its result. -macro_rules! unmarshall_then_body { - ( $body:tt, $ctx:ident, $args_iter:ident, $( $names:ident : $params:ty ),* ) => ({ - $( - let $names : <$params as $crate::vm::env_def::ConvertibleToWasm>::NativeType = - $args_iter.next() - .and_then(|v| <$params as $crate::vm::env_def::ConvertibleToWasm> - ::from_typed_value(v.clone())) - .expect( - "precondition: all imports should be checked against the signatures of corresponding - functions defined by `define_env!` macro by the user of the macro; - signatures of these functions defined by `$params`; - calls always made with arguments types of which are defined by the corresponding imports; - thus types of arguments should be equal to type list in `$params` and - length of argument list and $params should be equal; - thus this can never be `None`; - qed; - " - ); - )* - $body - }) -} - -/// Since we can't specify the type of closure directly at binding site: -/// -/// ```rust,ignore -/// let f: FnOnce() -> Result<::NativeType, _> = || { /* ... */ }; -/// ``` -/// -/// we use this function to constrain the type of the closure. -#[inline(always)] -pub fn constrain_closure(f: F) -> F -where - F: FnOnce() -> Result, -{ - f -} - -#[macro_export] -macro_rules! unmarshall_then_body_then_marshall { - ( $args_iter:ident, $ctx:ident, ( $( $names:ident : $params:ty ),* ) -> $returns:ty => $body:tt ) => ({ - let body = $crate::vm::env_def::macros::constrain_closure::< - <$returns as $crate::vm::env_def::ConvertibleToWasm>::NativeType, _ - >(|| { - unmarshall_then_body!($body, $ctx, $args_iter, $( $names : $params ),*) - }); - let r = body()?; - return Ok($crate::sandbox::ReturnValue::Value({ use $crate::vm::env_def::ConvertibleToWasm; r.to_typed_value() })) - }); - ( $args_iter:ident, $ctx:ident, ( $( $names:ident : $params:ty ),* ) => $body:tt ) => ({ - let body = $crate::vm::env_def::macros::constrain_closure::<(), _>(|| { - unmarshall_then_body!($body, $ctx, $args_iter, $( $names : $params ),*) - }); - body()?; - return Ok($crate::sandbox::ReturnValue::Unit) - }) -} - -#[macro_export] -macro_rules! define_func { - ( < E: $ext_ty:tt > $name:ident ( $ctx: ident $(, $names:ident : $params:ty)*) $(-> $returns:ty)* => $body:tt ) => { - fn $name< E: $ext_ty >( - $ctx: &mut $crate::vm::Runtime, - args: &[$crate::sandbox::TypedValue], - ) -> Result { - #[allow(unused)] - let mut args = args.iter(); - - unmarshall_then_body_then_marshall!( - args, - $ctx, - ( $( $names : $params ),* ) $( -> $returns )* => $body - ) - } - }; -} - -/// Define a function set that can be imported by executing wasm code. -/// -/// **NB**: Be advised that all functions defined by this macro -/// will panic if called with unexpected arguments. -/// -/// It's up to the user of this macro to check signatures of wasm code to be executed -/// and reject the code if any imported function has a mismached signature. -macro_rules! define_env { - ( $init_name:ident , < E: $ext_ty:tt > , - $( $name:ident ( $ctx:ident $( , $names:ident : $params:ty )* ) - $( -> $returns:ty )* => $body:tt , )* - ) => { - pub(crate) fn $init_name() -> HostFunctionSet { - let mut env = HostFunctionSet::new(); - - $( - env.funcs.insert( - stringify!( $name ).into(), - HostFunction::new( - gen_signature!( ( $( $params ),* ) $( -> $returns )* ), - { - define_func!( - < E: $ext_ty > $name ( $ctx $(, $names : $params )* ) $( -> $returns )* => $body - ); - $name:: - }, - ), - ); - )* - - env - } - }; -} - -#[cfg(test)] -mod tests { - use parity_wasm::elements::FunctionType; - use parity_wasm::elements::ValueType; - use runtime_primitives::traits::{As, Zero}; - use sandbox::{self, ReturnValue, TypedValue}; - use vm::env_def::{HostFunction, HostFunctionSet}; - use vm::tests::MockExt; - use vm::{Ext, Runtime}; - use Trait; - - #[test] - fn macro_unmarshall_then_body_then_marshall_value_or_trap() { - fn test_value( - _ctx: &mut u32, - args: &[sandbox::TypedValue], - ) -> Result { - let mut args = args.iter(); - unmarshall_then_body_then_marshall!( - args, - _ctx, - (a: u32, b: u32) -> u32 => { - if b == 0 { - Err(sandbox::HostError) - } else { - Ok(a / b) - } - } - ) - } - - let ctx = &mut 0; - assert_eq!( - test_value(ctx, &[TypedValue::I32(15), TypedValue::I32(3)]).unwrap(), - ReturnValue::Value(TypedValue::I32(5)), - ); - assert!(test_value(ctx, &[TypedValue::I32(15), TypedValue::I32(0)]).is_err()); - } - - #[test] - fn macro_unmarshall_then_body_then_marshall_unit() { - fn test_unit( - ctx: &mut u32, - args: &[sandbox::TypedValue], - ) -> Result { - let mut args = args.iter(); - unmarshall_then_body_then_marshall!( - args, - ctx, - (a: u32, b: u32) => { - *ctx = a + b; - Ok(()) - } - ) - } - - let ctx = &mut 0; - let result = test_unit(ctx, &[TypedValue::I32(2), TypedValue::I32(3)]).unwrap(); - assert_eq!(result, ReturnValue::Unit); - assert_eq!(*ctx, 5); - } - - #[test] - fn macro_define_func() { - define_func!( ext_gas (_ctx, amount: u32) => { - let amount = <<::T as Trait>::Gas as As>::sa(amount); - if !amount.is_zero() { - Ok(()) - } else { - Err(sandbox::HostError) - } - }); - let _f: fn(&mut Runtime, &[sandbox::TypedValue]) - -> Result = ext_gas::; - } - - #[test] - fn macro_gen_signature() { - assert_eq!( - gen_signature!((i32)), - FunctionType::new(vec![ValueType::I32], None), - ); - - assert_eq!( - gen_signature!( (i32, u32) -> u32 ), - FunctionType::new(vec![ValueType::I32, ValueType::I32], Some(ValueType::I32)), - ); - } - - #[test] - fn macro_unmarshall_then_body() { - let args = vec![TypedValue::I32(5), TypedValue::I32(3)]; - let mut args = args.iter(); - - let ctx: &mut u32 = &mut 0; - - let r = unmarshall_then_body!( - { - *ctx = a + b; - a * b - }, - ctx, - args, - a: u32, - b: u32 - ); - - assert_eq!(*ctx, 8); - assert_eq!(r, 15); - } - - #[test] - fn macro_define_env() { - define_env!(init_env, , - ext_gas( _ctx, amount: u32 ) => { - let amount = <<::T as Trait>::Gas as As>::sa(amount); - if !amount.is_zero() { - Ok(()) - } else { - Err(sandbox::HostError) - } - }, - ); - - let env = init_env::(); - assert!(env.funcs.get("ext_gas").is_some()); - } -} diff --git a/substrate/runtime/contract/src/vm/env_def/mod.rs b/substrate/runtime/contract/src/vm/env_def/mod.rs deleted file mode 100644 index dbdda705d3326..0000000000000 --- a/substrate/runtime/contract/src/vm/env_def/mod.rs +++ /dev/null @@ -1,315 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -use super::{BalanceOf, CallReceipt, CreateReceipt, Ext, GasMeterResult, Runtime}; -use codec::Decode; -use parity_wasm::elements::{FunctionType, ValueType}; -use rstd::prelude::*; -use rstd::string::String; -use rstd::collections::btree_map::BTreeMap; -use runtime_primitives::traits::As; -use sandbox::{self, TypedValue}; -use system; -use Trait; - -#[macro_use] -mod macros; - -pub trait ConvertibleToWasm: Sized { - const VALUE_TYPE: ValueType; - type NativeType; - fn to_typed_value(self) -> TypedValue; - fn from_typed_value(TypedValue) -> Option; -} -impl ConvertibleToWasm for i32 { - type NativeType = i32; - const VALUE_TYPE: ValueType = ValueType::I32; - fn to_typed_value(self) -> TypedValue { - TypedValue::I32(self) - } - fn from_typed_value(v: TypedValue) -> Option { - v.as_i32() - } -} -impl ConvertibleToWasm for u32 { - type NativeType = u32; - const VALUE_TYPE: ValueType = ValueType::I32; - fn to_typed_value(self) -> TypedValue { - TypedValue::I32(self as i32) - } - fn from_typed_value(v: TypedValue) -> Option { - match v { - TypedValue::I32(v) => Some(v as u32), - _ => None, - } - } -} -impl ConvertibleToWasm for u64 { - type NativeType = u64; - const VALUE_TYPE: ValueType = ValueType::I64; - fn to_typed_value(self) -> TypedValue { - TypedValue::I64(self as i64) - } - fn from_typed_value(v: TypedValue) -> Option { - match v { - TypedValue::I64(v) => Some(v as u64), - _ => None, - } - } -} - -/// Represents a set of function that defined in this particular environment and -/// which can be imported and called by the module. -pub(crate) struct HostFunctionSet { - /// Functions which defined in the environment. - pub funcs: BTreeMap>, -} -impl HostFunctionSet { - pub fn new() -> Self { - HostFunctionSet { - funcs: BTreeMap::new(), - } - } -} - -pub(crate) struct HostFunction { - pub(crate) f: fn(&mut Runtime, &[sandbox::TypedValue]) - -> Result, - func_type: FunctionType, -} -impl HostFunction { - /// Create a new instance of a host function. - pub fn new( - func_type: FunctionType, - f: fn(&mut Runtime, &[sandbox::TypedValue]) - -> Result, - ) -> Self { - HostFunction { func_type, f } - } - - /// Returns a function pointer of this host function. - pub fn raw_fn_ptr( - &self, - ) -> fn(&mut Runtime, &[sandbox::TypedValue]) - -> Result { - self.f - } - - /// Check if the this function could be invoked with the given function signature. - pub fn func_type_matches(&self, func_type: &FunctionType) -> bool { - &self.func_type == func_type - } -} - -// TODO: ext_balance, ext_address, ext_callvalue, etc. - -// Define a function `fn init_env() -> HostFunctionSet` that returns -// a function set which can be imported by an executed contract. -define_env!(init_env, , - - // gas(amount: u32) - // - // Account for used gas. Traps if gas used is greater than gas limit. - // - // - amount: How much gas is used. - gas(ctx, amount: u32) => { - let amount = <<::T as Trait>::Gas as As>::sa(amount); - - match ctx.gas_meter.charge(amount) { - GasMeterResult::Proceed => Ok(()), - GasMeterResult::OutOfGas => Err(sandbox::HostError), - } - }, - - // ext_put_storage(location_ptr: u32, value_non_null: u32, value_ptr: u32); - // - // Change the value at the given location in storage or remove it. - // - // - location_ptr: pointer into the linear - // memory where the location of the requested value is placed. - // - value_non_null: if set to 0, then the entry - // at the given location will be removed. - // - value_ptr: pointer into the linear memory - // where the value to set is placed. If `value_non_null` is set to 0, then this parameter is ignored. - ext_set_storage(ctx, location_ptr: u32, value_non_null: u32, value_ptr: u32) => { - let mut location = [0; 32]; - - ctx.memory().get(location_ptr, &mut location)?; - - let value = if value_non_null != 0 { - let mut value = [0; 32]; - ctx.memory().get(value_ptr, &mut value)?; - Some(value.to_vec()) - } else { - None - }; - ctx.ext.set_storage(&location, value); - - Ok(()) - }, - - // ext_get_storage(location_ptr: u32, dest_ptr: u32); - // - // Retrieve the value at the given location from the strorage. - // If there is no entry at the given location then all-zero-value - // will be returned. - // - // - location_ptr: pointer into the linear - // memory where the location of the requested value is placed. - // - dest_ptr: pointer where contents of the specified storage location - // should be placed. - ext_get_storage(ctx, location_ptr: u32, dest_ptr: u32) => { - let mut location = [0; 32]; - ctx.memory().get(location_ptr, &mut location)?; - - if let Some(value) = ctx.ext.get_storage(&location) { - ctx.memory().set(dest_ptr, &value)?; - } else { - ctx.memory().set(dest_ptr, &[0u8; 32])?; - } - - Ok(()) - }, - - // ext_call(transfer_to_ptr: u32, transfer_to_len: u32, gas: u64, value_ptr: u32, value_len: u32, input_data_ptr: u32, input_data_len: u32) - ext_call(ctx, callee_ptr: u32, callee_len: u32, gas: u64, value_ptr: u32, value_len: u32, input_data_ptr: u32, input_data_len: u32) -> u32 => { - let mut callee = Vec::new(); - callee.resize(callee_len as usize, 0); - ctx.memory().get(callee_ptr, &mut callee)?; - let callee = - <::T as system::Trait>::AccountId::decode(&mut &callee[..]) - .ok_or_else(|| sandbox::HostError)?; - - let mut value_buf = Vec::new(); - value_buf.resize(value_len as usize, 0); - ctx.memory().get(value_ptr, &mut value_buf)?; - let value = BalanceOf::<::T>::decode(&mut &value_buf[..]) - .ok_or_else(|| sandbox::HostError)?; - - let mut input_data = Vec::new(); - input_data.resize(input_data_len as usize, 0u8); - ctx.memory().get(input_data_ptr, &mut input_data)?; - - let nested_gas_limit = if gas == 0 { - ctx.gas_meter.gas_left() - } else { - <<::T as Trait>::Gas as As>::sa(gas) - }; - let ext = &mut ctx.ext; - let call_outcome = ctx.gas_meter.with_nested(nested_gas_limit, |nested_meter| { - match nested_meter { - Some(nested_meter) => ext.call(&callee, value, nested_meter, &input_data), - // there is not enough gas to allocate for the nested call. - None => Err(()), - } - }); - - match call_outcome { - // TODO: Find a way how to pass return_data back to the this sandbox. - Ok(CallReceipt { .. }) => Ok(0), - Err(_) => Ok(1), - } - }, - - // ext_create(code_ptr: u32, code_len: u32, gas: u64, value_ptr: u32, value_len: u32, input_data_ptr: u32, input_data_len: u32) -> u32 - ext_create( - ctx, code_ptr: u32, - code_len: u32, - gas: u64, - value_ptr: u32, - value_len: u32, - input_data_ptr: u32, - input_data_len: u32 - ) -> u32 => { - let mut value_buf = Vec::new(); - value_buf.resize(value_len as usize, 0); - ctx.memory().get(value_ptr, &mut value_buf)?; - let value = BalanceOf::<::T>::decode(&mut &value_buf[..]) - .ok_or_else(|| sandbox::HostError)?; - - let mut code = Vec::new(); - code.resize(code_len as usize, 0u8); - ctx.memory().get(code_ptr, &mut code)?; - - let mut input_data = Vec::new(); - input_data.resize(input_data_len as usize, 0u8); - ctx.memory().get(input_data_ptr, &mut input_data)?; - - let nested_gas_limit = if gas == 0 { - ctx.gas_meter.gas_left() - } else { - <<::T as Trait>::Gas as As>::sa(gas) - }; - let ext = &mut ctx.ext; - let create_outcome = ctx.gas_meter.with_nested(nested_gas_limit, |nested_meter| { - match nested_meter { - Some(nested_meter) => ext.create(&code, value, nested_meter, &input_data), - // there is not enough gas to allocate for the nested call. - None => Err(()), - } - }); - - match create_outcome { - // TODO: Copy an address of the created contract in the sandbox. - Ok(CreateReceipt { .. }) => Ok(0), - Err(_) => Ok(1), - } - }, - - // ext_return(data_ptr: u32, data_len: u32) -> ! - ext_return(ctx, data_ptr: u32, data_len: u32) => { - let mut data_buf = Vec::new(); - data_buf.resize(data_len as usize, 0); - ctx.memory().get(data_ptr, &mut data_buf)?; - - ctx.store_return_data(data_buf) - .map_err(|_| sandbox::HostError)?; - - // The trap mechanism is used to immediately terminate the execution. - // This trap should be handled appropriately before returning the result - // to the user of this crate. - Err(sandbox::HostError) - }, - - // ext_input_size() -> u32 - // - // Returns size of an input buffer. - ext_input_size(ctx) -> u32 => { - Ok(ctx.input_data.len() as u32) - }, - - // ext_input_copy(dest_ptr: u32, offset: u32, len: u32) - // - // Copy data from an input buffer starting from `offset` with length `len` into the contract memory. - // The region at which the data should be put is specified by `dest_ptr`. - ext_input_copy(ctx, dest_ptr: u32, offset: u32, len: u32) => { - let offset = offset as usize; - if offset > ctx.input_data.len() { - // Offset can't be larger than input buffer length. - return Err(sandbox::HostError); - } - - // This can't panic since `offset <= ctx.input_data.len()`. - let src = &ctx.input_data[offset..]; - if src.len() != len as usize { - return Err(sandbox::HostError); - } - - ctx.memory().set(dest_ptr, src)?; - - Ok(()) - }, -); diff --git a/substrate/runtime/contract/src/vm/mod.rs b/substrate/runtime/contract/src/vm/mod.rs deleted file mode 100644 index 0ca9f9e694b5b..0000000000000 --- a/substrate/runtime/contract/src/vm/mod.rs +++ /dev/null @@ -1,553 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! This module provides a means for executing contracts -//! represented in wasm. - -use exec::{CallReceipt, CreateReceipt}; -use gas::{GasMeter, GasMeterResult}; -use rstd::prelude::*; -use runtime_primitives::traits::{As, CheckedMul}; -use {sandbox, balances, system}; -use Trait; - -type BalanceOf = ::Balance; -type AccountIdOf = ::AccountId; - -mod prepare; - -#[macro_use] -mod env_def; - -use self::prepare::{prepare_contract, PreparedContract}; - -/// An interface that provides an access to the external environment in which the -/// smart-contract is executed. -/// -/// This interface is specialised to an account of the executing code, so all -/// operations are implicitly performed on that account. -pub trait Ext { - type T: Trait; - - /// Returns the storage entry of the executing account by the given key. - fn get_storage(&self, key: &[u8]) -> Option>; - - /// Sets the storage entry by the given key to the specified value. - fn set_storage(&mut self, key: &[u8], value: Option>); - - // TODO: Return the address of the created contract. - /// Create a new account for a contract. - /// - /// The newly created account will be associated with the `code`. `value` specifies the amount of value - /// transfered from this to the newly created account. - fn create( - &mut self, - code: &[u8], - value: BalanceOf, - gas_meter: &mut GasMeter, - data: &[u8], - ) -> Result, ()>; - - /// Call (possibly transfering some amount of funds) into the specified account. - fn call( - &mut self, - to: &AccountIdOf, - value: BalanceOf, - gas_meter: &mut GasMeter, - data: &[u8], - ) -> Result; -} - -/// Error that can occur while preparing or executing wasm smart-contract. -#[derive(Debug, PartialEq, Eq)] -pub enum Error { - /// Error happened while serializing the module. - Serialization, - - /// Error happened while deserializing the module. - Deserialization, - - /// Internal memory declaration has been found in the module. - InternalMemoryDeclared, - - /// Gas instrumentation failed. - /// - /// This most likely indicates the module isn't valid. - GasInstrumentation, - - /// Stack instrumentation failed. - /// - /// This most likely indicates the module isn't valid. - StackHeightInstrumentation, - - /// Error happened during invocation of the contract's entrypoint. - /// - /// Most likely because of trap. - Invoke, - - /// Error happened during instantiation. - /// - /// This might indicate that `start` function trapped, or module isn't - /// instantiable and/or unlinkable. - Instantiate, - - /// Memory creation error. - /// - /// This might happen when the memory import has invalid descriptor or - /// requested too much resources. - Memory, -} - -/// Enumerates all possible *special* trap conditions. -/// -/// In this runtime traps used not only for signaling about errors but also -/// to just terminate quickly in some cases. -enum SpecialTrap { - // TODO: Can we pass wrapped memory instance instead of copying? - /// Signals that trap was generated in response to call `ext_return` host function. - Return(Vec), -} - -pub(crate) struct Runtime<'a, 'data, E: Ext + 'a> { - ext: &'a mut E, - input_data: &'data [u8], - config: &'a Config, - memory: sandbox::Memory, - gas_meter: &'a mut GasMeter, - special_trap: Option, -} -impl<'a, 'data, E: Ext + 'a> Runtime<'a, 'data, E> { - fn memory(&self) -> &sandbox::Memory { - &self.memory - } - /// Save a data buffer as a result of the execution. - /// - /// This function also charges gas for the returning. - /// - /// Returns `Err` if there is not enough gas. - fn store_return_data(&mut self, data: Vec) -> Result<(), ()> { - let data_len = <<::T as Trait>::Gas as As>::sa(data.len() as u64); - let price = (self.config.return_data_per_byte_cost) - .checked_mul(&data_len) - .ok_or_else(|| ())?; - - match self.gas_meter.charge(price) { - GasMeterResult::Proceed => { - self.special_trap = Some(SpecialTrap::Return(data)); - Ok(()) - } - GasMeterResult::OutOfGas => Err(()), - } - } -} - -fn to_execution_result( - runtime: Runtime, - run_err: Option, -) -> Result { - // Check the exact type of the error. It could be plain trap or - // special runtime trap the we must recognize. - let return_data = match (run_err, runtime.special_trap) { - // No traps were generated. Proceed normally. - (None, None) => Vec::new(), - // Special case. The trap was the result of the execution `return` host function. - (Some(sandbox::Error::Execution), Some(SpecialTrap::Return(rd))) => rd, - // Any other kind of a trap should result in a failure. - (Some(_), _) => return Err(Error::Invoke), - // Any other case (such as special trap flag without actual trap) signifies - // a logic error. - _ => unreachable!(), - }; - - Ok(ExecutionResult { return_data }) -} - -/// The result of execution of a smart-contract. -#[derive(PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(Debug))] -pub struct ExecutionResult { - /// The result produced by the execution of the contract. - /// - /// The contract can designate some buffer at the execution time via a special function. - /// If contract called this function with non-empty buffer it will be copied here. - /// - /// Note that gas is already charged for returning the data. - pub return_data: Vec, -} - -/// Execute the given code as a contract. -pub fn execute<'a, E: Ext>( - code: &[u8], - input_data: &[u8], - ext: &'a mut E, - gas_meter: &mut GasMeter, -) -> Result { - let config = Config::default(); - let env = env_def::init_env(); - - let PreparedContract { - instrumented_code, - memory, - } = prepare_contract(code, &config, &env)?; - - let mut imports = sandbox::EnvironmentDefinitionBuilder::new(); - for (func_name, ext_func) in &env.funcs { - imports.add_host_func("env", &func_name[..], ext_func.raw_fn_ptr()); - } - imports.add_memory("env", "memory", memory.clone()); - - let mut runtime = Runtime { - ext, - input_data, - config: &config, - memory, - gas_meter, - special_trap: None, - }; - - let mut instance = sandbox::Instance::new(&instrumented_code, &imports, &mut runtime) - .map_err(|_| Error::Instantiate)?; - - let run_result = instance.invoke(b"call", &[], &mut runtime); - - to_execution_result(runtime, run_result.err()) -} - -// TODO: Extract it to the root of the crate -#[derive(Clone)] -struct Config { - /// Gas cost of a growing memory by single page. - grow_mem_cost: T::Gas, - - /// Gas cost of a regular operation. - regular_op_cost: T::Gas, - - /// Gas cost per one byte returned. - return_data_per_byte_cost: T::Gas, - - /// How tall the stack is allowed to grow? - /// - /// See https://wiki.parity.io/WebAssembly-StackHeight to find out - /// how the stack frame cost is calculated. - max_stack_height: u32, - - //// What is the maximal memory pages amount is allowed to have for - /// a contract. - max_memory_pages: u32, -} - -impl Default for Config { - fn default() -> Config { - Config { - grow_mem_cost: T::Gas::sa(1), - regular_op_cost: T::Gas::sa(1), - return_data_per_byte_cost: T::Gas::sa(1), - max_stack_height: 64 * 1024, - max_memory_pages: 16, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use gas::GasMeter; - use std::collections::HashMap; - use tests::Test; - use wabt; - - #[derive(Debug, PartialEq, Eq)] - struct CreateEntry { - code: Vec, - endowment: u64, - data: Vec, - gas_left: u64, - } - #[derive(Debug, PartialEq, Eq)] - struct TransferEntry { - to: u64, - value: u64, - data: Vec, - gas_left: u64, - } - #[derive(Default)] - pub struct MockExt { - storage: HashMap, Vec>, - creates: Vec, - transfers: Vec, - next_account_id: u64, - } - impl Ext for MockExt { - type T = Test; - - fn get_storage(&self, key: &[u8]) -> Option> { - self.storage.get(key).cloned() - } - fn set_storage(&mut self, key: &[u8], value: Option>) { - *self.storage.entry(key.to_vec()).or_insert(Vec::new()) = value.unwrap_or(Vec::new()); - } - fn create( - &mut self, - code: &[u8], - endowment: u64, - gas_meter: &mut GasMeter, - data: &[u8], - ) -> Result, ()> { - self.creates.push(CreateEntry { - code: code.to_vec(), - endowment, - data: data.to_vec(), - gas_left: gas_meter.gas_left(), - }); - let address = self.next_account_id; - self.next_account_id += 1; - - Ok(CreateReceipt { address }) - } - fn call( - &mut self, - to: &u64, - value: u64, - gas_meter: &mut GasMeter, - data: &[u8], - ) -> Result { - self.transfers.push(TransferEntry { - to: *to, - value, - data: data.to_vec(), - gas_left: gas_meter.gas_left(), - }); - // Assume for now that it was just a plain transfer. - // TODO: Add tests for different call outcomes. - Ok(CallReceipt { - return_data: Vec::new(), - }) - } - } - - const CODE_TRANSFER: &str = r#" -(module - ;; ext_call( - ;; callee_ptr: u32, - ;; callee_len: u32, - ;; gas: u64, - ;; value_ptr: u32, - ;; value_len: u32, - ;; input_data_ptr: u32, - ;; input_data_len: u32 - ;;) -> u32 - (import "env" "ext_call" (func $ext_call (param i32 i32 i64 i32 i32 i32 i32) (result i32))) - (import "env" "memory" (memory 1 1)) - (func (export "call") - (drop - (call $ext_call - (i32.const 4) ;; Pointer to "callee" address. - (i32.const 8) ;; Length of "callee" address. - (i64.const 0) ;; How much gas to devote for the execution. 0 = all. - (i32.const 12) ;; Pointer to the buffer with value to transfer - (i32.const 8) ;; Length of the buffer with value to transfer. - (i32.const 20) ;; Pointer to input data buffer address - (i32.const 4) ;; Length of input data buffer - ) - ) - ) - ;; Destination AccountId to transfer the funds. - ;; Represented by u64 (8 bytes long) in little endian. - (data (i32.const 4) "\09\00\00\00\00\00\00\00") - ;; Amount of value to transfer. - ;; Represented by u64 (8 bytes long) in little endian. - (data (i32.const 12) "\06\00\00\00\00\00\00\00") - - (data (i32.const 20) "\01\02\03\04") -) -"#; - - #[test] - fn contract_transfer() { - let code_transfer = wabt::wat2wasm(CODE_TRANSFER).unwrap(); - - let mut mock_ext = MockExt::default(); - execute( - &code_transfer, - &[], - &mut mock_ext, - &mut GasMeter::with_limit(50_000, 1), - ).unwrap(); - - assert_eq!( - &mock_ext.transfers, - &[TransferEntry { - to: 9, - value: 6, - data: vec![ - 1, 2, 3, 4, - ], - gas_left: 49990, - }] - ); - } - - const CODE_CREATE: &str = r#" -(module - ;; ext_create( - ;; code_ptr: u32, - ;; code_len: u32, - ;; gas: u64, - ;; value_ptr: u32, - ;; value_len: u32, - ;; input_data_ptr: u32, - ;; input_data_len: u32, - ;; ) -> u32 - (import "env" "ext_create" (func $ext_create (param i32 i32 i64 i32 i32 i32 i32) (result i32))) - (import "env" "memory" (memory 1 1)) - (func (export "call") - (drop - (call $ext_create - (i32.const 12) ;; Pointer to `code` - (i32.const 8) ;; Length of `code` - (i64.const 0) ;; How much gas to devote for the execution. 0 = all. - (i32.const 4) ;; Pointer to the buffer with value to transfer - (i32.const 8) ;; Length of the buffer with value to transfer - (i32.const 20) ;; Pointer to input data buffer address - (i32.const 4) ;; Length of input data buffer - ) - ) - ) - ;; Amount of value to transfer. - ;; Represented by u64 (8 bytes long) in little endian. - (data (i32.const 4) "\03\00\00\00\00\00\00\00") - ;; Embedded wasm code. - (data (i32.const 12) "\00\61\73\6d\01\00\00\00") - ;; Input data to pass to the contract being created. - (data (i32.const 20) "\01\02\03\04") -) -"#; - - #[test] - fn contract_create() { - let code_create = wabt::wat2wasm(CODE_CREATE).unwrap(); - - let mut mock_ext = MockExt::default(); - execute( - &code_create, - &[], - &mut mock_ext, - &mut GasMeter::with_limit(50_000, 1), - ).unwrap(); - - assert_eq!( - &mock_ext.creates, - &[CreateEntry { - code: vec![0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00], - endowment: 3, - data: vec![ - 1, 2, 3, 4, - ], - gas_left: 49990, - }] - ); - } - - const CODE_MEM: &str = r#" -(module - ;; Internal memory is not allowed. - (memory 1 1) - - (func (export "call") - nop - ) -) -"#; - - #[test] - fn contract_internal_mem() { - let code_mem = wabt::wat2wasm(CODE_MEM).unwrap(); - - let mut mock_ext = MockExt::default(); - - assert_matches!( - execute( - &code_mem, - &[], - &mut mock_ext, - &mut GasMeter::with_limit(100_000, 1) - ), - Err(_) - ); - } - - const CODE_TRANSFER_LIMITED_GAS: &str = r#" -(module - ;; ext_call( - ;; callee_ptr: u32, - ;; callee_len: u32, - ;; gas: u64, - ;; value_ptr: u32, - ;; value_len: u32, - ;; input_data_ptr: u32, - ;; input_data_len: u32 - ;;) -> u32 - (import "env" "ext_call" (func $ext_call (param i32 i32 i64 i32 i32 i32 i32) (result i32))) - (import "env" "memory" (memory 1 1)) - (func (export "call") - (drop - (call $ext_call - (i32.const 4) ;; Pointer to "callee" address. - (i32.const 8) ;; Length of "callee" address. - (i64.const 228) ;; How much gas to devote for the execution. - (i32.const 12) ;; Pointer to the buffer with value to transfer - (i32.const 8) ;; Length of the buffer with value to transfer. - (i32.const 20) ;; Pointer to input data buffer address - (i32.const 4) ;; Length of input data buffer - ) - ) - ) - ;; Destination AccountId to transfer the funds. - ;; Represented by u64 (8 bytes long) in little endian. - (data (i32.const 4) "\09\00\00\00\00\00\00\00") - ;; Amount of value to transfer. - ;; Represented by u64 (8 bytes long) in little endian. - (data (i32.const 12) "\06\00\00\00\00\00\00\00") - - (data (i32.const 20) "\01\02\03\04") -) -"#; - - #[test] - fn contract_call_limited_gas() { - let code_transfer = wabt::wat2wasm(CODE_TRANSFER_LIMITED_GAS).unwrap(); - - let mut mock_ext = MockExt::default(); - execute( - &code_transfer, - &[], - &mut mock_ext, - &mut GasMeter::with_limit(50_000, 1), - ).unwrap(); - - assert_eq!( - &mock_ext.transfers, - &[TransferEntry { - to: 9, - value: 6, - data: vec![ - 1, 2, 3, 4, - ], - gas_left: 228, - }] - ); - } -} diff --git a/substrate/runtime/contract/src/vm/prepare.rs b/substrate/runtime/contract/src/vm/prepare.rs deleted file mode 100644 index b15f9cbc3a977..0000000000000 --- a/substrate/runtime/contract/src/vm/prepare.rs +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright 2018 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 . - -//! Module that takes care of loading, checking and preprocessing of a -//! wasm module before execution. - -use super::env_def::HostFunctionSet; -use super::{Config, Error, Ext}; -use rstd::prelude::*; -use parity_wasm::elements::{self, External, MemoryType, Type}; -use pwasm_utils; -use pwasm_utils::rules; -use runtime_primitives::traits::As; -use sandbox; -use Trait; - -struct ContractModule<'a, T: Trait + 'a> { - // 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, - config: &'a Config, -} - -impl<'a, T: Trait> ContractModule<'a, T> { - fn new(original_code: &[u8], config: &'a Config) -> Result, Error> { - let module = - elements::deserialize_buffer(original_code).map_err(|_| Error::Deserialization)?; - Ok(ContractModule { - module: Some(module), - config, - }) - } - - /// Ensures that module doesn't declare internal memories. - /// - /// In this runtime we only allow wasm module to import memory from the environment. - /// 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<(), Error> { - let module = self - .module - .as_ref() - .expect("On entry to the function `module` can't be None; qed"); - if module - .memory_section() - .map_or(false, |ms| ms.entries().len() > 0) - { - return Err(Error::InternalMemoryDeclared); - } - Ok(()) - } - - fn inject_gas_metering(&mut self) -> Result<(), Error> { - let gas_rules = rules::Set::new(self.config.regular_op_cost.as_(), Default::default()) - .with_grow_cost(self.config.grow_mem_cost.as_()) - .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) - .map_err(|_| Error::GasInstrumentation)?; - - self.module = Some(contract_module); - Ok(()) - } - - fn inject_stack_height_metering(&mut self) -> Result<(), Error> { - let module = self - .module - .take() - .expect("On entry to the function `module` can't be `None`; qed"); - - let contract_module = - pwasm_utils::stack_height::inject_limiter(module, self.config.max_stack_height) - .map_err(|_| Error::StackHeightInstrumentation)?; - - self.module = Some(contract_module); - Ok(()) - } - - /// Scan an import section if any. - /// - /// This accomplishes two tasks: - /// - /// - checks any imported function against defined host functions set, incl. - /// their signatures. - /// - if there is a memory import, returns it's descriptor - fn scan_imports(&self, env: &HostFunctionSet) -> Result, Error> { - let module = self - .module - .as_ref() - .expect("On entry to the function `module` can't be `None`; qed"); - - let types = module.type_section().map(|ts| ts.types()).unwrap_or(&[]); - let import_entries = module - .import_section() - .map(|is| is.entries()) - .unwrap_or(&[]); - - let mut imported_mem_type = None; - - for import in import_entries { - if import.module() != "env" { - // This import tries to import something from non-"env" module, - // but all imports are located in "env" at the moment. - return Err(Error::Instantiate); - } - - let type_idx = match import.external() { - &External::Function(ref type_idx) => type_idx, - &External::Memory(ref memory_type) => { - imported_mem_type = Some(memory_type); - continue; - } - _ => continue, - }; - - let Type::Function(ref func_ty) = types - .get(*type_idx as usize) - .ok_or_else(|| Error::Instantiate)?; - - let ext_func = env - .funcs - .get(import.field()) - .ok_or_else(|| Error::Instantiate)?; - if !ext_func.func_type_matches(func_ty) { - return Err(Error::Instantiate); - } - } - Ok(imported_mem_type) - } - - fn into_wasm_code(mut self) -> Result, Error> { - elements::serialize( - self.module - .take() - .expect("On entry to the function `module` can't be `None`; qed"), - ).map_err(|_| Error::Serialization) - } -} - -pub(super) struct PreparedContract { - pub instrumented_code: Vec, - pub memory: sandbox::Memory, -} - -/// Loads the given module given in `original_code`, performs some checks on it and -/// does some preprocessing. -/// -/// The checks are: -/// -/// - module doesn't define an internal memory instance, -/// - imported memory (if any) doesn't reserve more memory than permitted by the `config`, -/// - 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(super) fn prepare_contract( - original_code: &[u8], - config: &Config, - env: &HostFunctionSet, -) -> Result { - let mut contract_module = ContractModule::new(original_code, config)?; - contract_module.ensure_no_internal_memory()?; - contract_module.inject_gas_metering()?; - contract_module.inject_stack_height_metering()?; - - let memory = if let Some(memory_type) = contract_module.scan_imports(env)? { - // Inspect the module to extract the initial and maximum page count. - let limits = memory_type.limits(); - match (limits.initial(), limits.maximum()) { - (initial, Some(maximum)) if initial > maximum => { - // Requested initial number of pages should not exceed the requested maximum. - return Err(Error::Memory); - } - (_, Some(maximum)) if maximum > config.max_memory_pages => { - // Maximum number of pages should not exceed the configured maximum. - return Err(Error::Memory); - } - (_, None) => { - // Maximum number of pages should be always declared. - // This isn't a hard requirement and can be treated as a maxiumum set - // to configured maximum. - return Err(Error::Memory); - } - (initial, maximum) => sandbox::Memory::new(initial, maximum), - } - } else { - // If none memory imported then just crate an empty placeholder. - // Any access to it will lead to out of bounds trap. - sandbox::Memory::new(0, Some(0)) - }; - let memory = memory.map_err(|_| Error::Memory)?; - - Ok(PreparedContract { - instrumented_code: contract_module.into_wasm_code()?, - memory, - }) -} - -#[cfg(test)] -mod tests { - use super::*; - use std::fmt; - use tests::Test; - use vm::tests::MockExt; - use wabt; - - impl fmt::Debug for PreparedContract { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "PreparedContract {{ .. }}") - } - } - - fn parse_and_prepare_wat(wat: &str) -> Result { - let wasm = wabt::Wat2Wasm::new().validate(false).convert(wat).unwrap(); - let config = Config::::default(); - let env = ::vm::env_def::init_env(); - prepare_contract::(wasm.as_ref(), &config, &env) - } - - #[test] - fn internal_memory_declaration() { - let r = parse_and_prepare_wat(r#"(module (memory 1 1))"#); - assert_matches!(r, Err(Error::InternalMemoryDeclared)); - } - - #[test] - fn memory() { - // This test assumes that maximum page number is configured to a certain number. - assert_eq!(Config::::default().max_memory_pages, 16); - - let r = parse_and_prepare_wat(r#"(module (import "env" "memory" (memory 1 1)))"#); - assert_matches!(r, Ok(_)); - - // No memory import - let r = parse_and_prepare_wat(r#"(module)"#); - assert_matches!(r, Ok(_)); - - // initial exceed maximum - let r = parse_and_prepare_wat(r#"(module (import "env" "memory" (memory 16 1)))"#); - assert_matches!(r, Err(Error::Memory)); - - // no maximum - let r = parse_and_prepare_wat(r#"(module (import "env" "memory" (memory 1)))"#); - assert_matches!(r, Err(Error::Memory)); - - // requested maximum exceed configured maximum - let r = parse_and_prepare_wat(r#"(module (import "env" "memory" (memory 1 17)))"#); - assert_matches!(r, Err(Error::Memory)); - } - - #[test] - fn imports() { - // nothing can be imported from non-"env" module for now. - let r = parse_and_prepare_wat(r#"(module (import "another_module" "memory" (memory 1 1)))"#); - assert_matches!(r, Err(Error::Instantiate)); - - let r = parse_and_prepare_wat(r#"(module (import "env" "gas" (func (param i32))))"#); - assert_matches!(r, Ok(_)); - - // wrong signature - let r = parse_and_prepare_wat(r#"(module (import "env" "gas" (func (param i64))))"#); - assert_matches!(r, Err(Error::Instantiate)); - - // unknown function name - let r = parse_and_prepare_wat(r#"(module (import "env" "unknown_func" (func)))"#); - assert_matches!(r, Err(Error::Instantiate)); - } -} diff --git a/substrate/runtime/council/Cargo.toml b/substrate/runtime/council/Cargo.toml deleted file mode 100644 index aa1bbd0bdf079..0000000000000 --- a/substrate/runtime/council/Cargo.toml +++ /dev/null @@ -1,43 +0,0 @@ -[package] -name = "substrate-runtime-council" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -hex-literal = "0.1.0" -integer-sqrt = { git = "https://github.com/paritytech/integer-sqrt-rs.git", branch = "master" } -serde = { version = "1.0", default_features = false } -serde_derive = { version = "1.0", optional = true } -safe-mix = { version = "1.0", default_features = false} -substrate-keyring = { path = "../../keyring", optional = true } -substrate-codec = { path = "../../codec", default_features = false } -substrate-codec-derive = { path = "../../codec/derive", default_features = false } -substrate-primitives = { path = "../../primitives", default_features = false } -substrate-runtime-std = { path = "../../runtime-std", default_features = false } -substrate-runtime-io = { path = "../../runtime-io", default_features = false } -substrate-runtime-support = { path = "../../runtime-support", default_features = false } -substrate-runtime-primitives = { path = "../primitives", default_features = false } -substrate-runtime-consensus = { path = "../consensus", default_features = false } -substrate-runtime-balances = { path = "../balances", default_features = false } -substrate-runtime-democracy = { path = "../democracy", default_features = false } -substrate-runtime-system = { path = "../system", default_features = false } - -[features] -default = ["std"] -std = [ - "serde/std", - "serde_derive", - "safe-mix/std", - "substrate-keyring", - "substrate-codec/std", - "substrate-codec-derive/std", - "substrate-primitives/std", - "substrate-runtime-std/std", - "substrate-runtime-io/std", - "substrate-runtime-support/std", - "substrate-runtime-primitives/std", - "substrate-runtime-consensus/std", - "substrate-runtime-balances/std", - "substrate-runtime-democracy/std", - "substrate-runtime-system/std", -] diff --git a/substrate/runtime/council/src/lib.rs b/substrate/runtime/council/src/lib.rs deleted file mode 100644 index 1c0883877fcba..0000000000000 --- a/substrate/runtime/council/src/lib.rs +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! Council system: Handles the voting in and maintenance of council members. - -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(feature = "std")] -extern crate serde; - -#[cfg(feature = "std")] -#[macro_use] -extern crate serde_derive; - -#[cfg(test)] -#[macro_use] -extern crate hex_literal; - -extern crate integer_sqrt; -extern crate substrate_codec as codec; -#[macro_use] extern crate substrate_codec_derive; -extern crate substrate_primitives; -#[cfg(feature = "std")] extern crate substrate_keyring as keyring; -#[macro_use] extern crate substrate_runtime_std as rstd; -extern crate substrate_runtime_io as runtime_io; -#[macro_use] extern crate substrate_runtime_support; -extern crate substrate_runtime_primitives as primitives; -extern crate substrate_runtime_balances as balances; -extern crate substrate_runtime_democracy as democracy; -extern crate substrate_runtime_system as system; - -#[cfg(feature = "std")] -use rstd::prelude::*; -#[cfg(feature = "std")] -use std::collections::HashMap; -#[cfg(feature = "std")] -use primitives::traits::As; -#[cfg(feature = "std")] -use substrate_runtime_support::StorageValue; - -pub mod voting; -pub mod motions; -pub mod seats; - -pub use seats::{Trait, Module, RawEvent, Event, VoteIndex}; - -#[cfg(feature = "std")] -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -#[serde(deny_unknown_fields)] -pub struct GenesisConfig { - // for the voting onto the council - pub candidacy_bond: T::Balance, - pub voter_bond: T::Balance, - pub present_slash_per_voter: T::Balance, - pub carry_count: u32, - pub active_council: Vec<(T::AccountId, T::BlockNumber)>, - pub approval_voting_period: T::BlockNumber, - pub presentation_duration: T::BlockNumber, - pub desired_seats: u32, - pub term_duration: T::BlockNumber, - pub inactive_grace_period: T::BlockNumber, - - // for the council's votes. - pub cooloff_period: T::BlockNumber, - pub voting_period: T::BlockNumber, -} - -#[cfg(feature = "std")] -impl Default for GenesisConfig { - fn default() -> Self { - GenesisConfig { - candidacy_bond: T::Balance::sa(9), - voter_bond: T::Balance::sa(0), - present_slash_per_voter: T::Balance::sa(1), - carry_count: 2, - inactive_grace_period: T::BlockNumber::sa(1), - active_council: vec![], - approval_voting_period: T::BlockNumber::sa(1000), - presentation_duration: T::BlockNumber::sa(1000), - desired_seats: 0, - term_duration: T::BlockNumber::sa(5), - cooloff_period: T::BlockNumber::sa(1000), - voting_period: T::BlockNumber::sa(3), - } - } -} - -#[cfg(feature = "std")] -impl primitives::BuildStorage for GenesisConfig -{ - fn build_storage(self) -> ::std::result::Result, Vec>, String> { - use codec::Encode; - - Ok(map![ - Self::hash(>::key()).to_vec() => self.candidacy_bond.encode(), - Self::hash(>::key()).to_vec() => self.voter_bond.encode(), - Self::hash(>::key()).to_vec() => self.present_slash_per_voter.encode(), - Self::hash(>::key()).to_vec() => self.carry_count.encode(), - Self::hash(>::key()).to_vec() => self.presentation_duration.encode(), - Self::hash(>::key()).to_vec() => self.approval_voting_period.encode(), - Self::hash(>::key()).to_vec() => self.term_duration.encode(), - Self::hash(>::key()).to_vec() => self.desired_seats.encode(), - Self::hash(>::key()).to_vec() => self.inactive_grace_period.encode(), - Self::hash(>::key()).to_vec() => self.active_council.encode(), - - Self::hash(>::key()).to_vec() => self.cooloff_period.encode(), - Self::hash(>::key()).to_vec() => self.voting_period.encode(), - Self::hash(>::key()).to_vec() => vec![0u8; 0].encode() - ]) - } -} - -#[cfg(test)] -mod tests { - // These re-exports are here for a reason, edit with care - pub use super::*; - pub use runtime_io::with_externalities; - pub use substrate_primitives::H256; - pub use primitives::BuildStorage; - pub use primitives::traits::{BlakeTwo256}; - pub use primitives::testing::{Digest, Header}; - pub use substrate_primitives::Blake2Hasher; - pub use {seats, motions, voting}; - - impl_outer_origin! { - pub enum Origin for Test { - motions - } - } - - impl_outer_event! { - pub enum Event for Test { - balances, democracy, seats, voting, motions - } - } - - impl_outer_dispatch! { - pub enum Call where origin: Origin { - Balances, - Democracy, - } - } - - // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. - #[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] - pub struct Test; - impl system::Trait for Test { - type Origin = Origin; - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type Digest = Digest; - type AccountId = u64; - type Header = Header; - type Event = Event; - } - impl balances::Trait for Test { - type Balance = u64; - type AccountIndex = u64; - type OnFreeBalanceZero = (); - type EnsureAccountLiquid = (); - type Event = Event; - } - impl democracy::Trait for Test { - type Proposal = Call; - type Event = Event; - } - impl seats::Trait for Test { - type Event = Event; - } - impl motions::Trait for Test { - type Origin = Origin; - type Proposal = Call; - type Event = Event; - } - impl voting::Trait for Test { - type Event = Event; - } - - pub fn new_test_ext(with_council: bool) -> runtime_io::TestExternalities { - let mut t = system::GenesisConfig::::default().build_storage().unwrap(); - t.extend(balances::GenesisConfig::{ - balances: vec![(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)], - transaction_base_fee: 0, - transaction_byte_fee: 0, - existential_deposit: 0, - transfer_fee: 0, - creation_fee: 0, - reclaim_rebate: 0, - }.build_storage().unwrap()); - t.extend(democracy::GenesisConfig::{ - launch_period: 1, - voting_period: 3, - minimum_deposit: 1, - }.build_storage().unwrap()); - t.extend(GenesisConfig::{ - candidacy_bond: 9, - voter_bond: 3, - present_slash_per_voter: 1, - carry_count: 2, - inactive_grace_period: 1, - active_council: if with_council { vec![ - (1, 10), - (2, 10), - (3, 10) - ] } else { vec![] }, - approval_voting_period: 4, - presentation_duration: 2, - desired_seats: 2, - term_duration: 5, - cooloff_period: 2, - voting_period: 1, - }.build_storage().unwrap()); - t.into() - } - - pub type System = system::Module; - pub type Balances = balances::Module; - pub type Democracy = democracy::Module; - pub type Council = seats::Module; - pub type CouncilVoting = voting::Module; - pub type CouncilMotions = motions::Module; -} \ No newline at end of file diff --git a/substrate/runtime/council/src/motions.rs b/substrate/runtime/council/src/motions.rs deleted file mode 100644 index b9b26df00f015..0000000000000 --- a/substrate/runtime/council/src/motions.rs +++ /dev/null @@ -1,372 +0,0 @@ -// Copyright 2017 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::*; -use rstd::result; -use substrate_primitives::u32_trait::Value as U32; -use primitives::traits::{Hash, EnsureOrigin, MaybeSerializeDebug, OnFinalise}; -use substrate_runtime_support::dispatch::{Result, Dispatchable, Parameter}; -use substrate_runtime_support::{StorageValue, StorageMap}; -use super::{Trait as CouncilTrait, Module as Council}; -use system::{self, ensure_signed}; - -/// Simple index type for proposal counting. -pub type ProposalIndex = u32; - -pub trait Trait: CouncilTrait + MaybeSerializeDebug { - /// The outer origin type. - type Origin: From; - - /// The outer call dispatch type. - type Proposal: Parameter + Dispatchable::Origin> + MaybeSerializeDebug; - - /// 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 Origin { - /// It has been condoned by a given number of council members. - Members(u32), -} - -/// Event for this module. -decl_event!( - pub enum Event with RawEvent - where ::Hash, ::AccountId - { - /// A motion (given hash) has been proposed (by given account) with a threshold (given u32). - Proposed(AccountId, ProposalIndex, Hash, u32), - /// A motion (given hash) has been voted on by given account, leaving - /// a tally (yes votes and no votes given as u32s respectively). - Voted(AccountId, Hash, bool, u32, u32), - /// 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), - } -); - -decl_module! { - #[cfg_attr(feature = "std", serde(bound(deserialize = "::Proposal: ::serde::de::DeserializeOwned")))] - pub struct Module for enum Call where origin: ::Origin { - fn propose(origin, threshold: u32, proposal: Box<::Proposal>) -> Result; - fn vote(origin, proposal: T::Hash, index: ProposalIndex, approve: bool) -> Result; - } -} - -decl_storage! { - trait Store for Module as CouncilMotions { - /// The (hashes of) the active proposals. - pub Proposals get(proposals): default Vec; - /// Actual proposal for a given hash, if it's current. - pub ProposalOf get(proposal_of): map [ T::Hash => ::Proposal ]; - /// Votes for a given proposal: (required_yes_votes, yes_voters, no_voters). - pub Voting get(voting): map [ T::Hash => (ProposalIndex, u32, Vec, Vec) ]; - /// Proposals so far. - pub ProposalCount get(proposal_count): default u32; - } -} - -impl Module { - - /// Deposit one of this module's events. - fn deposit_event(event: Event) { - >::deposit_event(::Event::from(event).into()); - } - - pub fn is_councillor(who: &T::AccountId) -> bool { - >::active_council().iter() - .any(|&(ref a, _)| a == who) - } - - // Dispatch - fn propose(origin: ::Origin, threshold: u32, proposal: Box<::Proposal>) -> Result { - 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 ok = proposal.dispatch(Origin::Members(1).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); - >::insert(proposal_hash, (index, threshold, vec![who.clone()], vec![])); - - Self::deposit_event(RawEvent::Proposed(who, index, proposal_hash, threshold)); - } - Ok(()) - } - - fn vote(origin: ::Origin, proposal: T::Hash, index: ProposalIndex, approve: bool) -> Result { - 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.0 == index, "mismatched index"); - - let position_yes = voting.2.iter().position(|a| a == &who); - let position_no = voting.3.iter().position(|a| a == &who); - - if approve { - if position_yes.is_none() { - voting.2.push(who.clone()); - } else { - return Err("duplicate vote ignored") - } - if let Some(pos) = position_no { - voting.3.swap_remove(pos); - } - } else { - if position_no.is_none() { - voting.3.push(who.clone()); - } else { - return Err("duplicate vote ignored") - } - if let Some(pos) = position_yes { - voting.2.swap_remove(pos); - } - } - - let yes_votes = voting.2.len() as u32; - let no_votes = voting.3.len() as u32; - Self::deposit_event(RawEvent::Voted(who, proposal, approve, yes_votes, no_votes)); - - let threshold = voting.1; - let potential_votes = >::active_council().len() as u32; - let approved = yes_votes >= threshold; - let disapproved = potential_votes.saturating_sub(no_votes) < threshold; - if approved || disapproved { - if approved { - Self::deposit_event(RawEvent::Approved(proposal)); - - // execute motion, assuming it exists. - if let Some(p) = >::take(&proposal) { - let ok = p.dispatch(Origin::Members(threshold).into()).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); - } - - Ok(()) - } -} - -impl OnFinalise for Module { - fn on_finalise(_n: T::BlockNumber) { - } -} - -/// 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: u32) -> result::Result - where OuterOrigin: Into> -{ - match o.into() { - Some(Origin::Members(x)) if x >= n => Ok(n), - _ => Err("bad origin: expected to be a threshold number of council members"), - } -} - -pub struct EnsureMembers(::rstd::marker::PhantomData); -impl EnsureOrigin for EnsureMembers - where O: Into> -{ - type Success = u32; - fn ensure_origin(o: O) -> result::Result { - ensure_council_members(o, N::VALUE) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use super::RawEvent; - use ::tests::*; - use ::tests::{Call, Origin, Event as OuterEvent}; - use substrate_runtime_support::Hashable; - use system::{EventRecord, Phase}; - - #[test] - fn motions_basic_environment_works() { - with_externalities(&mut new_test_ext(true), || { - 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(balances::address::Address::Id(42), value, 0)) - } - - #[test] - fn motions_propose_works() { - with_externalities(&mut new_test_ext(true), || { - 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((0, 3, vec![1], Vec::::new()))); - - assert_eq!(System::events(), vec![ - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Proposed(1, 0, hex!["a900ca23832b1f42a5d4af5d0ece88da63fbb4049cc00bac3f741eabb5a79c45"].into(), 3)) - } - ]); - }); - } - - #[test] - fn motions_ignoring_non_council_proposals_works() { - with_externalities(&mut new_test_ext(true), || { - 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 new_test_ext(true), || { - 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 new_test_ext(true), || { - 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 new_test_ext(true), || { - 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((0, 2, vec![1], Vec::::new()))); - 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((0, 2, Vec::::new(), 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!["a900ca23832b1f42a5d4af5d0ece88da63fbb4049cc00bac3f741eabb5a79c45"].into(), 2)) - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Voted(1, hex!["a900ca23832b1f42a5d4af5d0ece88da63fbb4049cc00bac3f741eabb5a79c45"].into(), false, 0, 1)) - } - ]); - }); - } - - #[test] - fn motions_disapproval_works() { - with_externalities(&mut new_test_ext(true), || { - 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!["a900ca23832b1f42a5d4af5d0ece88da63fbb4049cc00bac3f741eabb5a79c45"].into(), 3)) - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Voted(2, hex!["a900ca23832b1f42a5d4af5d0ece88da63fbb4049cc00bac3f741eabb5a79c45"].into(), false, 1, 1)) - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Disapproved(hex!["a900ca23832b1f42a5d4af5d0ece88da63fbb4049cc00bac3f741eabb5a79c45"].into())) - } - ]); - }); - } - - #[test] - fn motions_approval_works() { - with_externalities(&mut new_test_ext(true), || { - 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!["a900ca23832b1f42a5d4af5d0ece88da63fbb4049cc00bac3f741eabb5a79c45"].into(), 2)) - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Voted(2, hex!["a900ca23832b1f42a5d4af5d0ece88da63fbb4049cc00bac3f741eabb5a79c45"].into(), true, 2, 0)) - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Approved(hex!["a900ca23832b1f42a5d4af5d0ece88da63fbb4049cc00bac3f741eabb5a79c45"].into())) - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Executed(hex!["a900ca23832b1f42a5d4af5d0ece88da63fbb4049cc00bac3f741eabb5a79c45"].into(), false)) - } - ]); - }); - } -} diff --git a/substrate/runtime/council/src/seats.rs b/substrate/runtime/council/src/seats.rs deleted file mode 100644 index 294035a25a941..0000000000000 --- a/substrate/runtime/council/src/seats.rs +++ /dev/null @@ -1,1355 +0,0 @@ -// Copyright 2017 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, As, Lookup, OnFinalise}; -use runtime_io::print; -use substrate_runtime_support::{StorageValue, StorageMap, dispatch::Result}; -use democracy; -use balances::{self, address::Address}; -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 councilers: - -// 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 councilers will 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 prevent 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. - -pub type VoteIndex = u32; - -pub trait Trait: democracy::Trait { - type Event: From> + Into<::Event>; -} - -decl_module! { - pub struct Module for enum Call where origin: T::Origin { - fn set_approvals(origin, votes: Vec, index: VoteIndex) -> Result; - fn reap_inactive_voter(origin, reporter_index: u32, who: Address, who_index: u32, assumed_vote_index: VoteIndex) -> Result; - fn retract_voter(origin, index: u32) -> Result; - fn submit_candidacy(origin, slot: u32) -> Result; - fn present_winner(origin, candidate: Address, total: T::Balance, index: VoteIndex) -> Result; - - fn set_desired_seats(origin, count: u32) -> Result; - fn remove_member(origin, who: Address) -> Result; - fn set_presentation_duration(origin, count: T::BlockNumber) -> Result; - fn set_term_duration(origin, count: T::BlockNumber) -> Result; - } -} - -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): required T::Balance; - /// How much should be locked up in order to be able to submit votes. - pub VotingBond get(voting_bond): required T::Balance; - /// The punishment, per voter, if you provide an invalid presentation. - pub PresentSlashPerVoter get(present_slash_per_voter): required T::Balance; - /// How many runners-up should have their approvals persist until the next vote. - pub CarryCount get(carry_count): required u32; - /// How long to give each top candidate to present themselves after the vote ends. - pub PresentationDuration get(presentation_duration): required T::BlockNumber; - /// How many votes need to go by after a voter's last vote before they can be reaped if their - /// approvals are moot. - pub InactiveGracePeriod get(inactivity_grace_period): required VoteIndex; - /// How often (in blocks) to check for new votes. - pub VotingPeriod get(voting_period): required T::BlockNumber; - /// How long each position is active for. - pub TermDuration get(term_duration): required T::BlockNumber; - /// Number of accounts that should be sitting on the council. - pub DesiredSeats get(desired_seats): required u32; - - // permanent state (always relevant, changes only at the finalisation of voting) - /// The current council. When there's a vote going on, this should still be used for executive - /// matters. - pub ActiveCouncil get(active_council): default Vec<(T::AccountId, T::BlockNumber)>; - /// The total number of votes that have happened or are in progress. - pub VoteCount get(vote_index): default VoteIndex; - - // persistent state (always relevant, changes constantly) - /// The last cleared vote index that this voter was last active at. - pub ApprovalsOf get(approvals_of): default map [ T::AccountId => 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 => (VoteIndex, u32) ]; - /// The last cleared vote index that this voter was last active at. - pub LastActiveOf get(voter_last_active): map [ T::AccountId => VoteIndex ]; - /// The present voter list. - pub Voters get(voters): default Vec; - /// The present candidate list. - pub Candidates get(candidates): default Vec; // has holes - pub CandidateCount get(candidate_count): default u32; - - // temporary state (only relevant during finalisation/presentation) - /// The accounts holding the seats that will become free on the next tally. - pub NextFinalise get(next_finalise): (T::BlockNumber, u32, Vec); - /// The stakes as they were at the point that the vote ended. - pub SnapshotedStakes get(snapshoted_stakes): required Vec; - /// Get the leaderboard if we;re in the presentation phase. - pub Leaderboard get(leaderboard): Vec<(T::Balance, T::AccountId)>; // ORDERED low -> high - } -} - -decl_event!( - /// An event in this module. - pub enum Event with RawEvent - 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). - TallyFinalised(Vec, Vec), - } -); - -impl Module { - - /// Deposit one of this module's events. - fn deposit_event(event: Event) { - >::deposit_event(::Event::from(event).into()); - } - - // 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) - } - - /// 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_finalise() { - // 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) - } - } - - // dispatch - - /// Set candidate approvals. Approval slots stay valid as long as candidates in those slots - /// are registered. - fn set_approvals(origin: T::Origin, votes: Vec, index: VoteIndex) -> Result { - let who = ensure_signed(origin)?; - - ensure!(!Self::presentation_active(), "no approval changes during presentation period"); - ensure!(index == Self::vote_index(), "incorrect vote index"); - if !>::exists(&who) { - // not yet a voter - deduct bond. - // NOTE: this must be the last potential bailer, since it changes state. - >::reserve(&who, Self::voting_bond())?; - - >::put({ - let mut v = Self::voters(); - v.push(who.clone()); - v - }); - } - >::insert(&who, index); - >::insert(&who, votes); - Ok(()) - } - - /// 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. - /// - /// May be called by anyone. Returns the voter deposit to `signed`. - fn reap_inactive_voter( - origin: T::Origin, - reporter_index: u32, - who: Address, - who_index: u32, - assumed_vote_index: VoteIndex - ) -> Result { - let reporter = ensure_signed(origin)?; - - let who = >::lookup(who)?; - ensure!(!Self::presentation_active(), "cannot reap during presentation period"); - ensure!(Self::voter_last_active(&reporter).is_some(), "reporter must be a voter"); - let last_active = Self::voter_last_active(&who).ok_or("target for inactivity cleanup must be active")?; - ensure!(assumed_vote_index == Self::vote_index(), "vote index not current"); - ensure!(last_active < assumed_vote_index - Self::inactivity_grace_period(), "cannot reap during grace perid"); - let voters = Self::voters(); - let reporter_index = reporter_index as usize; - let who_index = who_index as usize; - ensure!(reporter_index < voters.len() && voters[reporter_index] == reporter, "bad reporter index"); - ensure!(who_index < voters.len() && voters[who_index] == who, "bad target index"); - - // will definitely kill one of signed or who now. - - let valid = !Self::approvals_of(&who).iter() - .zip(Self::candidates().iter()) - .any(|(&appr, addr)| - appr && - *addr != T::AccountId::default() && - Self::candidate_reg_info(addr).map_or(false, |x| x.0 <= last_active)/*defensive only: all items in candidates list are registered*/ - ); - - Self::remove_voter( - if valid { &who } else { &reporter }, - if valid { who_index } else { reporter_index }, - voters - ); - if valid { - // This only fails if `who` 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. - >::repatriate_reserved(&who, &reporter, Self::voting_bond())?; - Self::deposit_event(RawEvent::VoterReaped(who, reporter)); - } else { - >::slash_reserved(&reporter, Self::voting_bond()); - Self::deposit_event(RawEvent::BadReaperSlashed(reporter)); - } - Ok(()) - } - - /// Remove a voter. All votes are cancelled and the voter deposit is returned. - fn retract_voter(origin: T::Origin, index: u32) -> Result { - let who = ensure_signed(origin)?; - - ensure!(!Self::presentation_active(), "cannot retract when presenting"); - ensure!(>::exists(&who), "cannot retract non-voter"); - let voters = Self::voters(); - let index = index as usize; - ensure!(index < voters.len(), "retraction index invalid"); - ensure!(voters[index] == who, "retraction index mismatch"); - - Self::remove_voter(&who, index, voters); - >::unreserve(&who, Self::voting_bond()); - Ok(()) - } - - /// Submit oneself for candidacy. - /// - /// Account must have enough transferrable funds in it to pay the bond. - fn submit_candidacy(origin: T::Origin, slot: u32) -> Result { - 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. - >::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); - Ok(()) - } - - /// 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 - fn present_winner( - origin: T::Origin, - candidate: Address, - total: T::Balance, - index: VoteIndex - ) -> Result { - let who = ensure_signed(origin)?; - - let candidate = >::lookup(candidate)?; - ensure!(index == Self::vote_index(), "index not current"); - let (_, _, expiring) = Self::next_finalise().ok_or("cannot present outside of presentation period")?; - let stakes = Self::snapshoted_stakes(); - let voters = Self::voters(); - let bad_presentation_punishment = Self::present_slash_per_voter() * T::Balance::sa(voters.len() as u64); - ensure!(>::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 (registered_since, candidate_index): (VoteIndex, u32) = - Self::candidate_reg_info(&candidate).ok_or("presented candidate must be current")?; - let actual_total = voters.iter() - .zip(stakes.iter()) - .filter_map(|(voter, stake)| - match Self::voter_last_active(voter) { - Some(b) if b >= registered_since => - Self::approvals_of(voter).get(candidate_index as usize) - .and_then(|approved| if *approved { Some(*stake) } 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 _ = >::slash(&who, bad_presentation_punishment); - 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: T::Origin, count: u32) -> Result { - ensure_root(origin)?; - >::put(count); - Ok(()) - } - - /// Remove a particular member. A tally will happen instantly (if not already in a presentation - /// period) to fill the seat if removal means that the desired members are not met. - /// This is effective immediately. - fn remove_member(origin: T::Origin, who: Address) -> Result { - ensure_root(origin)?; - let who = >::lookup(who)?; - let new_council: Vec<(T::AccountId, T::BlockNumber)> = Self::active_council() - .into_iter() - .filter(|i| i.0 != who) - .collect(); - >::put(new_council); - Ok(()) - } - - /// Set the presentation duration. If there is currently a vote being presented for, will - /// invoke `finalise_vote`. - fn set_presentation_duration(origin: T::Origin, count: T::BlockNumber) -> Result { - ensure_root(origin)?; - >::put(count); - Ok(()) - } - - /// Set the presentation duration. If there is current a vote being presented for, will - /// invoke `finalise_vote`. - fn set_term_duration(origin: T::Origin, count: T::BlockNumber) -> Result { - ensure_root(origin)?; - >::put(count); - Ok(()) - } - - // 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_finalise() { - if block_number == number { - Self::finalise_tally()? - } - } - Ok(()) - } - - /// Remove a voter from the system. Trusts that Self::voters()[index] != voter. - fn remove_voter(voter: &T::AccountId, index: usize, mut voters: Vec) { - >::put({ voters.swap_remove(index); voters }); - >::remove(voter); - >::remove(voter); - } - - /// Close the voting, snapshot the staking and 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::>(); - if active_council.len() - expiring.len() < desired_seats { - let empty_seats = desired_seats - (active_council.len() - expiring.len()); - >::put((number + Self::presentation_duration(), empty_seats as u32, expiring)); - - let voters = Self::voters(); - let votes = voters.iter().map(>::total_balance).collect::>(); - >::put(votes); - - // initialise leaderboard. - let leaderboard_size = empty_seats + Self::carry_count() as usize; - >::put(vec![(T::Balance::zero(), T::AccountId::default()); leaderboard_size]); - - Self::deposit_event(RawEvent::TallyStarted(empty_seats as u32)); - } - } - - /// Finalise 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 finalise_tally() -> Result { - >::kill(); - let (_, coming, expiring): (T::BlockNumber, u32, Vec) = - >::take().ok_or("finalise can only be called after a tally is started.")?; - let leaderboard: Vec<(T::Balance, 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| {>::unreserve(a, candidacy_bond);}) - .collect(); - let active_council = Self::active_council(); - let outgoing = 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); - - // 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::TallyFinalised(incoming, outgoing)); - - >::put(new_candidates); - >::put(count); - >::put(Self::vote_index() + 1); - Ok(()) - } -} - -impl OnFinalise for Module { - fn on_finalise(n: T::BlockNumber) { - if let Err(e) = Self::end_block(n) { - print("Guru meditation"); - print(e); - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use ::tests::*; - - #[test] - fn params_should_work() { - with_externalities(&mut new_test_ext(false), || { - 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(), 9); - assert_eq!(Council::voting_bond(), 3); - assert_eq!(Council::present_slash_per_voter(), 1); - assert_eq!(Council::presentation_duration(), 2); - 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_finalise(), 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(), Vec::::new()); - assert_eq!(Council::voter_last_active(1), None); - assert_eq!(Council::approvals_of(1), vec![]); - }); - } - - #[test] - fn simple_candidate_submission_should_work() { - with_externalities(&mut new_test_ext(false), || { - 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 = new_test_ext(false); - 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() { - with_externalities(&mut new_test_ext_with_candidate_holes(), || { - 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 new_test_ext(false), || { - 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 new_test_ext(false), || { - 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 new_test_ext(false), || { - 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 new_test_ext(false), || { - 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 voting_should_work() { - with_externalities(&mut new_test_ext(false), || { - 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)); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![true], 0)); - - assert_eq!(Council::approvals_of(1), vec![true]); - assert_eq!(Council::approvals_of(4), vec![true]); - assert_eq!(Council::voters(), 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)); - assert_ok!(Council::set_approvals(Origin::signed(3), vec![false, true, true], 0)); - - assert_eq!(Council::approvals_of(1), vec![true]); - assert_eq!(Council::approvals_of(4), vec![true]); - assert_eq!(Council::approvals_of(2), vec![false, true, true]); - assert_eq!(Council::approvals_of(3), vec![false, true, true]); - - assert_eq!(Council::voters(), vec![1, 4, 2, 3]); - }); - } - - #[test] - fn resubmitting_voting_should_work() { - with_externalities(&mut new_test_ext(false), || { - 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)); - - assert_eq!(Council::approvals_of(4), vec![true]); - - 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(4), vec![true, false, true], 0)); - - assert_eq!(Council::approvals_of(4), vec![true, false, true]); - }); - } - - #[test] - fn retracting_voter_should_work() { - with_externalities(&mut new_test_ext(false), || { - 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_ok!(Council::set_approvals(Origin::signed(1), vec![true], 0)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![false, true, true], 0)); - assert_ok!(Council::set_approvals(Origin::signed(3), vec![false, true, true], 0)); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![true, false, true], 0)); - - assert_eq!(Council::voters(), vec![1, 2, 3, 4]); - assert_eq!(Council::approvals_of(1), vec![true]); - assert_eq!(Council::approvals_of(2), vec![false, true, true]); - assert_eq!(Council::approvals_of(3), vec![false, true, true]); - assert_eq!(Council::approvals_of(4), vec![true, false, true]); - - assert_ok!(Council::retract_voter(Origin::signed(1), 0)); - - assert_eq!(Council::voters(), vec![4, 2, 3]); - assert_eq!(Council::approvals_of(1), Vec::::new()); - assert_eq!(Council::approvals_of(2), vec![false, true, true]); - assert_eq!(Council::approvals_of(3), vec![false, true, true]); - assert_eq!(Council::approvals_of(4), vec![true, false, true]); - - assert_ok!(Council::retract_voter(Origin::signed(2), 1)); - - assert_eq!(Council::voters(), vec![4, 3]); - assert_eq!(Council::approvals_of(1), Vec::::new()); - assert_eq!(Council::approvals_of(2), Vec::::new()); - assert_eq!(Council::approvals_of(3), vec![false, true, true]); - assert_eq!(Council::approvals_of(4), vec![true, false, true]); - - assert_ok!(Council::retract_voter(Origin::signed(3), 1)); - - assert_eq!(Council::voters(), vec![4]); - assert_eq!(Council::approvals_of(1), Vec::::new()); - assert_eq!(Council::approvals_of(2), Vec::::new()); - assert_eq!(Council::approvals_of(3), Vec::::new()); - assert_eq!(Council::approvals_of(4), vec![true, false, true]); - }); - } - - #[test] - fn invalid_retraction_index_should_not_work() { - with_externalities(&mut new_test_ext(false), || { - 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)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true], 0)); - assert_eq!(Council::voters(), 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 new_test_ext(false), || { - 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)); - assert_noop!(Council::retract_voter(Origin::signed(1), 1), "retraction index invalid"); - }); - } - - #[test] - fn non_voter_retraction_should_not_work() { - with_externalities(&mut new_test_ext(false), || { - 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)); - assert_noop!(Council::retract_voter(Origin::signed(2), 0), "cannot retract non-voter"); - }); - } - - #[test] - fn simple_tally_should_work() { - with_externalities(&mut new_test_ext(false), || { - 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, false], 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, true], 0)); - assert_eq!(Council::voters(), vec![2, 5]); - assert_eq!(Council::approvals_of(2), vec![true, false]); - assert_eq!(Council::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.into(), 20, 0), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(4), 5.into(), 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_last_active(2), Some(0)); - assert_eq!(Council::voter_last_active(5), Some(0)); - }); - } - - #[test] - fn double_presentations_should_be_punished() { - with_externalities(&mut new_test_ext(false), || { - 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)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, true], 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert_ok!(Council::present_winner(Origin::signed(4), 2.into(), 20, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 5.into(), 50, 0)); - assert_eq!(Council::present_winner(Origin::signed(4), 5.into(), 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 new_test_ext(false), || { - 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)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert_ok!(Council::present_winner(Origin::signed(4), 2.into(), 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)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(10); - assert_ok!(Council::present_winner(Origin::signed(4), 5.into(), 50, 1)); - assert_ok!(Council::end_block(System::block_number())); - - assert_ok!(Council::reap_inactive_voter(Origin::signed(5), - Council::voters().iter().position(|&i| i == 5).unwrap() as u32, - 2.into(), Council::voters().iter().position(|&i| i == 2).unwrap() as u32, - 2 - )); - - assert_eq!(Council::voters(), vec![5]); - assert_eq!(Council::approvals_of(2).len(), 0); - assert_eq!(Balances::total_balance(&2), 17); - assert_eq!(Balances::total_balance(&5), 53); - }); - } - - #[test] - fn presenting_for_double_election_should_not_work() { - with_externalities(&mut new_test_ext(false), || { - 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)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert_ok!(Council::present_winner(Origin::signed(4), 2.into(), 20, 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(8); - assert_eq!(Council::submit_candidacy(Origin::signed(2), 0), Ok(())); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true], 1)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(10); - assert_noop!(Council::present_winner(Origin::signed(4), 2.into(), 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 new_test_ext(false), || { - 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)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert_ok!(Council::present_winner(Origin::signed(4), 2.into(), 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)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(10); - assert_ok!(Council::present_winner(Origin::signed(4), 5.into(), 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), - Council::voters().iter().position(|&i| i == 5).unwrap() as u32, - 2.into(), Council::voters().iter().position(|&i| i == 2).unwrap() as u32, - 2 - )); - - assert_eq!(Council::voters(), vec![5]); - assert_eq!(Council::approvals_of(2).len(), 0); - assert_eq!(Balances::total_balance(&2), 17); - assert_eq!(Balances::total_balance(&5), 53); - }); - } - - #[test] - fn retracting_inactive_voter_with_bad_reporter_index_should_not_work() { - with_externalities(&mut new_test_ext(false), || { - 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)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert_ok!(Council::present_winner(Origin::signed(4), 2.into(), 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)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(10); - assert_ok!(Council::present_winner(Origin::signed(4), 5.into(), 50, 1)); - assert_ok!(Council::end_block(System::block_number())); - - assert_noop!(Council::reap_inactive_voter(Origin::signed(2), - 42, - 2.into(), Council::voters().iter().position(|&i| i == 2).unwrap() as u32, - 2 - ), "bad reporter index"); - }); - } - - #[test] - fn retracting_inactive_voter_with_bad_target_index_should_not_work() { - with_externalities(&mut new_test_ext(false), || { - 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)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert_ok!(Council::present_winner(Origin::signed(4), 2.into(), 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)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(10); - assert_ok!(Council::present_winner(Origin::signed(4), 5.into(), 50, 1)); - assert_ok!(Council::end_block(System::block_number())); - - assert_noop!(Council::reap_inactive_voter(Origin::signed(2), - Council::voters().iter().position(|&i| i == 2).unwrap() as u32, - 2.into(), 42, - 2 - ), "bad target index"); - }); - } - - #[test] - fn attempting_to_retract_active_voter_should_slash_reporter() { - with_externalities(&mut new_test_ext(false), || { - 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)); - assert_ok!(Council::set_approvals(Origin::signed(3), vec![false, true, false, false], 0)); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![false, false, true, false], 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, false, false, true], 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert_ok!(Council::present_winner(Origin::signed(4), 2.into(), 20, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 3.into(), 30, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 4.into(), 40, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 5.into(), 50, 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(8); - assert_ok!(Council::set_desired_seats(Origin::ROOT, 3)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(10); - assert_ok!(Council::present_winner(Origin::signed(4), 2.into(), 20, 1)); - assert_ok!(Council::present_winner(Origin::signed(4), 3.into(), 30, 1)); - assert_ok!(Council::end_block(System::block_number())); - - assert_ok!(Council::reap_inactive_voter(Origin::signed(4), - Council::voters().iter().position(|&i| i == 4).unwrap() as u32, - 2.into(), Council::voters().iter().position(|&i| i == 2).unwrap() as u32, - 2 - )); - - assert_eq!(Council::voters(), vec![2, 3, 5]); - assert_eq!(Council::approvals_of(4).len(), 0); - assert_eq!(Balances::total_balance(&4), 37); - }); - } - - #[test] - fn attempting_to_retract_inactive_voter_by_nonvoter_should_not_work() { - with_externalities(&mut new_test_ext(false), || { - 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)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert_ok!(Council::present_winner(Origin::signed(4), 2.into(), 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)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(10); - assert_ok!(Council::present_winner(Origin::signed(4), 5.into(), 50, 1)); - assert_ok!(Council::end_block(System::block_number())); - - assert_noop!(Council::reap_inactive_voter(Origin::signed(4), - 0, - 2.into(), Council::voters().iter().position(|&i| i == 2).unwrap() as u32, - 2 - ), "reporter must be a voter"); - }); - } - - #[test] - fn presenting_loser_should_not_work() { - with_externalities(&mut new_test_ext(false), || { - 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)); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 1)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![false, true], 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 2)); - assert_ok!(Council::set_approvals(Origin::signed(3), vec![false, false, true], 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(4), 3)); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![false, false, false, true], 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)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert_ok!(Council::present_winner(Origin::signed(4), 1.into(), 60, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 3.into(), 30, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 4.into(), 40, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 5.into(), 50, 0)); - assert_noop!(Council::present_winner(Origin::signed(4), 2.into(), 20, 0), "candidate not worthy of leaderboard"); - }); - } - - #[test] - fn presenting_loser_first_should_not_matter() { - with_externalities(&mut new_test_ext(false), || { - 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)); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 1)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![false, true], 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 2)); - assert_ok!(Council::set_approvals(Origin::signed(3), vec![false, false, true], 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(4), 3)); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![false, false, false, true], 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)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert_ok!(Council::present_winner(Origin::signed(4), 2.into(), 20, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 1.into(), 60, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 3.into(), 30, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 4.into(), 40, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 5.into(), 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 new_test_ext(false), || { - System::set_block_number(4); - assert!(!Council::presentation_active()); - assert_noop!(Council::present_winner(Origin::signed(5), 5.into(), 1, 0), "cannot present outside of presentation period"); - }); - } - - #[test] - fn present_with_invalid_vote_index_should_not_work() { - with_externalities(&mut new_test_ext(false), || { - 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)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, true], 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert_noop!(Council::present_winner(Origin::signed(4), 2.into(), 20, 1), "index not current"); - }); - } - - #[test] - fn present_when_presenter_is_poor_should_not_work() { - with_externalities(&mut new_test_ext(false), || { - System::set_block_number(4); - assert!(!Council::presentation_active()); - - assert_ok!(Council::submit_candidacy(Origin::signed(1), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 1)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true, false], 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, true], 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert_eq!(Balances::free_balance(&1), 1); - assert_eq!(Balances::reserved_balance(&1), 9); - assert_noop!(Council::present_winner(Origin::signed(1), 1.into(), 20, 0), "presenter must have sufficient slashable funds"); - }); - } - - #[test] - fn invalid_present_tally_should_slash() { - with_externalities(&mut new_test_ext(false), || { - 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)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, true], 0)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert_err!(Council::present_winner(Origin::signed(4), 2.into(), 80, 0), "incorrect total"); - - assert_eq!(Balances::total_balance(&4), 38); - }); - } - - #[test] - fn runners_up_should_be_kept() { - with_externalities(&mut new_test_ext(false), || { - 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)); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 1)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![false, true], 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 2)); - assert_ok!(Council::set_approvals(Origin::signed(3), vec![false, false, true], 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(4), 3)); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![false, false, false, true], 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)); - - 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.into(), 60, 0)); - assert_eq!(Council::leaderboard(), Some(vec![ - (0, 0), - (0, 0), - (0, 0), - (60, 1) - ])); - assert_ok!(Council::present_winner(Origin::signed(4), 3.into(), 30, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 4.into(), 40, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 5.into(), 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_last_active(2), Some(0)); - assert_eq!(Council::voter_last_active(3), Some(0)); - assert_eq!(Council::voter_last_active(4), Some(0)); - assert_eq!(Council::voter_last_active(5), Some(0)); - assert_eq!(Council::voter_last_active(6), Some(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 new_test_ext(false), || { - 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)); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 1)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![false, true], 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 2)); - assert_ok!(Council::set_approvals(Origin::signed(3), vec![false, false, true], 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(4), 3)); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![false, false, false, true], 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)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(6); - assert_ok!(Council::present_winner(Origin::signed(4), 1.into(), 60, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 3.into(), 30, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 4.into(), 40, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 5.into(), 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)); - assert_ok!(Council::set_desired_seats(Origin::ROOT, 3)); - assert_ok!(Council::end_block(System::block_number())); - - System::set_block_number(10); - assert_ok!(Council::present_winner(Origin::signed(4), 3.into(), 90, 1)); - assert_ok!(Council::present_winner(Origin::signed(4), 4.into(), 40, 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_last_active(2), Some(0)); - assert_eq!(Council::voter_last_active(3), Some(0)); - assert_eq!(Council::voter_last_active(4), Some(0)); - assert_eq!(Council::voter_last_active(5), Some(0)); - assert_eq!(Council::voter_last_active(6), Some(1)); - - assert_eq!(Council::candidate_reg_info(4), Some((0, 3))); - }); - } -} diff --git a/substrate/runtime/council/src/voting.rs b/substrate/runtime/council/src/voting.rs deleted file mode 100644 index 450eb034f12cc..0000000000000 --- a/substrate/runtime/council/src/voting.rs +++ /dev/null @@ -1,505 +0,0 @@ -// Copyright 2017 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::*; -use rstd::borrow::Borrow; -use primitives::traits::{OnFinalise, Hash}; -use runtime_io::print; -use substrate_runtime_support::dispatch::Result; -use substrate_runtime_support::{StorageValue, StorageMap, IsSubType}; -use {system, democracy}; -use super::{Trait as CouncilTrait, Module as Council}; -use system::{ensure_signed, ensure_root}; - -pub trait Trait: CouncilTrait { - type Event: From> + Into<::Event>; -} - -decl_module! { - pub struct Module for enum Call where origin: T::Origin { - fn propose(origin, proposal: Box) -> Result; - fn vote(origin, proposal: T::Hash, approve: bool) -> Result; - fn veto(origin, proposal_hash: T::Hash) -> Result; - - fn set_cooloff_period(origin, blocks: T::BlockNumber) -> Result; - fn set_voting_period(origin, blocks: T::BlockNumber) -> Result; - } -} - -decl_storage! { - trait Store for Module as CouncilVoting { - pub CooloffPeriod get(cooloff_period): required T::BlockNumber; - pub VotingPeriod get(voting_period): required T::BlockNumber; - pub Proposals get(proposals): required Vec<(T::BlockNumber, T::Hash)>; // ordered by expiry. - pub ProposalOf get(proposal_of): map [ T::Hash => T::Proposal ]; - pub ProposalVoters get(proposal_voters): default map [ T::Hash => Vec ]; - pub CouncilVoteOf get(vote_of): map [ (T::Hash, T::AccountId) => bool ]; - pub VetoedProposal get(veto_of): map [ T::Hash => (T::BlockNumber, Vec) ]; - } -} - -/// An event in this module. -decl_event!( - pub enum Event with RawEvent - where ::Hash - { - /// A voting tally has happened for a referendum cancelation vote. - /// Last three are yes, no, abstain counts. - TallyCancelation(Hash, u32, u32, u32), - /// A voting tally has happened for a referendum vote. - /// Last three are yes, no, abstain counts. - TallyReferendum(Hash, u32, u32, u32), - } -); - -impl Module { - - /// Deposit one of this module's events. - fn deposit_event(event: Event) { - >::deposit_event(::Event::from(event).into()); - } - - pub fn is_vetoed>(proposal: B) -> bool { - Self::veto_of(proposal.borrow()) - .map(|(expiry, _): (T::BlockNumber, Vec)| >::block_number() < expiry) - .unwrap_or(false) - } - - pub fn will_still_be_councillor_at(who: &T::AccountId, n: T::BlockNumber) -> bool { - >::active_council().iter() - .find(|&&(ref a, _)| a == who) - .map(|&(_, expires)| expires > n) - .unwrap_or(false) - } - - pub fn is_councillor(who: &T::AccountId) -> bool { - >::active_council().iter() - .any(|&(ref a, _)| a == who) - } - - pub fn tally(proposal_hash: &T::Hash) -> (u32, u32, u32) { - Self::generic_tally(proposal_hash, |w: &T::AccountId, p: &T::Hash| Self::vote_of((*p, w.clone()))) - } - - // Dispatch - fn propose(origin: T::Origin, proposal: Box) -> Result { - let who = ensure_signed(origin)?; - - let expiry = >::block_number() + Self::voting_period(); - ensure!(Self::will_still_be_councillor_at(&who, expiry), "proposer would not be on council"); - - let proposal_hash = T::Hashing::hash_of(&proposal); - - ensure!(!>::exists(proposal_hash), "duplicate proposals not allowed"); - ensure!(!Self::is_vetoed(&proposal_hash), "proposal is vetoed"); - - let mut proposals = Self::proposals(); - proposals.push((expiry, proposal_hash)); - proposals.sort_by_key(|&(expiry, _)| expiry); - Self::set_proposals(&proposals); - - >::insert(proposal_hash, *proposal); - >::insert(proposal_hash, vec![who.clone()]); - >::insert((proposal_hash, who.clone()), true); - - Ok(()) - } - - fn vote(origin: T::Origin, proposal: T::Hash, approve: bool) -> Result { - let who = ensure_signed(origin)?; - - ensure!(Self::is_councillor(&who), "only councillors may vote on council proposals"); - - if Self::vote_of((proposal, who.clone())).is_none() { - >::mutate(proposal, |voters| voters.push(who.clone())); - } - >::insert((proposal, who), approve); - Ok(()) - } - - fn veto(origin: T::Origin, proposal_hash: T::Hash) -> Result { - let who = ensure_signed(origin)?; - - ensure!(Self::is_councillor(&who), "only councillors may veto council proposals"); - ensure!(>::exists(&proposal_hash), "proposal must exist to be vetoed"); - - let mut existing_vetoers = Self::veto_of(&proposal_hash) - .map(|pair| pair.1) - .unwrap_or_else(Vec::new); - let insert_position = existing_vetoers.binary_search(&who) - .err().ok_or("a councillor may not veto a proposal twice")?; - existing_vetoers.insert(insert_position, who); - Self::set_veto_of(&proposal_hash, >::block_number() + Self::cooloff_period(), existing_vetoers); - - Self::set_proposals(&Self::proposals().into_iter().filter(|&(_, h)| h != proposal_hash).collect::>()); - >::remove(proposal_hash); - >::remove(proposal_hash); - for (c, _) in >::active_council() { - >::remove((proposal_hash, c)); - } - Ok(()) - } - - fn set_cooloff_period(origin: T::Origin, blocks: T::BlockNumber) -> Result { - ensure_root(origin)?; - >::put(blocks); - Ok(()) - } - - fn set_voting_period(origin: T::Origin, blocks: T::BlockNumber) -> Result { - ensure_root(origin)?; - >::put(blocks); - Ok(()) - } - - // private - - - fn set_veto_of(proposal: &T::Hash, expiry: T::BlockNumber, vetoers: Vec) { - >::insert(proposal, (expiry, vetoers)); - } - - fn kill_veto_of(proposal: &T::Hash) { - >::remove(proposal); - } - - fn take_tally(proposal_hash: &T::Hash) -> (u32, u32, u32) { - Self::generic_tally(proposal_hash, |w: &T::AccountId, p: &T::Hash| >::take((*p, w.clone()))) - } - - fn generic_tally Option>(proposal_hash: &T::Hash, vote_of: F) -> (u32, u32, u32) { - let c = >::active_council(); - let (approve, reject) = c.iter() - .filter_map(|&(ref a, _)| vote_of(a, proposal_hash)) - .map(|approve| if approve { (1, 0) } else { (0, 1) }) - .fold((0, 0), |(a, b), (c, d)| (a + c, b + d)); - (approve, reject, c.len() as u32 - approve - reject) - } - - fn set_proposals(p: &Vec<(T::BlockNumber, T::Hash)>) { - >::put(p); - } - - fn take_proposal_if_expiring_at(n: T::BlockNumber) -> Option<(T::Proposal, T::Hash)> { - let proposals = Self::proposals(); - match proposals.first() { - Some(&(expiry, hash)) if expiry == n => { - // yes this is horrible, but fixing it will need substantial work in storage. - Self::set_proposals(&proposals[1..].to_vec()); - >::take(hash).map(|p| (p, hash)) /* defensive only: all queued proposal hashes must have associated proposals*/ - } - _ => None, - } - } - - fn end_block(now: T::BlockNumber) -> Result { - while let Some((proposal, proposal_hash)) = Self::take_proposal_if_expiring_at(now) { - let tally = Self::take_tally(&proposal_hash); - if let Some(&democracy::Call::cancel_referendum(ref_index)) = IsSubType::>::is_aux_sub_type(&proposal) { - Self::deposit_event(RawEvent::TallyCancelation(proposal_hash, tally.0, tally.1, tally.2)); - if let (_, 0, 0) = tally { - >::internal_cancel_referendum(ref_index); - } - } else { - Self::deposit_event(RawEvent::TallyReferendum(proposal_hash.clone(), tally.0, tally.1, tally.2)); - if tally.0 > tally.1 + tally.2 { - Self::kill_veto_of(&proposal_hash); - match tally { - (_, 0, 0) => >::internal_start_referendum(proposal, democracy::VoteThreshold::SuperMajorityAgainst).map(|_| ())?, - _ => >::internal_start_referendum(proposal, democracy::VoteThreshold::SimpleMajority).map(|_| ())?, - }; - } - } - } - Ok(()) - } -} - -impl OnFinalise for Module { - fn on_finalise(n: T::BlockNumber) { - if let Err(e) = Self::end_block(n) { - print("Guru meditation"); - print(e); - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use ::tests::*; - use ::tests::{Call, Origin}; - use substrate_runtime_support::Hashable; - use democracy::VoteThreshold; - - #[test] - fn basic_environment_works() { - with_externalities(&mut new_test_ext(true), || { - System::set_block_number(1); - assert_eq!(Balances::free_balance(&42), 0); - assert_eq!(CouncilVoting::cooloff_period(), 2); - assert_eq!(CouncilVoting::voting_period(), 1); - assert_eq!(CouncilVoting::will_still_be_councillor_at(&1, 1), true); - assert_eq!(CouncilVoting::will_still_be_councillor_at(&1, 10), false); - assert_eq!(CouncilVoting::will_still_be_councillor_at(&4, 10), false); - assert_eq!(CouncilVoting::is_councillor(&1), true); - assert_eq!(CouncilVoting::is_councillor(&4), false); - assert_eq!(CouncilVoting::proposals(), Vec::<(u64, H256)>::new()); - assert_eq!(CouncilVoting::proposal_voters(H256::default()), Vec::::new()); - assert_eq!(CouncilVoting::is_vetoed(&H256::default()), false); - assert_eq!(CouncilVoting::vote_of((H256::default(), 1)), None); - assert_eq!(CouncilVoting::tally(&H256::default()), (0, 0, 3)); - }); - } - - fn set_balance_proposal(value: u64) -> Call { - Call::Balances(balances::Call::set_balance(balances::address::Address::Id(42), value, 0)) - } - - fn cancel_referendum_proposal(id: u32) -> Call { - Call::Democracy(democracy::Call::cancel_referendum(id)) - } - - #[test] - fn referendum_cancellation_should_work_when_unanimous() { - with_externalities(&mut new_test_ext(true), || { - System::set_block_number(1); - let proposal = set_balance_proposal(42); - assert_ok!(Democracy::internal_start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove), 0); - assert_eq!(Democracy::active_referendums(), vec![(0, 4, proposal, VoteThreshold::SuperMajorityApprove)]); - - let cancellation = cancel_referendum_proposal(0); - let hash = cancellation.blake2_256().into(); - assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(cancellation))); - assert_ok!(CouncilVoting::vote(Origin::signed(2), hash, true)); - assert_ok!(CouncilVoting::vote(Origin::signed(3), hash, true)); - assert_eq!(CouncilVoting::proposals(), vec![(2, hash)]); - assert_ok!(CouncilVoting::end_block(System::block_number())); - - System::set_block_number(2); - assert_ok!(CouncilVoting::end_block(System::block_number())); - assert_eq!(Democracy::active_referendums(), vec![]); - assert_eq!(Balances::free_balance(&42), 0); - }); - } - - #[test] - fn referendum_cancellation_should_fail_when_not_unanimous() { - with_externalities(&mut new_test_ext(true), || { - System::set_block_number(1); - let proposal = set_balance_proposal(42); - assert_ok!(Democracy::internal_start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove), 0); - - let cancellation = cancel_referendum_proposal(0); - let hash = cancellation.blake2_256().into(); - assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(cancellation))); - assert_ok!(CouncilVoting::vote(Origin::signed(2), hash, true)); - assert_ok!(CouncilVoting::vote(Origin::signed(3), hash, false)); - assert_ok!(CouncilVoting::end_block(System::block_number())); - - System::set_block_number(2); - assert_ok!(CouncilVoting::end_block(System::block_number())); - assert_eq!(Democracy::active_referendums(), vec![(0, 4, proposal, VoteThreshold::SuperMajorityApprove)]); - }); - } - - #[test] - fn referendum_cancellation_should_fail_when_abstentions() { - with_externalities(&mut new_test_ext(true), || { - System::set_block_number(1); - let proposal = set_balance_proposal(42); - assert_ok!(Democracy::internal_start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove), 0); - - let cancellation = cancel_referendum_proposal(0); - let hash = cancellation.blake2_256().into(); - assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(cancellation))); - assert_ok!(CouncilVoting::vote(Origin::signed(2), hash, true)); - assert_ok!(CouncilVoting::end_block(System::block_number())); - - System::set_block_number(2); - assert_ok!(CouncilVoting::end_block(System::block_number())); - assert_eq!(Democracy::active_referendums(), vec![(0, 4, proposal, VoteThreshold::SuperMajorityApprove)]); - }); - } - - #[test] - fn veto_should_work() { - with_externalities(&mut new_test_ext(true), || { - System::set_block_number(1); - let proposal = set_balance_proposal(42); - let hash = proposal.blake2_256().into(); - assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone()))); - assert_ok!(CouncilVoting::veto(Origin::signed(2), hash)); - assert_eq!(CouncilVoting::proposals().len(), 0); - assert_eq!(Democracy::active_referendums().len(), 0); - }); - } - - #[test] - fn double_veto_should_not_work() { - with_externalities(&mut new_test_ext(true), || { - System::set_block_number(1); - let proposal = set_balance_proposal(42); - let hash = proposal.blake2_256().into(); - assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone()))); - assert_ok!(CouncilVoting::veto(Origin::signed(2), hash)); - - System::set_block_number(3); - assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone()))); - assert_noop!(CouncilVoting::veto(Origin::signed(2), hash), "a councillor may not veto a proposal twice"); - }); - } - - #[test] - fn retry_in_cooloff_should_not_work() { - with_externalities(&mut new_test_ext(true), || { - System::set_block_number(1); - let proposal = set_balance_proposal(42); - let hash = proposal.blake2_256().into(); - assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone()))); - assert_ok!(CouncilVoting::veto(Origin::signed(2), hash)); - - System::set_block_number(2); - assert_noop!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone())), "proposal is vetoed"); - }); - } - - #[test] - fn retry_after_cooloff_should_work() { - with_externalities(&mut new_test_ext(true), || { - System::set_block_number(1); - let proposal = set_balance_proposal(42); - let hash = proposal.blake2_256().into(); - assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone()))); - assert_ok!(CouncilVoting::veto(Origin::signed(2), hash)); - - System::set_block_number(3); - assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone()))); - assert_ok!(CouncilVoting::vote(Origin::signed(2), hash, false)); - assert_ok!(CouncilVoting::vote(Origin::signed(3), hash, true)); - assert_ok!(CouncilVoting::end_block(System::block_number())); - - System::set_block_number(4); - assert_ok!(CouncilVoting::end_block(System::block_number())); - assert_eq!(CouncilVoting::proposals().len(), 0); - assert_eq!(Democracy::active_referendums(), vec![(0, 7, set_balance_proposal(42), VoteThreshold::SimpleMajority)]); - }); - } - - #[test] - fn alternative_double_veto_should_work() { - with_externalities(&mut new_test_ext(true), || { - System::set_block_number(1); - let proposal = set_balance_proposal(42); - let hash = proposal.blake2_256().into(); - assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone()))); - assert_ok!(CouncilVoting::veto(Origin::signed(2), hash)); - - System::set_block_number(3); - assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone()))); - assert_ok!(CouncilVoting::veto(Origin::signed(3), hash)); - assert_eq!(CouncilVoting::proposals().len(), 0); - assert_eq!(Democracy::active_referendums().len(), 0); - }); - } - - #[test] - fn simple_propose_should_work() { - with_externalities(&mut new_test_ext(true), || { - System::set_block_number(1); - let proposal = set_balance_proposal(42); - let hash = proposal.blake2_256().into(); - assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone()))); - assert_eq!(CouncilVoting::proposals().len(), 1); - assert_eq!(CouncilVoting::proposal_voters(&hash), vec![1]); - assert_eq!(CouncilVoting::vote_of((hash, 1)), Some(true)); - assert_eq!(CouncilVoting::tally(&hash), (1, 0, 2)); - }); - } - - #[test] - fn unvoted_proposal_should_expire_without_action() { - with_externalities(&mut new_test_ext(true), || { - System::set_block_number(1); - let proposal = set_balance_proposal(42); - assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone()))); - assert_eq!(CouncilVoting::tally(&proposal.blake2_256().into()), (1, 0, 2)); - assert_ok!(CouncilVoting::end_block(System::block_number())); - - System::set_block_number(2); - assert_ok!(CouncilVoting::end_block(System::block_number())); - assert_eq!(CouncilVoting::proposals().len(), 0); - assert_eq!(Democracy::active_referendums().len(), 0); - }); - } - - #[test] - fn unanimous_proposal_should_expire_with_biased_referendum() { - with_externalities(&mut new_test_ext(true), || { - System::set_block_number(1); - let proposal = set_balance_proposal(42); - assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone()))); - assert_ok!(CouncilVoting::vote(Origin::signed(2), proposal.blake2_256().into(), true)); - assert_ok!(CouncilVoting::vote(Origin::signed(3), proposal.blake2_256().into(), true)); - assert_eq!(CouncilVoting::tally(&proposal.blake2_256().into()), (3, 0, 0)); - assert_ok!(CouncilVoting::end_block(System::block_number())); - - System::set_block_number(2); - assert_ok!(CouncilVoting::end_block(System::block_number())); - assert_eq!(CouncilVoting::proposals().len(), 0); - assert_eq!(Democracy::active_referendums(), vec![(0, 5, proposal, VoteThreshold::SuperMajorityAgainst)]); - }); - } - - #[test] - fn majority_proposal_should_expire_with_unbiased_referendum() { - with_externalities(&mut new_test_ext(true), || { - System::set_block_number(1); - let proposal = set_balance_proposal(42); - assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone()))); - assert_ok!(CouncilVoting::vote(Origin::signed(2), proposal.blake2_256().into(), true)); - assert_ok!(CouncilVoting::vote(Origin::signed(3), proposal.blake2_256().into(), false)); - assert_eq!(CouncilVoting::tally(&proposal.blake2_256().into()), (2, 1, 0)); - assert_ok!(CouncilVoting::end_block(System::block_number())); - - System::set_block_number(2); - assert_ok!(CouncilVoting::end_block(System::block_number())); - assert_eq!(CouncilVoting::proposals().len(), 0); - assert_eq!(Democracy::active_referendums(), vec![(0, 5, proposal, VoteThreshold::SimpleMajority)]); - }); - } - - #[test] - fn propose_by_public_should_not_work() { - with_externalities(&mut new_test_ext(true), || { - System::set_block_number(1); - let proposal = set_balance_proposal(42); - assert_noop!(CouncilVoting::propose(Origin::signed(4), Box::new(proposal)), "proposer would not be on council"); - }); - } - - #[test] - fn vote_by_public_should_not_work() { - with_externalities(&mut new_test_ext(true), || { - System::set_block_number(1); - let proposal = set_balance_proposal(42); - assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone()))); - assert_noop!(CouncilVoting::vote(Origin::signed(4), proposal.blake2_256().into(), true), "only councillors may vote on council proposals"); - }); - } -} diff --git a/substrate/runtime/democracy/Cargo.toml b/substrate/runtime/democracy/Cargo.toml deleted file mode 100644 index 4667e22be3781..0000000000000 --- a/substrate/runtime/democracy/Cargo.toml +++ /dev/null @@ -1,37 +0,0 @@ -[package] -name = "substrate-runtime-democracy" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -hex-literal = "0.1.0" -serde = { version = "1.0", default_features = false } -serde_derive = { version = "1.0", optional = true } -safe-mix = { version = "1.0", default_features = false} -substrate-codec = { path = "../../codec", default_features = false } -substrate-codec-derive = { path = "../../codec/derive", default_features = false } -substrate-primitives = { path = "../../primitives", default_features = false } -substrate-runtime-std = { path = "../../runtime-std", default_features = false } -substrate-runtime-io = { path = "../../runtime-io", default_features = false } -substrate-runtime-support = { path = "../../runtime-support", default_features = false } -substrate-runtime-primitives = { path = "../primitives", default_features = false } -substrate-runtime-balances = { path = "../balances", default_features = false } -substrate-runtime-consensus = { path = "../consensus", default_features = false } -substrate-runtime-system = { path = "../system", default_features = false } - -[features] -default = ["std"] -std = [ - "serde/std", - "serde_derive", - "safe-mix/std", - "substrate-codec/std", - "substrate-primitives/std", - "substrate-runtime-std/std", - "substrate-runtime-io/std", - "substrate-runtime-support/std", - "substrate-runtime-primitives/std", - "substrate-runtime-consensus/std", - "substrate-runtime-balances/std", - "substrate-runtime-system/std", -] diff --git a/substrate/runtime/democracy/src/lib.rs b/substrate/runtime/democracy/src/lib.rs deleted file mode 100644 index 12f2e7e7aa402..0000000000000 --- a/substrate/runtime/democracy/src/lib.rs +++ /dev/null @@ -1,681 +0,0 @@ -// Copyright 2017 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 . - -//! Democratic system: Handles administration of general stakeholder voting. - -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(feature = "std")] -extern crate serde; - -#[cfg(feature = "std")] -extern crate substrate_primitives; - -#[cfg(feature = "std")] -#[macro_use] -extern crate serde_derive; - -#[macro_use] -extern crate substrate_codec_derive; -#[macro_use] -extern crate substrate_runtime_std as rstd; -#[macro_use] -extern crate substrate_runtime_support; - -extern crate substrate_codec as codec; -extern crate substrate_runtime_io as runtime_io; -extern crate substrate_runtime_primitives as primitives; -extern crate substrate_runtime_balances as balances; -extern crate substrate_runtime_system as system; - -use rstd::prelude::*; -use rstd::result; -use primitives::traits::{Zero, OnFinalise, As, MaybeSerializeDebug}; -use substrate_runtime_support::{StorageValue, StorageMap, Parameter, Dispatchable, IsSubType}; -use substrate_runtime_support::dispatch::Result; -use system::{ensure_signed, ensure_root}; - -#[cfg(any(feature = "std", test))] -use std::collections::HashMap; - -mod vote_threshold; -pub use vote_threshold::{Approved, VoteThreshold}; - -/// A proposal index. -pub type PropIndex = u32; -/// A referendum index. -pub type ReferendumIndex = u32; - -pub trait Trait: balances::Trait + Sized { - type Proposal: Parameter + Dispatchable + IsSubType> + MaybeSerializeDebug; - - type Event: From> + Into<::Event>; -} - -decl_module! { - pub struct Module for enum Call where origin: T::Origin { - fn propose(origin, proposal: Box, value: T::Balance) -> Result; - fn second(origin, proposal: PropIndex) -> Result; - fn vote(origin, ref_index: ReferendumIndex, approve_proposal: bool) -> Result; - - fn start_referendum(origin, proposal: Box, vote_threshold: VoteThreshold) -> Result; - fn cancel_referendum(origin, ref_index: ReferendumIndex) -> Result; - } -} - -decl_storage! { - trait Store for Module as Democracy { - - /// The number of (public) proposals that have been made so far. - pub PublicPropCount get(public_prop_count): default PropIndex; - /// The public proposals. Unsorted. - pub PublicProps get(public_props): default Vec<(PropIndex, T::Proposal, T::AccountId)>; - /// Those who have locked a deposit. - pub DepositOf get(deposit_of): map [ PropIndex => (T::Balance, Vec) ]; - /// How often (in blocks) new public referenda are launched. - pub LaunchPeriod get(launch_period): required T::BlockNumber; - /// The minimum amount to be used as a deposit for a public referendum proposal. - pub MinimumDeposit get(minimum_deposit): required T::Balance; - - /// How often (in blocks) to check for new votes. - pub VotingPeriod get(voting_period): required T::BlockNumber; - - /// The next free referendum index, aka the number of referendums started so far. - pub ReferendumCount get(referendum_count): required ReferendumIndex; - /// The next referendum index that should be tallied. - pub NextTally get(next_tally): required ReferendumIndex; - /// Information concerning any given referendum. - pub ReferendumInfoOf get(referendum_info): map [ ReferendumIndex => (T::BlockNumber, T::Proposal, VoteThreshold) ]; - - /// Get the voters for the current proposal. - pub VotersFor get(voters_for): default map [ ReferendumIndex => Vec ]; - - /// Get the vote, if Some, of `who`. - pub VoteOf get(vote_of): map [ (ReferendumIndex, T::AccountId) => bool ]; - } -} - -decl_event!( - /// An event in this module. - pub enum Event with RawEvent - where ::Balance, ::AccountId - { - Tabled(PropIndex, Balance, Vec), - Started(ReferendumIndex, VoteThreshold), - Passed(ReferendumIndex), - NotPassed(ReferendumIndex), - Cancelled(ReferendumIndex), - Executed(ReferendumIndex, bool), - } -); - -impl Module { - - /// Deposit one of this module's events. - fn deposit_event(event: Event) { - >::deposit_event(::Event::from(event).into()); - } - - // exposed immutables. - - /// Get the amount locked in support of `proposal`; `None` if proposal isn't a valid proposal - /// index. - pub fn locked_for(proposal: PropIndex) -> Option { - Self::deposit_of(proposal).map(|(d, l)| d * T::Balance::sa(l.len() as u64)) - } - - /// Return true if `ref_index` is an on-going referendum. - pub fn is_active_referendum(ref_index: ReferendumIndex) -> bool { - >::exists(ref_index) - } - - /// Get all referendums currently active. - pub fn active_referendums() -> Vec<(ReferendumIndex, T::BlockNumber, T::Proposal, VoteThreshold)> { - let next = Self::next_tally(); - let last = Self::referendum_count(); - (next..last).into_iter() - .filter_map(|i| Self::referendum_info(i).map(|(n, p, t)| (i, n, p, t))) - .collect() - } - - /// Get all referendums ready for tally at block `n`. - pub fn maturing_referendums_at(n: T::BlockNumber) -> Vec<(ReferendumIndex, T::BlockNumber, T::Proposal, VoteThreshold)> { - let next = Self::next_tally(); - let last = Self::referendum_count(); - (next..last).into_iter() - .filter_map(|i| Self::referendum_info(i).map(|(n, p, t)| (i, n, p, t))) - .take_while(|&(_, block_number, _, _)| block_number == n) - .collect() - } - - /// Get the voters for the current proposal. - pub fn tally(ref_index: ReferendumIndex) -> (T::Balance, T::Balance) { - Self::voters_for(ref_index).iter() - .map(|a| (>::total_balance(a), Self::vote_of((ref_index, a.clone())).unwrap_or(false)/*defensive only: all items come from `voters`; for an item to be in `voters` there must be a vote registered; qed*/)) - .map(|(bal, vote)| if vote { (bal, Zero::zero()) } else { (Zero::zero(), bal) }) - .fold((Zero::zero(), Zero::zero()), |(a, b), (c, d)| (a + c, b + d)) - } - - // dispatching. - - /// Propose a sensitive action to be taken. - fn propose(origin: T::Origin, proposal: Box, value: T::Balance) -> Result { - let who = ensure_signed(origin)?; - ensure!(value >= Self::minimum_deposit(), "value too low"); - >::reserve(&who, value) - .map_err(|_| "proposer's balance too low")?; - - let index = Self::public_prop_count(); - >::put(index + 1); - >::insert(index, (value, vec![who.clone()])); - - let mut props = Self::public_props(); - props.push((index, (*proposal).clone(), who)); - >::put(props); - Ok(()) - } - - /// Propose a sensitive action to be taken. - fn second(origin: T::Origin, proposal: PropIndex) -> Result { - let who = ensure_signed(origin)?; - let mut deposit = Self::deposit_of(proposal) - .ok_or("can only second an existing proposal")?; - >::reserve(&who, deposit.0) - .map_err(|_| "seconder's balance too low")?; - deposit.1.push(who); - >::insert(proposal, deposit); - Ok(()) - } - - /// Vote in a referendum. If `approve_proposal` is true, the vote is to enact the proposal; - /// false would be a vote to keep the status quo. - fn vote(origin: T::Origin, ref_index: ReferendumIndex, approve_proposal: bool) -> Result { - let who = ensure_signed(origin)?; - ensure!(Self::is_active_referendum(ref_index), "vote given for invalid referendum."); - ensure!(!>::total_balance(&who).is_zero(), - "transactor must have balance to signal approval."); - if !>::exists(&(ref_index, who.clone())) { - >::mutate(ref_index, |voters| voters.push(who.clone())); - } - >::insert(&(ref_index, who), approve_proposal); - Ok(()) - } - - /// Start a referendum. - fn start_referendum(origin: T::Origin, proposal: Box, vote_threshold: VoteThreshold) -> Result { - ensure_root(origin)?; - Self::inject_referendum( - >::block_number() + Self::voting_period(), - *proposal, - vote_threshold - ).map(|_| ()) - } - - /// Remove a referendum. - fn cancel_referendum(origin: T::Origin, ref_index: ReferendumIndex) -> Result { - ensure_root(origin)?; - Self::clear_referendum(ref_index); - Ok(()) - } - - // exposed mutables. - - /// Start a referendum. Can be called directly by the council. - pub fn internal_start_referendum(proposal: T::Proposal, vote_threshold: VoteThreshold) -> result::Result { - >::inject_referendum(>::block_number() + >::voting_period(), proposal, vote_threshold) - } - - /// Remove a referendum. Can be called directly by the council. - pub fn internal_cancel_referendum(ref_index: ReferendumIndex) { - Self::deposit_event(RawEvent::Cancelled(ref_index)); - >::clear_referendum(ref_index); - } - - // private. - - /// Start a referendum - fn inject_referendum( - end: T::BlockNumber, - proposal: T::Proposal, - vote_threshold: VoteThreshold - ) -> result::Result { - let ref_index = Self::referendum_count(); - if ref_index > 0 && Self::referendum_info(ref_index - 1).map(|i| i.0 > end).unwrap_or(false) { - Err("Cannot inject a referendum that ends earlier than preceeding referendum")? - } - - >::put(ref_index + 1); - >::insert(ref_index, (end, proposal, vote_threshold)); - Self::deposit_event(RawEvent::Started(ref_index, vote_threshold)); - Ok(ref_index) - } - - /// Remove all info on a referendum. - fn clear_referendum(ref_index: ReferendumIndex) { - >::remove(ref_index); - >::remove(ref_index); - for v in Self::voters_for(ref_index) { - >::remove((ref_index, v)); - } - } - - /// Current era is ending; we should finish up any proposals. - fn end_block(now: T::BlockNumber) -> Result { - // pick out another public referendum if it's time. - if (now % Self::launch_period()).is_zero() { - let mut public_props = Self::public_props(); - if let Some((winner_index, _)) = public_props.iter() - .enumerate() - .max_by_key(|x| Self::locked_for((x.1).0).unwrap_or_else(Zero::zero)/*defensive only: All current public proposals have an amount locked*/) - { - let (prop_index, proposal, _) = public_props.swap_remove(winner_index); - >::put(public_props); - - if let Some((deposit, depositors)) = >::take(prop_index) {//: (T::Balance, Vec) = - // refund depositors - for d in &depositors { - >::unreserve(d, deposit); - } - Self::deposit_event(RawEvent::Tabled(prop_index, deposit, depositors)); - Self::inject_referendum(now + Self::voting_period(), proposal, VoteThreshold::SuperMajorityApprove)?; - } - } - } - - // tally up votes for any expiring referenda. - for (index, _, proposal, vote_threshold) in Self::maturing_referendums_at(now) { - let (approve, against) = Self::tally(index); - let total_issuance = >::total_issuance(); - Self::clear_referendum(index); - if vote_threshold.approved(approve, against, total_issuance) { - Self::deposit_event(RawEvent::Passed(index)); - let ok = proposal.dispatch(system::RawOrigin::Root.into()).is_ok(); - Self::deposit_event(RawEvent::Executed(index, ok)); - } else { - Self::deposit_event(RawEvent::NotPassed(index)); - } - >::put(index + 1); - } - Ok(()) - } -} - -impl OnFinalise for Module { - fn on_finalise(n: T::BlockNumber) { - if let Err(e) = Self::end_block(n) { - runtime_io::print(e); - } - } -} - -#[cfg(any(feature = "std", test))] -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -#[serde(deny_unknown_fields)] -pub struct GenesisConfig { - pub launch_period: T::BlockNumber, - pub voting_period: T::BlockNumber, - pub minimum_deposit: T::Balance, -} - -#[cfg(any(feature = "std", test))] -impl GenesisConfig { - pub fn new() -> Self { - GenesisConfig { - launch_period: T::BlockNumber::sa(1), - voting_period: T::BlockNumber::sa(1), - minimum_deposit: T::Balance::sa(1), - } - } -} - -#[cfg(any(feature = "std", test))] -impl Default for GenesisConfig { - fn default() -> Self { - GenesisConfig { - launch_period: T::BlockNumber::sa(1000), - voting_period: T::BlockNumber::sa(1000), - minimum_deposit: T::Balance::sa(0), - } - } -} - -#[cfg(any(feature = "std", test))] -impl primitives::BuildStorage for GenesisConfig -{ - fn build_storage(self) -> ::std::result::Result, Vec>, String> { - use codec::Encode; - - Ok(map![ - Self::hash(>::key()).to_vec() => self.launch_period.encode(), - Self::hash(>::key()).to_vec() => self.voting_period.encode(), - Self::hash(>::key()).to_vec() => self.minimum_deposit.encode(), - Self::hash(>::key()).to_vec() => (0 as ReferendumIndex).encode(), - Self::hash(>::key()).to_vec() => (0 as ReferendumIndex).encode(), - Self::hash(>::key()).to_vec() => (0 as PropIndex).encode() - ]) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use runtime_io::with_externalities; - use substrate_primitives::{H256, Blake2Hasher}; - use primitives::BuildStorage; - use primitives::traits::{BlakeTwo256}; - use primitives::testing::{Digest, Header}; - - impl_outer_origin! { - pub enum Origin for Test {} - } - - impl_outer_dispatch! { - pub enum Call where origin: Origin { - Balances, - Democracy, - } - } - - // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. - #[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] - pub struct Test; - impl system::Trait for Test { - type Origin = Origin; - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type Digest = Digest; - type AccountId = u64; - type Header = Header; - type Event = (); - } - impl balances::Trait for Test { - type Balance = u64; - type AccountIndex = u64; - type OnFreeBalanceZero = (); - type EnsureAccountLiquid = (); - type Event = (); - } - impl Trait for Test { - type Proposal = Call; - type Event = (); - } - - fn new_test_ext() -> runtime_io::TestExternalities { - let mut t = system::GenesisConfig::::default().build_storage().unwrap(); - t.extend(balances::GenesisConfig::{ - balances: vec![(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)], - transaction_base_fee: 0, - transaction_byte_fee: 0, - existential_deposit: 0, - transfer_fee: 0, - creation_fee: 0, - reclaim_rebate: 0, - }.build_storage().unwrap()); - t.extend(GenesisConfig::{ - launch_period: 1, - voting_period: 1, - minimum_deposit: 1, - }.build_storage().unwrap()); - t.into() - } - - type System = system::Module; - type Balances = balances::Module; - type Democracy = Module; - - #[test] - fn params_should_work() { - with_externalities(&mut new_test_ext(), || { - assert_eq!(Democracy::launch_period(), 1); - assert_eq!(Democracy::voting_period(), 1); - assert_eq!(Democracy::minimum_deposit(), 1); - assert_eq!(Democracy::referendum_count(), 0); - assert_eq!(Balances::free_balance(&42), 0); - assert_eq!(Balances::total_issuance(), 210); - }); - } - - fn set_balance_proposal(value: u64) -> Call { - Call::Balances(balances::Call::set_balance(balances::address::Address::Id(42), value, 0)) - } - - fn propose_set_balance(who: u64, value: u64, locked: u64) -> super::Result { - Democracy::propose(Origin::signed(who), Box::new(set_balance_proposal(value)), locked) - } - - #[test] - fn locked_for_should_work() { - with_externalities(&mut new_test_ext(), || { - System::set_block_number(1); - assert_ok!(propose_set_balance(1, 2, 2)); - assert_ok!(propose_set_balance(1, 4, 4)); - assert_ok!(propose_set_balance(1, 3, 3)); - assert_eq!(Democracy::locked_for(0), Some(2)); - assert_eq!(Democracy::locked_for(1), Some(4)); - assert_eq!(Democracy::locked_for(2), Some(3)); - }); - } - - #[test] - fn single_proposal_should_work() { - with_externalities(&mut new_test_ext(), || { - System::set_block_number(1); - assert_ok!(propose_set_balance(1, 2, 1)); - assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - - System::set_block_number(2); - let r = 0; - assert_ok!(Democracy::vote(Origin::signed(1), r, true)); - - assert_eq!(Democracy::referendum_count(), 1); - assert_eq!(Democracy::voters_for(r), vec![1]); - assert_eq!(Democracy::vote_of((r, 1)), Some(true)); - assert_eq!(Democracy::tally(r), (10, 0)); - - assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - - assert_eq!(Balances::free_balance(&42), 2); - }); - } - - #[test] - fn deposit_for_proposals_should_be_taken() { - with_externalities(&mut new_test_ext(), || { - System::set_block_number(1); - assert_ok!(propose_set_balance(1, 2, 5)); - assert_ok!(Democracy::second(Origin::signed(2), 0)); - assert_ok!(Democracy::second(Origin::signed(5), 0)); - assert_ok!(Democracy::second(Origin::signed(5), 0)); - assert_ok!(Democracy::second(Origin::signed(5), 0)); - assert_eq!(Balances::free_balance(&1), 5); - assert_eq!(Balances::free_balance(&2), 15); - assert_eq!(Balances::free_balance(&5), 35); - }); - } - - #[test] - fn deposit_for_proposals_should_be_returned() { - with_externalities(&mut new_test_ext(), || { - System::set_block_number(1); - assert_ok!(propose_set_balance(1, 2, 5)); - assert_ok!(Democracy::second(Origin::signed(2), 0)); - assert_ok!(Democracy::second(Origin::signed(5), 0)); - assert_ok!(Democracy::second(Origin::signed(5), 0)); - assert_ok!(Democracy::second(Origin::signed(5), 0)); - assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - assert_eq!(Balances::free_balance(&1), 10); - assert_eq!(Balances::free_balance(&2), 20); - assert_eq!(Balances::free_balance(&5), 50); - }); - } - - #[test] - fn proposal_with_deposit_below_minimum_should_not_work() { - with_externalities(&mut new_test_ext(), || { - System::set_block_number(1); - assert_noop!(propose_set_balance(1, 2, 0), "value too low"); - }); - } - - #[test] - fn poor_proposer_should_not_work() { - with_externalities(&mut new_test_ext(), || { - System::set_block_number(1); - assert_noop!(propose_set_balance(1, 2, 11), "proposer\'s balance too low"); - }); - } - - #[test] - fn poor_seconder_should_not_work() { - with_externalities(&mut new_test_ext(), || { - System::set_block_number(1); - assert_ok!(propose_set_balance(2, 2, 11)); - assert_noop!(Democracy::second(Origin::signed(1), 0), "seconder\'s balance too low"); - }); - } - - #[test] - fn runners_up_should_come_after() { - with_externalities(&mut new_test_ext(), || { - System::set_block_number(0); - assert_ok!(propose_set_balance(1, 2, 2)); - assert_ok!(propose_set_balance(1, 4, 4)); - assert_ok!(propose_set_balance(1, 3, 3)); - assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - - System::set_block_number(1); - assert_ok!(Democracy::vote(Origin::signed(1), 0, true)); - assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - assert_eq!(Balances::free_balance(&42), 4); - - System::set_block_number(2); - assert_ok!(Democracy::vote(Origin::signed(1), 1, true)); - assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - assert_eq!(Balances::free_balance(&42), 3); - - System::set_block_number(3); - assert_ok!(Democracy::vote(Origin::signed(1), 2, true)); - assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - }); - } - - #[test] - fn simple_passing_should_work() { - with_externalities(&mut new_test_ext(), || { - System::set_block_number(1); - let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap(); - assert_ok!(Democracy::vote(Origin::signed(1), r, true)); - - assert_eq!(Democracy::voters_for(r), vec![1]); - assert_eq!(Democracy::vote_of((r, 1)), Some(true)); - assert_eq!(Democracy::tally(r), (10, 0)); - - assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - - assert_eq!(Balances::free_balance(&42), 2); - }); - } - - #[test] - fn cancel_referendum_should_work() { - with_externalities(&mut new_test_ext(), || { - System::set_block_number(1); - let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap(); - assert_ok!(Democracy::vote(Origin::signed(1), r, true)); - assert_ok!(Democracy::cancel_referendum(Origin::ROOT, r)); - - assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - - assert_eq!(Balances::free_balance(&42), 0); - }); - } - - #[test] - fn simple_failing_should_work() { - with_externalities(&mut new_test_ext(), || { - System::set_block_number(1); - let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap(); - assert_ok!(Democracy::vote(Origin::signed(1), r, false)); - - assert_eq!(Democracy::voters_for(r), vec![1]); - assert_eq!(Democracy::vote_of((r, 1)), Some(false)); - assert_eq!(Democracy::tally(r), (0, 10)); - - assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - - assert_eq!(Balances::free_balance(&42), 0); - }); - } - - #[test] - fn controversial_voting_should_work() { - with_externalities(&mut new_test_ext(), || { - System::set_block_number(1); - let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap(); - assert_ok!(Democracy::vote(Origin::signed(1), r, true)); - assert_ok!(Democracy::vote(Origin::signed(2), r, false)); - assert_ok!(Democracy::vote(Origin::signed(3), r, false)); - assert_ok!(Democracy::vote(Origin::signed(4), r, true)); - assert_ok!(Democracy::vote(Origin::signed(5), r, false)); - assert_ok!(Democracy::vote(Origin::signed(6), r, true)); - - assert_eq!(Democracy::tally(r), (110, 100)); - - assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - - assert_eq!(Balances::free_balance(&42), 2); - }); - } - - #[test] - fn controversial_low_turnout_voting_should_work() { - with_externalities(&mut new_test_ext(), || { - System::set_block_number(1); - let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap(); - assert_ok!(Democracy::vote(Origin::signed(5), r, false)); - assert_ok!(Democracy::vote(Origin::signed(6), r, true)); - - assert_eq!(Democracy::tally(r), (60, 50)); - - assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - - assert_eq!(Balances::free_balance(&42), 0); - }); - } - - #[test] - fn passing_low_turnout_voting_should_work() { - with_externalities(&mut new_test_ext(), || { - assert_eq!(Balances::free_balance(&42), 0); - assert_eq!(Balances::total_issuance(), 210); - - System::set_block_number(1); - let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap(); - assert_ok!(Democracy::vote(Origin::signed(4), r, true)); - assert_ok!(Democracy::vote(Origin::signed(5), r, false)); - assert_ok!(Democracy::vote(Origin::signed(6), r, true)); - - assert_eq!(Democracy::tally(r), (100, 50)); - - assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - - assert_eq!(Balances::free_balance(&42), 2); - }); - } -} diff --git a/substrate/runtime/democracy/src/vote_threshold.rs b/substrate/runtime/democracy/src/vote_threshold.rs deleted file mode 100644 index ce46149222055..0000000000000 --- a/substrate/runtime/democracy/src/vote_threshold.rs +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2017 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 . - -//! Voting thresholds. - -use primitives::traits::{Zero, IntegerSquareRoot}; -use rstd::ops::{Add, Mul, Div, Rem}; - -/// A means of determining if a vote is past pass threshold. -#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] -pub enum VoteThreshold { - /// A supermajority of approvals is needed to pass this vote. - SuperMajorityApprove, - /// A supermajority of rejects is needed to fail this vote. - SuperMajorityAgainst, - /// A simple majority of approvals is needed to pass this vote. - SimpleMajority, -} - -pub trait Approved { - /// Given `approve` votes for and `against` votes against from a total electorate size of - /// `electorate` (`electorate - (approve + against)` are abstainers), then returns true if the - /// overall outcome is in favour of approval. - fn approved(&self, approve: Balance, against: Balance, electorate: Balance) -> bool; -} - -/// Return `true` iff `n1 / d1 < n2 / d2`. `d1` and `d2` may not be zero. -fn compare_rationals + Div + Rem + Ord + Copy>(mut n1: T, mut d1: T, mut n2: T, mut d2: T) -> bool { - // Uses a continued fractional representation for a non-overflowing compare. - // Detailed at https://janmr.com/blog/2014/05/comparing-rational-numbers-without-overflow/. - loop { - let q1 = n1 / d1; - let q2 = n2 / d2; - if q1 < q2 { - return true; - } - if q2 < q1 { - return false; - } - let r1 = n1 % d1; - let r2 = n2 % d2; - if r2.is_zero() { - return false; - } - if r1.is_zero() { - return true; - } - n1 = d2; - n2 = d1; - d1 = r2; - d2 = r1; - } -} - -impl + Mul + Div + Rem + Copy> Approved for VoteThreshold { - /// Given `approve` votes for and `against` votes against from a total electorate size of - /// `electorate` (`electorate - (approve + against)` are abstainers), then returns true if the - /// overall outcome is in favour of approval. - fn approved(&self, approve: Balance, against: Balance, electorate: Balance) -> bool { - let voters = approve + against; - let sqrt_voters = voters.integer_sqrt(); - let sqrt_electorate = electorate.integer_sqrt(); - if sqrt_voters.is_zero() { return false; } - match *self { - VoteThreshold::SuperMajorityApprove => - compare_rationals(against, sqrt_voters, approve, sqrt_electorate), - VoteThreshold::SuperMajorityAgainst => - compare_rationals(against, sqrt_electorate, approve, sqrt_voters), - VoteThreshold::SimpleMajority => approve > against, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn should_work() { - assert_eq!(VoteThreshold::SuperMajorityApprove.approved(60, 50, 210), false); - assert_eq!(VoteThreshold::SuperMajorityApprove.approved(100, 50, 210), true); - } -} diff --git a/substrate/runtime/example/Cargo.toml b/substrate/runtime/example/Cargo.toml deleted file mode 100644 index 92c0d887e7f3a..0000000000000 --- a/substrate/runtime/example/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ -[package] -name = "substrate-runtime-example" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -hex-literal = "0.1.0" -serde = { version = "1.0", default_features = false } -serde_derive = { version = "1.0", optional = true } -substrate-runtime-std = { path = "../../runtime-std", default_features = false } -substrate-runtime-io = { path = "../../runtime-io", default_features = false } -substrate-runtime-support = { path = "../../runtime-support", default_features = false } -substrate-runtime-primitives = { path = "../primitives", default_features = false } -substrate-codec = { path = "../../codec", default_features = false } -substrate-codec-derive = { path = "../../codec/derive", default_features = false } -substrate-primitives = { path = "../../primitives", default_features = false } -substrate-runtime-system = { path = "../system", default_features = false } -substrate-runtime-balances = { path = "../balances", default_features = false } - -[features] -default = ["std"] -std = [ - "substrate-runtime-std/std", - "substrate-runtime-io/std", - "substrate-runtime-support/std", - "substrate-runtime-primitives/std", - "substrate-runtime-balances/std", - "serde/std", - "serde_derive", - "substrate-codec/std", - "substrate-codec-derive/std", - "substrate-primitives/std", - "substrate-runtime-system/std", -] diff --git a/substrate/runtime/example/src/lib.rs b/substrate/runtime/example/src/lib.rs deleted file mode 100644 index 8247106e38466..0000000000000 --- a/substrate/runtime/example/src/lib.rs +++ /dev/null @@ -1,415 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! The Example: A simple example of a runtime module demonstrating -//! concepts, APIs and structures common to most runtime modules. - -// Ensure we're `no_std` when compiling for Wasm. -#![cfg_attr(not(feature = "std"), no_std)] - -// Assert macros used in tests. -#[cfg_attr(feature = "std", macro_use)] -extern crate substrate_runtime_std; - -// Needed for tests (`with_externalities`). -#[cfg(test)] -extern crate substrate_runtime_io as runtime_io; - -// Needed for the set of mock primitives used in our tests. -#[cfg(test)] -extern crate substrate_primitives; - -// Needed for deriving `Serialize` and `Deserialize` for various types. -// We only implement the serde traits for std builds - they're unneeded -// in the wasm runtime. -#[cfg(feature = "std")] -#[macro_use] -extern crate serde_derive; - -// Needed for deriving `Encode` and `Decode` for `RawEvent`. -#[macro_use] -extern crate substrate_codec_derive; -extern crate substrate_codec as codec; - -// Needed for type-safe access to storage DB. -#[macro_use] -extern crate substrate_runtime_support as runtime_support; - -// Needed for various traits. In our case, `OnFinalise`. -extern crate substrate_runtime_primitives as runtime_primitives; -// `system` module provides us with all sorts of useful stuff and macros -// depend on it being around. -extern crate substrate_runtime_system as system; -// `balances` module is needed for our little example. It's not required in -// general (though if you want your module to be able to work with tokens, then you -// might find it useful). -extern crate substrate_runtime_balances as balances; - -use runtime_primitives::traits::OnFinalise; -use runtime_support::{StorageValue, dispatch::Result}; -use system::{ensure_signed, ensure_root}; - -/// 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 -/// should be added to our implied traits list. -/// -/// `system::Trait` should always be included in our implied traits. -pub trait Trait: balances::Trait { - /// The overarching event type. - type Event: From> + Into<::Event>; -} - -// The module declaration. This states the entry points that we handle. The -// macro takes care of the marshalling of arguments and dispatch. -// -// Anyone can have these functions execute by signing and submitting -// an extrinsic. Ensure that calls into each of these execute in a time, memory and -// using storage space proportional to any costs paid for by the caller or otherwise the -// difficulty of forcing the call to happen. -// -// Generally you'll want to split these into three groups: -// - Public calls that are signed by an external account. -// - Root calls that are allowed to be made only by the governance system. -// - Inherent calls that are allowed to be made only by the block authors and validators. -// -// Information about where this dispatch initiated from is provided as the first argument -// "origin". As such functions must always look like: -// -// `fn foo(origin, bar: Bar, baz: Baz) -> Result = 0;` -// -// The `Result` is required as part of the syntax (and expands to the conventional dispatch -// result of `Result<(), &'static str>`). -// -// When you come to `impl` them later in the module, you must specify the full type for `origin`: -// -// `fn foo(origin: T::Origin, bar: Bar, baz: Baz) { ... }` -// -// There are three entries in the `system::Origin` enum that correspond -// to the above bullets: `::Signed(AccountId)`, `::Root` and `::Inherent`. You should always match -// against them as the first thing you do in your function. There are three convenience calls -// in system that do the matching for you and return a convenient result: `ensure_signed`, -// `ensure_root` and `ensure_inherent`. -decl_module! { - // Simple declaration of the `Module` type. Lets the macro know what its working on. - pub struct Module for enum Call where origin: T::Origin { - /// This is your public interface. Be extremely careful. - /// This is just a simple example of how to interact with the module from the external - /// world. - fn accumulate_dummy(origin, increase_by: T::Balance) -> Result; - - fn accumulate_foo(origin, increase_by: T::Balance) -> Result; - - /// A privileged call; in this case it resets our dummy value to something new. - fn set_dummy(origin, new_dummy: T::Balance) -> Result; - } -} - -/// An event in this module. Events are simple means of reporting specific conditions and -/// circumstances that have happened that users, Dapps and/or chain explorers would find -/// interesting and otherwise difficult to detect. -decl_event!( - pub enum Event with RawEvent - where ::Balance - { - // Just a normal `enum`, here's a dummy event to ensure it compiles. - /// Dummy event, just here so there's a generic type that's used. - Dummy(B), - } -); - -decl_storage! { - // A macro for the Storage trait, and its implementation, for this module. - // This allows for type-safe usage of the Substrate storage database, so you can - // keep things around between blocks. - trait Store for Module as Example { - // Any storage declarations of the form: - // `pub? Name get(getter_name)? : [required | default]? ;` - // where `` is either: - // - `Type` (a basic value item); or - // - `map [ KeyType => ValueType ]` (a map item). - // - // Note that there are two optional modifiers for the storage type declaration. - // - `Foo: u32`: - // - `Foo::put(1); Foo::get()` returns `Some(1)`; - // - `Foo::kill(); Foo::get()` returns `None`. - // - `Foo: required u32`: - // - `Foo::put(1); Foo::get()` returns `1`; - // - `Foo::kill(); Foo::get()` panics. - // - `Foo: default u32`: - // - `Foo::put(1); Foo::get()` returns `1`; - // - `Foo::kill(); Foo::get()` returns `0` (u32::default()). - // e.g. Foo: u32; - // e.g. pub Bar get(bar): default map [ T::AccountId => Vec<(T::Balance, u64)> ]; - // - // For basic value items, you'll get a type which implements - // `runtime_support::StorageValue`. For map items, you'll get a type which - // implements `runtime_support::StorageMap`. - // - // If they have a getter (`get(getter_name)`), then your module will come - // equipped with `fn getter_name() -> Type` for basic value items or - // `fn getter_name(key: KeyType) -> ValueType` for map items. - Dummy get(dummy): T::Balance; - - // this one uses the default, we'll demonstrate the usage of 'mutate' API. - Foo get(foo): default T::Balance; - } -} - -// The main implementation block for the module. Functions here fall into three broad -// categories: -// - Implementations of dispatch functions. The dispatch code generated by the module macro -// expects each of its functions to be implemented. -// - Public interface. These are functions that are `pub` and generally fall into inspector -// functions that do not write to storage and operation functions that do. -// - Private functions. These are your usual private utilities unavailable to other modules. -impl Module { - /// Deposit one of this module's events. - // TODO: move into `decl_module` macro. - fn deposit_event(event: Event) { - >::deposit_event(::Event::from(event).into()); - } - - // Implement Calls and add public immutables and private mutables. - - // Implement dispatched function `accumulate_dummy`. This just increases the value - // of `Dummy` by `increase_by`. - // - // Since this is a dispatched function there are two extremely important things to - // remember: - // - // - MUST NOT PANIC: Under no circumstances (save, perhaps, storage getting into an - // irreparably damaged state) must this function panic. - // - NO SIDE-EFFECTS ON ERROR: This function must either complete totally (and return - // `Ok(())` or it must have no side-effects on storage and return `Err('Some reason')`. - // - // The first is relatively easy to audit for - just ensure all panickers are removed from - // logic that executes in production (which you do anyway, right?!). To ensure the second - // is followed, you should do all tests for validity at the top of your function. This - // is stuff like checking the sender (`origin`) or that state is such that the operation - // makes sense. - // - // Once you've determined that it's all good, then enact the operation and change storage. - // If you can't be certain that the operation will succeed without substantial computation - // then you have a classic blockchain attack scenario. The normal way of managing this is - // to attach a bond to the operation. As the first major alteration of storage, reserve - // some value from the sender's account (`Balances` module has a `reserve` function for - // exactly this scenario). This amount should be enough to cover any costs of the - // substantial execution in case it turns out that you can't proceed with the operation. - // - // If it eventually transpires that the operation is fine and, therefore, that the - // expense of the checks should be borne by the network, then you can refund the reserved - // deposit. If, however, the operation turns out to be invalid and the computation is - // wasted, then you can burn it or repatriate elsewhere. - // - // Security bonds ensure that attackers can't game it by ensuring that anyone interacting - // with the system either progresses it or pays for the trouble of faffing around with - // no progress. - // - // If you don't respect these rules, it is likely that your chain will be attackable. - fn accumulate_dummy(origin: T::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)?; - - // Read the value of dummy from storage. - // let dummy = Self::dummy(); - // Will also work using the `::get` on the storage item type itself: - // let dummy = >::get(); - - // Calculate the new value. - // let new_dummy = dummy.map_or(increase_by, |dummy| dummy + increase_by); - - // Put the new value into storage. - // >::put(new_dummy); - // Will also work with a reference: - // >::put(&new_dummy); - - // Here's the new one of read and then modify the value. - >::mutate(|dummy| { - let new_dummy = dummy.map_or(increase_by, |dummy| dummy + increase_by); - *dummy = Some(new_dummy); - }); - - // Let's deposit an event to let the outside world know this happened. - Self::deposit_event(RawEvent::Dummy(increase_by)); - - // All good. - Ok(()) - } - - fn accumulate_foo(origin: T::Origin, increase_by: T::Balance) -> Result { - let _sender = ensure_signed(origin)?; - - // Because Foo has 'default', the type of 'foo' in closure is the raw type instead of an Option<> type. - >::mutate(|foo| *foo = *foo + increase_by); - - Ok(()) - } - - // Implementation of a privileged call. This doesn't have an `origin` parameter 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 - // assume it's a one-off operation and substantial processing/storage/memory can be used - // without worrying about gameability or attack scenarios. - fn set_dummy(origin: T::Origin, new_value: T::Balance) -> Result { - // This is a privileged call, so we ensure that the origin is "Root". - ensure_root(origin)?; - - // Put the new value into storage. - >::put(new_value); - - // All good. - Ok(()) - } -} - -// This trait expresses what should happen when the block is finalised. -impl OnFinalise for Module { - fn on_finalise(_: T::BlockNumber) { - // Anything that needs to be done at the end of the block. - // We just kill our dummy storage item. - >::kill(); - } -} - -#[cfg(feature = "std")] -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -#[serde(deny_unknown_fields)] -/// The genesis block configuration type. This is a simple default-capable struct that -/// contains any fields with which this module can be configured at genesis time. -pub struct GenesisConfig { - /// A value with which to initialise the Dummy storage item. - pub dummy: T::Balance, - pub foo: T::Balance, -} - -#[cfg(feature = "std")] -impl Default for GenesisConfig { - fn default() -> Self { - GenesisConfig { - dummy: Default::default(), - foo: Default::default(), - } - } -} - -// This expresses the specific key/value pairs that must be placed in storage in order -// to initialise the module and properly reflect the configuration. -// -// Ideally this would re-use the `::put` logic in the storage item type for introducing -// the values into the `StorageMap` (which is just a `HashMap, Vec>`). That -// is not yet in place, though, so for now we do everything "manually", using `hash`, -// `::key()` and `.to_vec()` for the key and `.encode()` for the value. -#[cfg(feature = "std")] -impl runtime_primitives::BuildStorage for GenesisConfig -{ - fn build_storage(self) -> ::std::result::Result { - use codec::Encode; - Ok(map![ - Self::hash(>::key()).to_vec() => self.dummy.encode(), - Self::hash(>::key()).to_vec() => self.foo.encode() - ]) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - use runtime_io::with_externalities; - use substrate_primitives::{H256, Blake2Hasher}; - use runtime_primitives::BuildStorage; - use runtime_primitives::traits::{BlakeTwo256}; - - // 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 runtime_primitives::testing::{Digest, Header}; - - 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; - impl system::Trait for Test { - type Origin = Origin; - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type Digest = Digest; - type AccountId = u64; - type Header = Header; - type Event = (); - } - impl balances::Trait for Test { - type Balance = u64; - type AccountIndex = u64; - type OnFreeBalanceZero = (); - type EnsureAccountLiquid = (); - type Event = (); - } - impl Trait for Test { - type Event = (); - } - type Example = Module; - - // This function basically just builds a genesis storage key/value store according to - // our desired mockup. - fn new_test_ext() -> runtime_io::TestExternalities { - let mut t = system::GenesisConfig::::default().build_storage().unwrap(); - // We use default for brevity, but you can configure as desired if needed. - t.extend(balances::GenesisConfig::::default().build_storage().unwrap()); - t.extend(GenesisConfig::{ - dummy: 42, - foo: 24, - }.build_storage().unwrap()); - t.into() - } - - #[test] - fn it_works_for_optional_value() { - with_externalities(&mut new_test_ext(), || { - // Check that GenesisBuilder works properly. - assert_eq!(Example::dummy(), Some(42)); - - // Check that accumulate works when we have Some value in Dummy already. - assert_ok!(Example::accumulate_dummy(Origin::signed(1), 27)); - assert_eq!(Example::dummy(), Some(69)); - - // Check that finalising the block removes Dummy from storage. - >::on_finalise(1); - assert_eq!(Example::dummy(), None); - - // Check that accumulate works when we Dummy has None in it. - assert_ok!(Example::accumulate_dummy(Origin::signed(1), 42)); - assert_eq!(Example::dummy(), Some(42)); - }); - } - - #[test] - fn it_works_for_default_value() { - with_externalities(&mut new_test_ext(), || { - assert_eq!(Example::foo(), 24); - assert_ok!(Example::accumulate_foo(Origin::signed(1), 1)); - assert_eq!(Example::foo(), 25); - }); - } -} diff --git a/substrate/runtime/executive/Cargo.toml b/substrate/runtime/executive/Cargo.toml deleted file mode 100644 index 965bbe2a1e2c1..0000000000000 --- a/substrate/runtime/executive/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "substrate-runtime-executive" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -hex-literal = "0.1.0" -serde = { version = "1.0", default_features = false } -serde_derive = { version = "1.0", optional = true } -substrate-codec = { path = "../../codec", default_features = false } -substrate-runtime-std = { path = "../../runtime-std", default_features = false } -substrate-runtime-io = { path = "../../runtime-io", default_features = false } -substrate-runtime-support = { path = "../../runtime-support", default_features = false } -substrate-runtime-primitives = { path = "../primitives", default_features = false } -substrate-runtime-system = { path = "../system", default_features = false } - -[dev-dependencies] -substrate-primitives = { path = "../../primitives" } -substrate-runtime-balances = { path = "../balances" } -substrate-codec-derive = { path = "../../codec/derive" } - -[features] -default = ["std"] -std = [ - "substrate-runtime-std/std", - "substrate-runtime-support/std", - "serde/std", - "serde_derive", - "substrate-codec/std", - "substrate-runtime-primitives/std", - "substrate-runtime-io/std", - "substrate-runtime-system/std", -] diff --git a/substrate/runtime/executive/src/lib.rs b/substrate/runtime/executive/src/lib.rs deleted file mode 100644 index 9b7e736f6d934..0000000000000 --- a/substrate/runtime/executive/src/lib.rs +++ /dev/null @@ -1,359 +0,0 @@ -// Copyright 2017 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 . - -//! Executive: Handles all of the top-level stuff; essentially just executing blocks/extrinsics. - -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(feature = "std")] -extern crate serde; -#[cfg(test)] -#[macro_use] -extern crate serde_derive; - -#[cfg(test)] -#[macro_use] -extern crate substrate_codec_derive; - -#[cfg_attr(test, macro_use)] -extern crate substrate_runtime_support as runtime_support; - -extern crate substrate_runtime_std as rstd; -extern crate substrate_runtime_io as runtime_io; -extern crate substrate_codec as codec; -extern crate substrate_runtime_primitives as primitives; -extern crate substrate_runtime_system as system; - -#[cfg(test)] -#[macro_use] -extern crate hex_literal; - -#[cfg(test)] -extern crate substrate_primitives; - -#[cfg(test)] -extern crate substrate_runtime_balances as balances; - -use rstd::prelude::*; -use rstd::marker::PhantomData; -use rstd::result; -use primitives::traits::{self, Header, Zero, One, Checkable, Applyable, CheckEqual, OnFinalise, - MakePayment, Hash}; -use codec::{Codec, Encode}; -use system::extrinsics_root; -use primitives::{ApplyOutcome, ApplyError}; - -mod internal { - pub enum ApplyError { - BadSignature(&'static str), - Stale, - Future, - CantPay, - } - - pub enum ApplyOutcome { - Success, - Fail(&'static str), - } -} - -pub struct Executive< - System, - Block, - Lookup, - Payment, - Finalisation, ->(PhantomData<(System, Block, Lookup, Payment, Finalisation)>); - -impl< - Address, - System: system::Trait, - Block: traits::Block, - Lookup: traits::Lookup, - Payment: MakePayment, - Finalisation: OnFinalise, -> Executive where - Block::Extrinsic: Checkable Result> + Codec, - Result>>::Checked: Applyable -{ - /// Start the execution of a particular block. - pub fn initialise_block(header: &System::Header) { - >::initialise(header.number(), header.parent_hash(), header.extrinsics_root()); - } - - fn initial_checks(block: &Block) { - let header = block.header(); - - // check parent_hash is correct. - let n = header.number().clone(); - assert!( - n > System::BlockNumber::zero() && >::block_hash(n - System::BlockNumber::one()) == *header.parent_hash(), - "Parent hash should be valid." - ); - - // check transaction trie root represents the transactions. - let xts_root = extrinsics_root::(&block.extrinsics()); - header.extrinsics_root().check_equal(&xts_root); - assert!(header.extrinsics_root() == &xts_root, "Transaction trie root must be valid."); - } - - /// Actually execute all transitioning for `block`. - pub fn execute_block(block: Block) { - Self::initialise_block(block.header()); - - // any initial checks - Self::initial_checks(&block); - - // execute transactions - let (header, extrinsics) = block.deconstruct(); - extrinsics.into_iter().for_each(Self::apply_extrinsic_no_note); - - // post-transactional book-keeping. - >::note_finished_extrinsics(); - Finalisation::on_finalise(*header.number()); - - // any final checks - Self::final_checks(&header); - } - - /// Finalise the block - it is up the caller to ensure that all header fields are valid - /// except state-root. - pub fn finalise_block() -> System::Header { - >::note_finished_extrinsics(); - Finalisation::on_finalise(>::block_number()); - - // setup extrinsics - >::derive_extrinsics(); - >::finalise() - } - - /// Apply extrinsic outside of the block execution function. - /// This doesn't attempt to validate anything regarding the block, but it builds a list of uxt - /// hashes. - pub fn apply_extrinsic(uxt: Block::Extrinsic) -> result::Result { - let encoded = uxt.encode(); - let encoded_len = encoded.len(); - >::note_extrinsic(encoded); - match Self::apply_extrinsic_no_note_with_len(uxt, encoded_len) { - Ok(internal::ApplyOutcome::Success) => Ok(ApplyOutcome::Success), - Ok(internal::ApplyOutcome::Fail(_)) => Ok(ApplyOutcome::Fail), - Err(internal::ApplyError::CantPay) => Err(ApplyError::CantPay), - Err(internal::ApplyError::BadSignature(_)) => Err(ApplyError::BadSignature), - Err(internal::ApplyError::Stale) => Err(ApplyError::Stale), - Err(internal::ApplyError::Future) => Err(ApplyError::Future), - } - } - - /// Apply an extrinsic inside the block execution function. - fn apply_extrinsic_no_note(uxt: Block::Extrinsic) { - let l = uxt.encode().len(); - match Self::apply_extrinsic_no_note_with_len(uxt, l) { - Ok(internal::ApplyOutcome::Success) => (), - Ok(internal::ApplyOutcome::Fail(e)) => runtime_io::print(e), - Err(internal::ApplyError::CantPay) => panic!("All extrinsics should have sender able to pay their fees"), - Err(internal::ApplyError::BadSignature(_)) => panic!("All extrinsics should be properly signed"), - Err(internal::ApplyError::Stale) | Err(internal::ApplyError::Future) => panic!("All extrinsics should have the correct nonce"), - } - } - - /// Actually apply an extrinsic given its `encoded_len`; this doesn't note its hash. - fn apply_extrinsic_no_note_with_len(uxt: Block::Extrinsic, encoded_len: usize) -> result::Result { - // Verify the signature is good. - let xt = uxt.check_with(Lookup::lookup).map_err(internal::ApplyError::BadSignature)?; - - if let Some(sender) = xt.sender() { - // check index - let expected_index = >::account_nonce(sender); - if xt.index() != &expected_index { return Err( - if xt.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. - - // increment nonce in storage - >::inc_account_nonce(sender); - } - - // decode parameters and dispatch - let r = xt.apply(); - - >::note_applied_extrinsic(&r); - - r.map(|_| internal::ApplyOutcome::Success).or_else(|e| Ok(internal::ApplyOutcome::Fail(e))) - } - - fn final_checks(header: &System::Header) { - // check digest - assert!(header.digest() == &>::digest()); - - // remove temporaries. - >::finalise(); - - // check storage root. - let storage_root = System::Hashing::storage_root(); - header.state_root().check_equal(&storage_root); - assert!(header.state_root() == &storage_root, "Storage root must match that calculated."); - } -} - -#[cfg(test)] -mod tests { - use super::*; - use balances::Call; - use runtime_io::with_externalities; - use substrate_primitives::{H256, Blake2Hasher}; - use primitives::BuildStorage; - use primitives::traits::{Header as HeaderT, BlakeTwo256, Lookup}; - use primitives::testing::{Digest, Header, Block}; - use system; - - struct NullLookup; - impl Lookup for NullLookup { - type Source = u64; - type Target = u64; - fn lookup(s: Self::Source) -> Result { - Ok(s) - } - } - - impl_outer_origin! { - pub enum Origin for Runtime { - } - } - - impl_outer_event!{ - pub enum MetaEvent for Runtime { - balances - } - } - - // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. - #[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] - pub struct Runtime; - impl system::Trait for Runtime { - type Origin = Origin; - type Index = u64; - type BlockNumber = u64; - type Hash = substrate_primitives::H256; - type Hashing = BlakeTwo256; - type Digest = Digest; - type AccountId = u64; - type Header = Header; - type Event = MetaEvent; - } - impl balances::Trait for Runtime { - type Balance = u64; - type AccountIndex = u64; - type OnFreeBalanceZero = (); - type EnsureAccountLiquid = (); - type Event = MetaEvent; - } - - type TestXt = primitives::testing::TestXt>; - type Executive = super::Executive, NullLookup, balances::Module, ()>; - - #[test] - fn balance_transfer_dispatch_works() { - let mut t = system::GenesisConfig::::default().build_storage().unwrap(); - t.extend(balances::GenesisConfig:: { - balances: vec![(1, 111)], - transaction_base_fee: 10, - transaction_byte_fee: 0, - existential_deposit: 0, - transfer_fee: 0, - creation_fee: 0, - reclaim_rebate: 0, - }.build_storage().unwrap()); - let xt = primitives::testing::TestXt(Some(1), 0, Call::transfer(2.into(), 69)); - let mut t = runtime_io::TestExternalities::from(t); - with_externalities(&mut t, || { - Executive::initialise_block(&Header::new(1, H256::default(), H256::default(), [69u8; 32].into(), Digest::default())); - Executive::apply_extrinsic(xt).unwrap(); - assert_eq!(>::total_balance(&1), 32); - assert_eq!(>::total_balance(&2), 69); - }); - } - - fn new_test_ext() -> runtime_io::TestExternalities { - let mut t = system::GenesisConfig::::default().build_storage().unwrap(); - t.extend(balances::GenesisConfig::::default().build_storage().unwrap()); - t.into() - } - - #[test] - fn block_import_works() { - with_externalities(&mut new_test_ext(), || { - Executive::execute_block(Block { - header: Header { - parent_hash: [69u8; 32].into(), - number: 1, - state_root: hex!("d1d3da2b1efb1a6ef740b8cdef52e4cf3c6dade6f8a360969fd7ef0034c53b54").into(), - extrinsics_root: hex!("45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0").into(), - digest: Digest { logs: vec![], }, - }, - extrinsics: vec![], - }); - }); - } - - #[test] - #[should_panic] - fn block_import_of_bad_state_root_fails() { - with_externalities(&mut new_test_ext(), || { - Executive::execute_block(Block { - header: Header { - parent_hash: [69u8; 32].into(), - number: 1, - state_root: [0u8; 32].into(), - extrinsics_root: hex!("45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0").into(), - digest: Digest { logs: vec![], }, - }, - extrinsics: vec![], - }); - }); - } - - #[test] - #[should_panic] - fn block_import_of_bad_extrinsic_root_fails() { - with_externalities(&mut new_test_ext(), || { - Executive::execute_block(Block { - header: Header { - parent_hash: [69u8; 32].into(), - number: 1, - state_root: hex!("d1d3da2b1efb1a6ef740b8cdef52e4cf3c6dade6f8a360969fd7ef0034c53b54").into(), - extrinsics_root: [0u8; 32].into(), - digest: Digest { logs: vec![], }, - }, - extrinsics: vec![], - }); - }); - } - - #[test] - fn bad_extrinsic_not_inserted() { - let mut t = new_test_ext(); - let xt = primitives::testing::TestXt(Some(1), 42, Call::transfer(33.into(), 69)); - with_externalities(&mut t, || { - Executive::initialise_block(&Header::new(1, H256::default(), H256::default(), [69u8; 32].into(), Digest::default())); - assert!(Executive::apply_extrinsic(xt).is_err()); - assert_eq!(>::extrinsic_index(), Some(0)); - }); - } -} diff --git a/substrate/runtime/primitives/Cargo.toml b/substrate/runtime/primitives/Cargo.toml deleted file mode 100644 index e7adc094d6789..0000000000000 --- a/substrate/runtime/primitives/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ -[package] -name = "substrate-runtime-primitives" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -num-traits = { version = "0.2", default_features = false } -integer-sqrt = { git = "https://github.com/paritytech/integer-sqrt-rs.git", branch = "master" } -serde = { version = "1.0", optional = true } -serde_derive = { version = "1.0", optional = true } -substrate-codec = { path = "../../codec", default_features = false } -substrate-codec-derive = { path = "../../codec/derive", default_features = false } -substrate-primitives = { path = "../../primitives", default_features = false } -substrate-runtime-std = { path = "../../runtime-std", default_features = false } -substrate-runtime-io = { path = "../../runtime-io", default_features = false } -substrate-runtime-support = { path = "../../runtime-support", default_features = false } -log = {version = "0.3", optional = true } - -[dev-dependencies] -serde_json = "1.0" - -[features] -default = ["std"] -std = [ - "num-traits/std", - "serde", - "serde_derive", - "log", - "substrate-runtime-std/std", - "substrate-runtime-io/std", - "substrate-runtime-support/std", - "substrate-codec/std", - "substrate-primitives/std", -] diff --git a/substrate/runtime/primitives/src/bft.rs b/substrate/runtime/primitives/src/bft.rs deleted file mode 100644 index 17eb54ab10531..0000000000000 --- a/substrate/runtime/primitives/src/bft.rs +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright 2017 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 . - -//! Message formats for the BFT consensus layer. - -use rstd::prelude::*; -use codec::{Decode, Encode, Input, Output}; -use substrate_primitives::{AuthorityId, Signature}; - -/// Type alias for extracting message type from block. -pub type ActionFor = Action::Hash>; - -/// Actions which can be taken during the BFT process. -#[derive(Clone, PartialEq, Eq, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] -pub enum Action { - /// Proposal of a block candidate. - #[codec(index = "1")] - Propose(u32, Block), - /// Proposal header of a block candidate. Accompanies any proposal, - /// but is used for misbehavior reporting since blocks themselves are big. - #[codec(index = "2")] - ProposeHeader(u32, H), - /// Preparation to commit for a candidate. - #[codec(index = "3")] - Prepare(u32, H), - /// Vote to commit to a candidate. - #[codec(index = "4")] - Commit(u32, H), - /// Vote to advance round after inactive primary. - #[codec(index = "5")] - AdvanceRound(u32), -} - -/// Type alias for extracting message type from block. -pub type MessageFor = Message::Hash>; - -/// Messages exchanged between participants in the BFT consensus. -#[derive(Clone, PartialEq, Eq, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] -pub struct Message { - /// The parent header hash this action is relative to. - pub parent: Hash, - /// The action being broadcasted. - pub action: Action, -} - -/// Justification of a block. -#[derive(Clone, PartialEq, Eq, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] -pub struct Justification { - /// The round consensus was reached in. - pub round_number: u32, - /// The hash of the header justified. - pub hash: H, - /// The signatures and signers of the hash. - pub signatures: Vec<(AuthorityId, Signature)> -} - -// single-byte code to represent misbehavior kind. -#[repr(i8)] -enum MisbehaviorCode { - /// BFT: double prepare. - BftDoublePrepare = 0x11, - /// BFT: double commit. - BftDoubleCommit = 0x12, -} - -impl MisbehaviorCode { - fn from_i8(x: i8) -> Option { - match x { - 0x11 => Some(MisbehaviorCode::BftDoublePrepare), - 0x12 => Some(MisbehaviorCode::BftDoubleCommit), - _ => None, - } - } -} - -/// Misbehavior kinds. -#[derive(Clone, PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] -pub enum MisbehaviorKind { - /// BFT: double prepare. - BftDoublePrepare(u32, (Hash, Signature), (Hash, Signature)), - /// BFT: double commit. - BftDoubleCommit(u32, (Hash, Signature), (Hash, Signature)), -} - -impl Encode for MisbehaviorKind { - fn encode_to(&self, dest: &mut T) { - match *self { - MisbehaviorKind::BftDoublePrepare(ref round, (ref h_a, ref s_a), (ref h_b, ref s_b)) => { - dest.push(&(MisbehaviorCode::BftDoublePrepare as i8)); - dest.push(round); - dest.push(h_a); - dest.push(s_a); - dest.push(h_b); - dest.push(s_b); - } - MisbehaviorKind::BftDoubleCommit(ref round, (ref h_a, ref s_a), (ref h_b, ref s_b)) => { - dest.push(&(MisbehaviorCode::BftDoubleCommit as i8)); - dest.push(round); - dest.push(h_a); - dest.push(s_a); - dest.push(h_b); - dest.push(s_b); - } - } - } -} -impl Decode for MisbehaviorKind { - fn decode(input: &mut I) -> Option { - Some(match i8::decode(input).and_then(MisbehaviorCode::from_i8)? { - MisbehaviorCode::BftDoublePrepare => { - MisbehaviorKind::BftDoublePrepare( - u32::decode(input)?, - (Hash::decode(input)?, Signature::decode(input)?), - (Hash::decode(input)?, Signature::decode(input)?), - ) - } - MisbehaviorCode::BftDoubleCommit => { - MisbehaviorKind::BftDoubleCommit( - u32::decode(input)?, - (Hash::decode(input)?, Signature::decode(input)?), - (Hash::decode(input)?, Signature::decode(input)?), - ) - } - }) - } -} - - -/// A report of misbehavior by an authority. -#[derive(Clone, PartialEq, Eq, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] -pub struct MisbehaviorReport { - /// The parent hash of the block where the misbehavior occurred. - pub parent_hash: Hash, - /// The parent number of the block where the misbehavior occurred. - pub parent_number: Number, - /// The authority who misbehavior. - pub target: AuthorityId, - /// The misbehavior kind. - pub misbehavior: MisbehaviorKind, -} - -#[cfg(test)] -mod test { - use super::*; - use substrate_primitives::H256; - - #[test] - fn misbehavior_report_roundtrip() { - let report = MisbehaviorReport:: { - parent_hash: [0; 32].into(), - parent_number: 999, - target: [1; 32].into(), - misbehavior: MisbehaviorKind::BftDoubleCommit( - 511, - ([2; 32].into(), [3; 64].into()), - ([4; 32].into(), [5; 64].into()), - ), - }; - - let encoded = report.encode(); - assert_eq!(MisbehaviorReport::::decode(&mut &encoded[..]).unwrap(), report); - - let report = MisbehaviorReport:: { - parent_hash: [0; 32].into(), - parent_number: 999, - target: [1; 32].into(), - misbehavior: MisbehaviorKind::BftDoublePrepare( - 511, - ([2; 32].into(), [3; 64].into()), - ([4; 32].into(), [5; 64].into()), - ), - }; - - let encoded = report.encode(); - assert_eq!(MisbehaviorReport::::decode(&mut &encoded[..]).unwrap(), report); - } -} diff --git a/substrate/runtime/primitives/src/generic/block.rs b/substrate/runtime/primitives/src/generic/block.rs deleted file mode 100644 index 40538a392c3d0..0000000000000 --- a/substrate/runtime/primitives/src/generic/block.rs +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2017 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 a block and associated items. - -#[cfg(feature = "std")] -use std::fmt; - -use rstd::prelude::*; -use codec::Codec; -use traits::{self, Member, Block as BlockT, Header as HeaderT}; -use bft::Justification; - -/// Something to identify a block. -#[derive(PartialEq, Eq, Clone)] -#[cfg_attr(feature = "std", derive(Debug, Serialize))] -#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] -#[cfg_attr(feature = "std", serde(deny_unknown_fields))] -pub enum BlockId { - /// Identify by block header hash. - Hash(<::Header as HeaderT>::Hash), - /// Identify by block number. - Number(<::Header as HeaderT>::Number), -} - -impl BlockId { - /// Create a block ID from a hash. - pub fn hash(hash: Block::Hash) -> Self { - BlockId::Hash(hash) - } - - /// Create a block ID from a number. - pub fn number(number: ::Number) -> Self { - BlockId::Number(number) - } -} - -impl Copy for BlockId {} - -#[cfg(feature = "std")] -impl fmt::Display for BlockId { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(f, "{:?}", self) - } -} - -/// Abstraction over a substrate block. -#[derive(PartialEq, Eq, Clone, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] -#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] -#[cfg_attr(feature = "std", serde(deny_unknown_fields))] -pub struct Block { - /// The block header. - pub header: Header, - /// The accompanying extrinsics. - pub extrinsics: Vec, -} - -impl traits::Block for Block -where - Header: HeaderT, - Extrinsic: Member + Codec, -{ - type Extrinsic = Extrinsic; - type Header = Header; - type Hash = ::Hash; - - fn header(&self) -> &Self::Header { - &self.header - } - fn extrinsics(&self) -> &[Self::Extrinsic] { - &self.extrinsics[..] - } - fn deconstruct(self) -> (Self::Header, Vec) { - (self.header, self.extrinsics) - } - fn new(header: Self::Header, extrinsics: Vec) -> Self { - Block { header, extrinsics } - } -} - -/// Abstraction over a substrate block and justification. -#[derive(PartialEq, Eq, Clone, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] -#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] -#[cfg_attr(feature = "std", serde(deny_unknown_fields))] -pub struct SignedBlock { - /// Full block. - pub block: Block, - /// Block header justification. - pub justification: Justification, -} diff --git a/substrate/runtime/primitives/src/generic/checked_extrinsic.rs b/substrate/runtime/primitives/src/generic/checked_extrinsic.rs deleted file mode 100644 index e5aee32a02c58..0000000000000 --- a/substrate/runtime/primitives/src/generic/checked_extrinsic.rs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2017 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 extrinsic that has passed the verification -//! stage. - -use runtime_support::Dispatchable; -use traits::{self, Member, SimpleArithmetic, MaybeDisplay}; - -/// 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(Serialize, Deserialize, Debug))] -pub struct CheckedExtrinsic { - /// Who this purports to be from, if anyone (note this is not a signature). - pub signed: Option, - /// The number of extrinsics have come before from the same signer. - pub index: Index, - /// The function that should be called. - pub function: Call, -} - -impl traits::Applyable - for CheckedExtrinsic -where - AccountId: Member + MaybeDisplay, - Index: Member + MaybeDisplay + SimpleArithmetic, - Call: Member + Dispatchable, - ::Origin: From> -{ - type Index = Index; - type AccountId = AccountId; - - fn index(&self) -> &Self::Index { - &self.index - } - - fn sender(&self) -> Option<&Self::AccountId> { - self.signed.as_ref() - } - - fn apply(self) -> Result<(), &'static str> { - self.function.dispatch(self.signed.into()) - } -} diff --git a/substrate/runtime/primitives/src/generic/digest.rs b/substrate/runtime/primitives/src/generic/digest.rs deleted file mode 100644 index 0616ec8914e39..0000000000000 --- a/substrate/runtime/primitives/src/generic/digest.rs +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright 2017 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 a digest. - -use rstd::prelude::*; - -use codec::{Decode, Encode, Codec, Input}; -use traits::{self, Member, DigestItem as DigestItemT}; - -#[derive(PartialEq, Eq, Clone, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] -pub struct Digest { - pub logs: Vec, -} - -impl Default for Digest { - fn default() -> Self { - Digest { logs: Vec::new(), } - } -} - -impl traits::Digest for Digest where - Item: DigestItemT + Codec -{ - type Item = Item; - - fn logs(&self) -> &[Self::Item] { - &self.logs - } - - fn push(&mut self, item: Self::Item) { - self.logs.push(item); - } -} - -/// Digest item that is able to encode/decode 'system' digest items and -/// provide opaque access to other items. -#[derive(PartialEq, Eq, Clone)] -#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] -pub enum DigestItem { - /// System digest item announcing that authorities set has been changed - /// in the block. Contains the new set of authorities. - AuthoritiesChange(Vec), - /// Any 'non-system' digest item, opaque to the native code. - Other(Vec), -} - -/// A 'referencing view' for digest item. Does not own its contents. Used by -/// final runtime implementations for encoding/decoding its log items. -#[derive(PartialEq, Eq, Clone)] -#[cfg_attr(feature = "std", derive(Debug))] -pub enum DigestItemRef<'a, AuthorityId: 'a> { - /// Reference to `DigestItem::AuthoritiesChange`. - AuthoritiesChange(&'a [AuthorityId]), - /// Reference to `DigestItem::Other`. - Other(&'a Vec), -} - -/// Type of the digest item. Used to gain explicit control over `DigestItem` encoding -/// process. We need an explicit control, because final runtimes are encoding their own -/// digest items using `DigestItemRef` type and we can't auto-derive `Decode` -/// trait for `DigestItemRef`. -#[repr(u32)] -#[derive(Encode, Decode)] -enum DigestItemType { - Other = 0, - AuthoritiesChange, -} - -impl DigestItem { - /// Returns Some if `self` is a `DigestItem::Other`. - pub fn as_other(&self) -> Option<&Vec> { - match *self { - DigestItem::Other(ref v) => Some(v), - _ => None, - } - } - - /// Returns a 'referencing view' for this digest item. - fn dref<'a>(&'a self) -> DigestItemRef<'a, AuthorityId> { - match *self { - DigestItem::AuthoritiesChange(ref v) => DigestItemRef::AuthoritiesChange(v), - DigestItem::Other(ref v) => DigestItemRef::Other(v), - } - } -} - -impl traits::DigestItem for DigestItem { - type AuthorityId = AuthorityId; - - fn as_authorities_change(&self) -> Option<&[Self::AuthorityId]> { - match *self { - DigestItem::AuthoritiesChange(ref authorities) => Some(authorities), - _ => None, - } - } -} - -impl Encode for DigestItem { - fn encode(&self) -> Vec { - self.dref().encode() - } -} - -impl Decode for DigestItem { - fn decode(input: &mut I) -> Option { - let item_type: DigestItemType = Decode::decode(input)?; - match item_type { - DigestItemType::AuthoritiesChange => Some(DigestItem::AuthoritiesChange( - Decode::decode(input)?, - )), - DigestItemType::Other => Some(DigestItem::Other( - Decode::decode(input)?, - )), - } - } -} - -impl<'a, AuthorityId: Encode> Encode for DigestItemRef<'a, AuthorityId> { - fn encode(&self) -> Vec { - let mut v = Vec::new(); - - match *self { - DigestItemRef::AuthoritiesChange(authorities) => { - DigestItemType::AuthoritiesChange.encode_to(&mut v); - authorities.encode_to(&mut v); - }, - DigestItemRef::Other(val) => { - DigestItemType::Other.encode_to(&mut v); - val.encode_to(&mut v); - }, - } - - v - } -} diff --git a/substrate/runtime/primitives/src/generic/header.rs b/substrate/runtime/primitives/src/generic/header.rs deleted file mode 100644 index 8425a17bb7b26..0000000000000 --- a/substrate/runtime/primitives/src/generic/header.rs +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright 2017 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 a block header. - -#[cfg(feature = "std")] -use serde::{Deserialize, Deserializer}; - -use codec::{Decode, Encode, Codec, Input, Output}; -use traits::{self, Member, SimpleArithmetic, SimpleBitOps, MaybeDisplay, - Hash as HashT, DigestItem as DigestItemT}; -use generic::Digest; - -/// Abstraction over a block header for a substrate chain. -#[derive(PartialEq, Eq, Clone)] -#[cfg_attr(feature = "std", derive(Debug, Serialize))] -#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] -#[cfg_attr(feature = "std", serde(deny_unknown_fields))] -pub struct Header { - /// The parent hash. - pub parent_hash: ::Output, - /// The block number. - pub number: Number, - /// The state trie merkle root - pub state_root: ::Output, - /// The merkle root of the extrinsics. - pub extrinsics_root: ::Output, - /// A chain-specific digest of data useful for light clients or referencing auxiliary data. - pub digest: Digest, -} - -// Hack to work around the fact that deriving deserialize doesn't work nicely with -// the `hashing` trait used as a parameter. -// dummy struct that uses the hash type directly. -// https://github.com/serde-rs/serde/issues/1296 -#[cfg(feature = "std")] -#[serde(rename_all = "camelCase")] -#[derive(Deserialize)] -struct DeserializeHeader { - parent_hash: H, - number: N, - state_root: H, - extrinsics_root: H, - digest: Digest, -} - -#[cfg(feature = "std")] -impl From> for Header { - fn from(other: DeserializeHeader) -> Self { - Header { - parent_hash: other.parent_hash, - number: other.number, - state_root: other.state_root, - extrinsics_root: other.extrinsics_root, - digest: other.digest, - } - } -} - -#[cfg(feature = "std")] -impl<'a, Number: 'a, Hash: 'a + HashT, DigestItem: 'a> Deserialize<'a> for Header where - Number: Deserialize<'a>, - Hash::Output: Deserialize<'a>, - DigestItem: Deserialize<'a>, -{ - fn deserialize>(de: D) -> Result { - DeserializeHeader::::deserialize(de).map(Into::into) - } -} - -// TODO [ToDr] Issue with bounds -impl Decode for Header where - Number: Decode, - Hash: HashT, - Hash::Output: Decode, - DigestItem: DigestItemT + Decode, -{ - fn decode(input: &mut I) -> Option { - Some(Header { - parent_hash: Decode::decode(input)?, - number: Decode::decode(input)?, - state_root: Decode::decode(input)?, - extrinsics_root: Decode::decode(input)?, - digest: Decode::decode(input)?, - }) - } -} - -impl Encode for Header where - Number: Encode, - Hash: HashT, - Hash::Output: Encode, - DigestItem: DigestItemT + Encode, -{ - fn encode_to(&self, dest: &mut T) { - dest.push(&self.parent_hash); - dest.push(&self.number); - dest.push(&self.state_root); - dest.push(&self.extrinsics_root); - dest.push(&self.digest); - } -} - -impl traits::Header for Header where - Number: Member + ::rstd::hash::Hash + Copy + Codec + MaybeDisplay + SimpleArithmetic + Codec, - Hash: HashT, - DigestItem: DigestItemT + Codec, - Hash::Output: Default + ::rstd::hash::Hash + Copy + Member + MaybeDisplay + SimpleBitOps + Codec, - { - type Number = Number; - type Hash = ::Output; - type Hashing = Hash; - type Digest = Digest; - - fn number(&self) -> &Self::Number { &self.number } - fn set_number(&mut self, num: Self::Number) { self.number = num } - - fn extrinsics_root(&self) -> &Self::Hash { &self.extrinsics_root } - fn set_extrinsics_root(&mut self, root: Self::Hash) { self.extrinsics_root = root } - - fn state_root(&self) -> &Self::Hash { &self.state_root } - fn set_state_root(&mut self, root: Self::Hash) { self.state_root = root } - - fn parent_hash(&self) -> &Self::Hash { &self.parent_hash } - fn set_parent_hash(&mut self, hash: Self::Hash) { self.parent_hash = hash } - - fn digest(&self) -> &Self::Digest { &self.digest } - fn set_digest(&mut self, digest: Self::Digest) { self.digest = digest } - - fn new( - number: Self::Number, - extrinsics_root: Self::Hash, - state_root: Self::Hash, - parent_hash: Self::Hash, - digest: Self::Digest - ) -> Self { - Header { - number, extrinsics_root: extrinsics_root, state_root, parent_hash, digest - } - } -} - -impl Header where - Number: Member + ::rstd::hash::Hash + Copy + Codec + MaybeDisplay + SimpleArithmetic + Codec, - Hash: HashT, - DigestItem: DigestItemT + Codec, - Hash::Output: Default + ::rstd::hash::Hash + Copy + Member + MaybeDisplay + SimpleBitOps + Codec, - { - /// Convenience helper for computing the hash of the header without having - /// to import the trait. - pub fn hash(&self) -> Hash::Output { - Hash::hash_of(self) - } -} diff --git a/substrate/runtime/primitives/src/generic/mod.rs b/substrate/runtime/primitives/src/generic/mod.rs deleted file mode 100644 index 23907d9f6cb7f..0000000000000 --- a/substrate/runtime/primitives/src/generic/mod.rs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -// tag::description[] -//! Generic implementations of Extrinsic/Header/Block. -// end::description[] - -mod unchecked_extrinsic; -mod checked_extrinsic; -mod header; -mod block; -mod digest; -#[cfg(test)] -mod tests; - -pub use self::unchecked_extrinsic::UncheckedExtrinsic; -pub use self::checked_extrinsic::CheckedExtrinsic; -pub use self::header::Header; -pub use self::block::{Block, SignedBlock, BlockId}; -pub use self::digest::{Digest, DigestItem, DigestItemRef}; diff --git a/substrate/runtime/primitives/src/generic/tests.rs b/substrate/runtime/primitives/src/generic/tests.rs deleted file mode 100644 index 6d22357d1902c..0000000000000 --- a/substrate/runtime/primitives/src/generic/tests.rs +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2017 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 generic implementations of Extrinsic/Header/Block. - -use codec::{Decode, Encode}; -use substrate_primitives::{H256, H512}; -use super::{Digest, Header, DigestItem, UncheckedExtrinsic}; - -type Block = super::Block< - Header>, - UncheckedExtrinsic, ->; - -#[test] -fn block_roundtrip_serialization() { - let block: Block = Block { - header: Header { - parent_hash: [0u8; 32].into(), - number: 100_000, - state_root: [1u8; 32].into(), - extrinsics_root: [2u8; 32].into(), - digest: Digest { logs: vec![ - DigestItem::Other::(vec![1, 2, 3]), - DigestItem::Other::(vec![4, 5, 6]), - ] }, - }, - extrinsics: vec![ - UncheckedExtrinsic::new_signed( - 0, - 100, - [255u8; 32].into(), - H512::from([0u8; 64]).into() - ), - UncheckedExtrinsic::new_signed( - 100, - 99, - [128u8; 32].into(), - H512::from([255u8; 64]).into() - ) - ] - }; - - { - let encoded = ::serde_json::to_vec(&block).unwrap(); - let decoded: Block = ::serde_json::from_slice(&encoded).unwrap(); - - assert_eq!(block, decoded); - } - { - let encoded = block.encode(); - let decoded = Block::decode(&mut &encoded[..]).unwrap(); - - assert_eq!(block, decoded); - } -} - -#[test] -fn system_digest_item_encoding() { - let item = DigestItem::AuthoritiesChange::(vec![10, 20, 30]); - let encoded = item.encode(); - assert_eq!(encoded, vec![ - // type = DigestItemType::AuthoritiesChange - 1, - // number of items in athorities set - 3, 0, 0, 0, - // authorities - 10, 0, 0, 0, - 20, 0, 0, 0, - 30, 0, 0, 0, - ]); - - let decoded: DigestItem = Decode::decode(&mut &encoded[..]).unwrap(); - assert_eq!(item, decoded); -} - -#[test] -fn non_system_digest_item_encoding() { - let item = DigestItem::Other::(vec![10, 20, 30]); - let encoded = item.encode(); - assert_eq!(encoded, vec![ - // type = DigestItemType::Other - 0, - // length of other data - 3, 0, 0, 0, - // authorities - 10, 20, 30, - ]); - - let decoded: DigestItem = Decode::decode(&mut &encoded[..]).unwrap(); - assert_eq!(item, decoded); -} \ No newline at end of file diff --git a/substrate/runtime/primitives/src/generic/unchecked_extrinsic.rs b/substrate/runtime/primitives/src/generic/unchecked_extrinsic.rs deleted file mode 100644 index c7e3693fbf187..0000000000000 --- a/substrate/runtime/primitives/src/generic/unchecked_extrinsic.rs +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2017 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 codec::{Decode, Encode, Input}; -use traits::{self, Member, SimpleArithmetic, MaybeDisplay}; -use super::CheckedExtrinsic; - -/// A extrinsic right from the external world. This is unchecked and so -/// can contain a signature. -#[derive(PartialEq, Eq, Clone)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -pub struct UncheckedExtrinsic { - /// The signature and address, if this is a signed extrinsic. - pub signature: Option<(Address, Signature)>, - /// The number of extrinsics have come before from the same signer. - pub index: Index, - /// The function that should be called. - pub function: Call, -} - -impl UncheckedExtrinsic { - /// New instance of a signed extrinsic aka "transaction". - pub fn new_signed(index: Index, function: Call, signed: Address, signature: Signature) -> Self { - UncheckedExtrinsic { - signature: Some((signed, signature)), - index, - function, - } - } - - /// New instance of an unsigned extrinsic aka "inherent". - pub fn new_unsigned(index: Index, function: Call) -> Self { - UncheckedExtrinsic { - signature: None, - index, - function, - } - } - - /// `true` if there is a signature. - pub fn is_signed(&self) -> bool { - self.signature.is_some() - } -} - -impl traits::Checkable - for UncheckedExtrinsic -where - Address: Member + MaybeDisplay, - Index: Encode + Member + MaybeDisplay + SimpleArithmetic, - Call: Encode + Member, - Signature: Member + traits::Verify, - AccountId: Member + MaybeDisplay, - ThisLookup: FnOnce(Address) -> Result, -{ - type Checked = CheckedExtrinsic; - - fn check_with(self, lookup: ThisLookup) -> Result { - Ok(match self.signature { - Some((signed, signature)) => { - let payload = (self.index, self.function); - let signed = lookup(signed)?; - if !::verify_encoded_lazy(&signature, &payload, &signed) { - return Err("bad signature in extrinsic") - } - CheckedExtrinsic { - signed: Some(signed), - index: payload.0, - function: payload.1, - } - } - None => CheckedExtrinsic { - signed: None, - index: self.index, - function: self.function, - }, - }) - } -} - -impl Decode - for UncheckedExtrinsic -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 u32, which has the total number of bytes following (we don't need - // to use this). - let _length_do_not_remove_me_see_above: u32 = Decode::decode(input)?; - - Some(UncheckedExtrinsic { - signature: Decode::decode(input)?, - index: Decode::decode(input)?, - function: Decode::decode(input)?, - }) - } -} - -impl Encode - for UncheckedExtrinsic -where - Address: Encode, - Signature: Encode, - Index: Encode, - Call: Encode, -{ - fn encode(&self) -> Vec { - let mut v = Vec::new(); - - // need to prefix with the total length as u32 to ensure it's binary comptible with - // Vec. we'll make room for it here, then overwrite once we know the length. - v.extend(&[0u8; 4]); - - self.signature.encode_to(&mut v); - self.index.encode_to(&mut v); - self.function.encode_to(&mut v); - - let length = (v.len() - 4) as u32; - length.using_encoded(|s| v[0..4].copy_from_slice(s)); - - v - } -} - -/// TODO: use derive when possible. -#[cfg(feature = "std")] -impl fmt::Debug for UncheckedExtrinsic where - Address: fmt::Debug, - Index: fmt::Debug, - Call: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "UncheckedExtrinsic({:?}, {:?}, {:?})", self.signature.as_ref().map(|x| &x.0), self.function, self.index) - } -} diff --git a/substrate/runtime/primitives/src/lib.rs b/substrate/runtime/primitives/src/lib.rs deleted file mode 100644 index e9bddc3286d87..0000000000000 --- a/substrate/runtime/primitives/src/lib.rs +++ /dev/null @@ -1,434 +0,0 @@ -// Copyright 2017 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 . - -//! System manager: Handles all of the top-level stuff; executing block/transaction, setting code -//! and depositing logs. - -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(feature = "std")] -extern crate serde; - -#[cfg(feature = "std")] -#[macro_use] -extern crate serde_derive; - -#[cfg(feature = "std")] -#[macro_use] -extern crate log; - -#[macro_use] -extern crate substrate_codec_derive; - -extern crate num_traits; -extern crate integer_sqrt; -extern crate substrate_runtime_std as rstd; -extern crate substrate_runtime_io as runtime_io; -extern crate substrate_runtime_support as runtime_support; -extern crate substrate_codec as codec; -extern crate substrate_primitives; - -#[cfg(test)] -extern crate serde_json; - -#[cfg(feature = "std")] -use std::collections::HashMap; - -use rstd::prelude::*; -use substrate_primitives::hash::{H256, H512}; - -#[cfg(feature = "std")] -use substrate_primitives::hexdisplay::ascii_format; - -#[cfg(feature = "std")] -pub mod testing; - -pub mod traits; -pub mod generic; -pub mod bft; - -use traits::{Verify, Lazy}; - -#[cfg(feature = "std")] -pub use serde::{Serialize, de::DeserializeOwned}; - -/// A set of key value pairs for storage. -#[cfg(feature = "std")] -pub type StorageMap = HashMap, Vec>; - -/// Complex storage builder stuff. -#[cfg(feature = "std")] -pub trait BuildStorage { - fn hash(data: &[u8]) -> [u8; 16] { - let r = runtime_io::twox_128(data); - trace!(target: "build_storage", "{} <= {}", substrate_primitives::hexdisplay::HexDisplay::from(&r), ascii_format(data)); - r - } - fn build_storage(self) -> Result; -} - -#[cfg(feature = "std")] -impl BuildStorage for StorageMap { - fn build_storage(self) -> Result { - Ok(self) - } -} - -/// Permill is parts-per-million (i.e. after multiplying by this, divide by 1000000). -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] -#[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq)] -pub struct Permill(u32); - -// TODO: impl Mul for N where N: As -impl Permill { - pub fn times + ::rstd::ops::Mul + ::rstd::ops::Div>(self, b: N) -> N { - // TODO: handle overflows - b * >::sa(self.0 as usize) / >::sa(1000000) - } - - pub fn from_millionths(x: u32) -> Permill { Permill(x) } - - pub fn from_percent(x: u32) -> Permill { Permill(x * 10_000) } - - #[cfg(feature = "std")] - pub fn from_fraction(x: f64) -> Permill { Permill((x * 1_000_000.0) as u32) } -} - -#[cfg(feature = "std")] -impl From for Permill { - fn from(x: f64) -> Permill { - Permill::from_fraction(x) - } -} - -#[cfg(feature = "std")] -impl From for Permill { - fn from(x: f32) -> Permill { - Permill::from_fraction(x as f64) - } -} - -/// Ed25519 signature verify. -#[derive(Eq, PartialEq, Clone, Default, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] -pub struct Ed25519Signature(pub H512); - -impl Verify for Ed25519Signature { - type Signer = H256; - fn verify>(&self, mut msg: L, signer: &Self::Signer) -> bool { - runtime_io::ed25519_verify(&(self.0).0, msg.get(), &signer.0[..]) - } -} - -impl From for Ed25519Signature { - fn from(h: H512) -> Ed25519Signature { - Ed25519Signature(h) - } -} - -#[derive(Eq, PartialEq, Clone, Copy, Decode)] -#[cfg_attr(feature = "std", derive(Debug, Serialize))] -#[repr(u8)] -/// Outcome of a valid extrinsic application. Capable of being sliced. -pub enum ApplyOutcome { - /// Successful application (extrinsic reported no issue). - Success = 0, - /// Failed application (extrinsic was probably a no-op other than fees). - Fail = 1, -} - -impl codec::Encode for ApplyOutcome { - fn using_encoded R>(&self, f: F) -> R { - f(&[*self as u8]) - } -} - -#[derive(Eq, PartialEq, Clone, Copy, Decode)] -#[cfg_attr(feature = "std", derive(Debug, Serialize))] -#[repr(u8)] -/// Reason why an extrinsic couldn't be applied (i.e. invalid extrinsic). -pub enum ApplyError { - /// Bad signature. - BadSignature = 0, - /// Nonce too low. - Stale = 1, - /// Nonce too high. - Future = 2, - /// Sending account had too low a balance. - CantPay = 3, -} - -impl codec::Encode for ApplyError { - fn using_encoded R>(&self, f: F) -> R { - f(&[*self as u8]) - } -} - -/// Result from attempt to apply an extrinsic. -pub type ApplyResult = Result; - -/// Verify a signature on an encoded value in a lazy manner. This can be -/// an optimization if the signature scheme has an "unsigned" escape hash. -pub fn verify_encoded_lazy(sig: &V, item: &T, signer: &V::Signer) -> bool { - // The `Lazy` trait expresses something like `X: FnMut &'a T>`. - // unfortunately this is a lifetime relationship that can't - // be expressed without generic associated types, better unification of HRTBs in type position, - // and some kind of integration into the Fn* traits. - struct LazyEncode { - inner: F, - encoded: Option>, - } - - impl Vec> traits::Lazy<[u8]> for LazyEncode { - fn get(&mut self) -> &[u8] { - self.encoded.get_or_insert_with(&self.inner).as_slice() - } - } - - sig.verify( - LazyEncode { inner: || item.encode(), encoded: None }, - signer, - ) -} - -#[macro_export] -macro_rules! __impl_outer_config_types { - ($concrete:ident $config:ident $snake:ident $($rest:ident)*) => { - #[cfg(any(feature = "std", test))] - pub type $config = $snake::GenesisConfig<$concrete>; - __impl_outer_config_types! {$concrete $($rest)*} - }; - ($concrete:ident) => () -} - -#[macro_export] -/// Implement the output "meta" module configuration struct. -macro_rules! impl_outer_config { - ( pub struct $main:ident for $concrete:ident { $( $config:ident => $snake:ident, )* } ) => { - __impl_outer_config_types! { $concrete $( $config $snake )* } - #[cfg(any(feature = "std", test))] - #[derive(Serialize, Deserialize)] - #[serde(rename_all = "camelCase")] - #[serde(deny_unknown_fields)] - pub struct $main { - $( - pub $snake: Option<$config>, - )* - } - #[cfg(any(feature = "std", test))] - impl $crate::BuildStorage for $main { - fn build_storage(self) -> ::std::result::Result<$crate::StorageMap, String> { - let mut s = $crate::StorageMap::new(); - $( - if let Some(extra) = self.$snake { - s.extend(extra.build_storage()?); - } - )* - Ok(s) - } - } - } -} - -/// Generates enum that contains all possible log entries for the runtime. -/// Every individual module of the runtime that is mentioned, must -/// expose a `Log` and `RawLog` enums. -/// -/// Generated enum is binary-compatible with and could be interpreted -/// as `generic::DigestItem`. -/// -/// Requires `use runtime_primitives::generic;` to be used. -/// -/// Runtime requirements: -/// 1) binary representation of all supported 'system' log items should stay -/// the same. Otherwise, the native code will be unable to read log items -/// generated by previous runtime versions -/// 2) the support of 'system' log items should never be dropped by runtime. -/// Otherwise, native code will lost its ability to read items of this type -/// even if they were generated by the versions which have supported these -/// items. -#[macro_export] -macro_rules! impl_outer_log { - ( - $(#[$attr:meta])* - pub enum $name:ident ($internal:ident: DigestItem<$( $genarg:ty ),*>) for $trait:ident { - $( $module:ident($( $item:ident ),*) ),* - } - ) => { - /// Wrapper for all possible log entries for the `$trait` runtime. Provides binary-compatible - /// `Encode`/`Decode` implementations with the corresponding `generic::DigestItem`. - #[derive(Clone, PartialEq, Eq)] - #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] - $(#[$attr])* - #[allow(non_camel_case_types)] - pub struct $name($internal); - - /// All possible log entries for the `$trait` runtime. `Encode`/`Decode` implementations - /// are auto-generated => it is not binary-compatible with `generic::DigestItem`. - #[derive(Clone, PartialEq, Eq, Encode, Decode)] - #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] - $(#[$attr])* - #[allow(non_camel_case_types)] - enum $internal { - $( - $module($module::Log<$trait>), - )* - } - - impl $name { - /// Try to convert `$name` into `generic::DigestItemRef`. Returns Some when - /// `self` is a 'system' log && it has been marked as 'system' in macro call. - /// Otherwise, None is returned. - #[allow(unreachable_patterns)] - fn dref<'a>(&'a self) -> Option> { - match self.0 { - $($( - $internal::$module($module::RawLog::$item(ref v)) => - Some(generic::DigestItemRef::$item(v)), - )*)* - _ => None, - } - } - } - - impl From> for $name { - /// Converts `generic::DigestItem` into `$name`. If `generic::DigestItem` represents - /// a system item which is supported by the runtime, it is returned. - /// Otherwise we expect a `Other` log item. Trying to convert from anything other - /// will lead to panic in runtime, since the runtime does not supports this 'system' - /// log item. - #[allow(unreachable_patterns)] - fn from(gen: generic::DigestItem<$($genarg),*>) -> Self { - match gen { - $($( - generic::DigestItem::$item(value) => - $name($internal::$module($module::RawLog::$item(value))), - )*)* - _ => gen.as_other() - .and_then(|value| Decode::decode(&mut &value[..])) - .map($name) - .expect("not allowed to fail in runtime"), - } - } - } - - impl Decode for $name { - /// `generic::DigestItem` binray compatible decode. - fn decode(input: &mut I) -> Option { - let gen: generic::DigestItem<$($genarg),*> = Decode::decode(input)?; - Some($name::from(gen)) - } - } - - impl Encode for $name { - /// `generic::DigestItem` binray compatible encode. - fn encode(&self) -> Vec { - match self.dref() { - Some(dref) => dref.encode(), - None => { - let gen: generic::DigestItem<$($genarg),*> = generic::DigestItem::Other(self.0.encode()); - gen.encode() - }, - } - } - } - - $( - impl From<$module::Log<$trait>> for $name { - /// Converts single module log item into `$name`. - fn from(x: $module::Log<$trait>) -> Self { - $name(x.into()) - } - } - - impl From<$module::Log<$trait>> for $internal { - /// Converts single module log item into `$internal`. - fn from(x: $module::Log<$trait>) -> Self { - $internal::$module(x) - } - } - )* - }; -} - -#[cfg(test)] -mod tests { - use codec::{Encode, Decode, Input}; - - pub trait RuntimeT { - type AuthorityId; - } - - pub struct Runtime; - - impl RuntimeT for Runtime { - type AuthorityId = u64; - } - - #[test] - fn impl_outer_log_works() { - mod a { - use super::RuntimeT; - pub type Log = RawLog<::AuthorityId>; - - #[derive(Serialize, Deserialize, Debug, Encode, Decode, PartialEq, Eq, Clone)] - pub enum RawLog { A1(AuthorityId), AuthoritiesChange(Vec), A3(AuthorityId) } - } - - mod b { - use super::RuntimeT; - pub type Log = RawLog<::AuthorityId>; - - #[derive(Serialize, Deserialize, Debug, Encode, Decode, PartialEq, Eq, Clone)] - pub enum RawLog { B1(AuthorityId), B2(AuthorityId) } - } - - use super::generic; // required before macro invocation - - // TODO try to avoid redundant brackets: a(AuthoritiesChange), b - impl_outer_log! { - pub enum Log(InternalLog: DigestItem) for Runtime { - a(AuthoritiesChange), b() - } - } - - // encode/decode regular item - let b1: Log = b::RawLog::B1::(777).into(); - let encoded_b1 = b1.encode(); - let decoded_b1: Log = Decode::decode(&mut &encoded_b1[..]).unwrap(); - assert_eq!(b1, decoded_b1); - - // encode/decode system item - let auth_change: Log = a::RawLog::AuthoritiesChange::(vec![100, 200, 300]).into(); - let encoded_auth_change = auth_change.encode(); - let decoded_auth_change: Log = Decode::decode(&mut &encoded_auth_change[..]).unwrap(); - assert_eq!(auth_change, decoded_auth_change); - - // interpret regular item using `generic::DigestItem` - let generic_b1: generic::DigestItem = Decode::decode(&mut &encoded_b1[..]).unwrap(); - match generic_b1 { - generic::DigestItem::Other(_) => (), - _ => panic!("unexpected generic_b1: {:?}", generic_b1), - } - - // interpret system item using `generic::DigestItem` - let generic_auth_change: generic::DigestItem = Decode::decode(&mut &encoded_auth_change[..]).unwrap(); - match generic_auth_change { - generic::DigestItem::AuthoritiesChange(authorities) => assert_eq!(authorities, vec![100, 200, 300]), - _ => panic!("unexpected generic_auth_change: {:?}", generic_auth_change), - } - } -} diff --git a/substrate/runtime/primitives/src/testing.rs b/substrate/runtime/primitives/src/testing.rs deleted file mode 100644 index 764828bfe5662..0000000000000 --- a/substrate/runtime/primitives/src/testing.rs +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright 2017 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 . - -//! Testing utilities. - -use serde::{Serialize, de::DeserializeOwned}; -use std::fmt::Debug; -use codec::Codec; -use runtime_support::Dispatchable; -use traits::{self, Checkable, Applyable, BlakeTwo256}; - -pub use substrate_primitives::H256; - -#[derive(Default, PartialEq, Eq, Clone, Serialize, Deserialize, Debug, Encode, Decode)] -pub struct Digest { - pub logs: Vec, -} - -impl traits::Digest for Digest { - type Item = u64; - - fn logs(&self) -> &[Self::Item] { - &self.logs - } - - fn push(&mut self, item: Self::Item) { - self.logs.push(item); - } -} - -impl traits::DigestItem for () { - type AuthorityId = (); -} - -impl traits::DigestItem for u64 { - type AuthorityId = (); -} - -#[derive(PartialEq, Eq, Clone, Serialize, Deserialize, Debug, Encode, Decode)] -#[serde(rename_all = "camelCase")] -#[serde(deny_unknown_fields)] -pub struct Header { - pub parent_hash: H256, - pub number: u64, - pub state_root: H256, - pub extrinsics_root: H256, - pub digest: Digest, -} - -impl traits::Header for Header { - type Number = u64; - type Hashing = BlakeTwo256; - type Hash = H256; - type Digest = Digest; - - fn number(&self) -> &Self::Number { &self.number } - fn set_number(&mut self, num: Self::Number) { self.number = num } - - fn extrinsics_root(&self) -> &Self::Hash { &self.extrinsics_root } - fn set_extrinsics_root(&mut self, root: Self::Hash) { self.extrinsics_root = root } - - fn state_root(&self) -> &Self::Hash { &self.state_root } - fn set_state_root(&mut self, root: Self::Hash) { self.state_root = root } - - fn parent_hash(&self) -> &Self::Hash { &self.parent_hash } - fn set_parent_hash(&mut self, hash: Self::Hash) { self.parent_hash = hash } - - fn digest(&self) -> &Self::Digest { &self.digest } - fn set_digest(&mut self, digest: Self::Digest) { self.digest = digest } - - fn new( - number: Self::Number, - extrinsics_root: Self::Hash, - state_root: Self::Hash, - parent_hash: Self::Hash, - digest: Self::Digest - ) -> Self { - Header { - number, extrinsics_root: extrinsics_root, state_root, parent_hash, digest - } - } -} - -#[derive(PartialEq, Eq, Clone, Serialize, Deserialize, Debug, Encode, Decode)] -pub struct Block { - pub header: Header, - pub extrinsics: Vec, -} - -impl traits::Block for Block { - type Extrinsic = Xt; - type Header = Header; - type Hash =
::Hash; - - fn header(&self) -> &Self::Header { - &self.header - } - fn extrinsics(&self) -> &[Self::Extrinsic] { - &self.extrinsics[..] - } - fn deconstruct(self) -> (Self::Header, Vec) { - (self.header, self.extrinsics) - } - fn new(header: Self::Header, extrinsics: Vec) -> Self { - Block { header, extrinsics } - } -} - -#[derive(PartialEq, Eq, Clone, Serialize, Deserialize, Debug, Encode, Decode)] -pub struct TestXt(pub Option, pub u64, pub Call); - -impl Checkable for TestXt { - type Checked = Self; - fn check_with(self, _: Context) -> Result { Ok(self) } -} -impl Applyable for TestXt where - Call: Sized + Send + Sync + Clone + Eq + Dispatchable + Codec + Debug + Serialize + DeserializeOwned, - ::Origin: From> -{ - type AccountId = u64; - type Index = u64; - fn sender(&self) -> Option<&u64> { self.0.as_ref() } - fn index(&self) -> &u64 { &self.1 } - fn apply(self) -> Result<(), &'static str> { self.2.dispatch(self.0.into()) } -} diff --git a/substrate/runtime/primitives/src/traits.rs b/substrate/runtime/primitives/src/traits.rs deleted file mode 100644 index 32aa47a16f95a..0000000000000 --- a/substrate/runtime/primitives/src/traits.rs +++ /dev/null @@ -1,462 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! Primitives for the runtime modules. - -use rstd::prelude::*; -use rstd::{self, result}; -use runtime_io; -#[cfg(feature = "std")] use std::fmt::{Debug, Display}; -#[cfg(feature = "std")] use serde::{Serialize, de::DeserializeOwned}; -use substrate_primitives; -use substrate_primitives::Blake2Hasher; -use codec::{Codec, Encode}; -pub use integer_sqrt::IntegerSquareRoot; -pub use num_traits::{Zero, One, Bounded}; -pub use num_traits::ops::checked::{CheckedAdd, CheckedSub, CheckedMul, CheckedDiv}; -use rstd::ops::{Add, Sub, Mul, Div, Rem, AddAssign, SubAssign, MulAssign, DivAssign, - RemAssign, Shl, Shr}; - -/// A lazy value. -pub trait Lazy { - fn get(&mut self) -> &T; -} - -impl<'a> Lazy<[u8]> for &'a [u8] { - fn get(&mut self) -> &[u8] { &**self } -} - -/// Means of signature verification. -pub trait Verify { - /// Type of the signer. - type Signer; - /// Verify a signature. Return `true` if signature is valid for the value. - fn verify>(&self, msg: L, signer: &Self::Signer) -> bool; -} - -/// Some sort of check on the origin is performed by this object. -pub trait EnsureOrigin { - type Success; - fn ensure_origin(o: OuterOrigin) -> Result; -} - -/// Means of changing one type into another in a manner dependent on the source type. -pub trait Lookup { - /// Type to lookup from. - type Source; - /// Type to lookup into. - type Target; - /// Attempt a lookup. - fn lookup(s: Self::Source) -> result::Result; -} - -/// Simple payment making trait, operating on a single generic `AccountId` type. -pub trait MakePayment { - /// Make some sort of payment concerning `who` for an extrinsic (transaction) of encoded length - /// `encoded_len` bytes. Return true iff the payment was successful. - fn make_payment(who: &AccountId, encoded_len: usize) -> Result<(), &'static str>; -} - -impl MakePayment for () { - fn make_payment(_: &T, _: usize) -> Result<(), &'static str> { Ok(()) } -} - -/// Extensible conversion trait. Generic over both source and destination types. -pub trait Convert { - /// Make conversion. - fn convert(a: A) -> B; -} - -/// Simple trait similar to `Into`, except that it can be used to convert numerics between each -/// other. -pub trait As { - /// Convert forward (ala `Into::into`). - fn as_(self) -> T; - /// Convert backward (ala `From::from`). - fn sa(T) -> Self; -} - -macro_rules! impl_numerics { - ( $( $t:ty ),* ) => { - $( - impl_numerics!($t: u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize,); - )* - }; - ( $f:ty : $t:ty, $( $rest:ty, )* ) => { - impl As<$t> for $f { - fn as_(self) -> $t { self as $t } - fn sa(t: $t) -> Self { t as Self } - } - impl_numerics!($f: $( $rest, )*); - }; - ( $f:ty : ) => {} -} - -impl_numerics!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize); - -pub struct Identity; -impl Convert for Identity { - fn convert(a: T) -> T { a } -} -impl Convert for () { - fn convert(_: T) -> () { () } -} - -pub trait RefInto { - fn ref_into(&self) -> &T; -} -impl RefInto for T { - fn ref_into(&self) -> &T { &self } -} - -pub trait SimpleArithmetic: - Zero + One + IntegerSquareRoot + As + - Add + AddAssign + - Sub + SubAssign + - Mul + MulAssign + - Div + DivAssign + - Rem + RemAssign + - Shl + Shr + - CheckedAdd + - CheckedSub + - CheckedMul + - CheckedDiv + - PartialOrd + Ord -{} -impl + - Add + AddAssign + - Sub + SubAssign + - Mul + MulAssign + - Div + DivAssign + - Rem + RemAssign + - Shl + Shr + - CheckedAdd + - CheckedSub + - CheckedMul + - CheckedDiv + - PartialOrd + Ord -> SimpleArithmetic for T {} - -/// Trait for things that can be clear (have no bits set). For numeric types, essentially the same -/// as `Zero`. -pub trait Clear { - /// True iff no bits are set. - fn is_clear(&self) -> bool; - - /// Return the value of Self that is clear. - fn clear() -> Self; -} - -impl Clear for T { - fn is_clear(&self) -> bool { *self == Self::clear() } - fn clear() -> Self { Default::default() } -} - -pub trait SimpleBitOps: - Sized + Clear + - rstd::ops::BitOr + - rstd::ops::BitAnd -{} -impl + - rstd::ops::BitAnd -> SimpleBitOps for T {} - -/// The block finalisation trait. Implementing this lets you express what should happen -/// for your module when the block is ending. -pub trait OnFinalise { - /// The block is being finalised. Implement to have something happen. - fn on_finalise(_n: BlockNumber) {} -} - -impl OnFinalise for () {} - -macro_rules! tuple_impl { - ($one:ident,) => { - impl> OnFinalise for ($one,) { - fn on_finalise(n: Number) { - $one::on_finalise(n); - } - } - }; - ($first:ident, $($rest:ident,)+) => { - impl< - Number: Copy, - $first: OnFinalise, - $($rest: OnFinalise),+ - > OnFinalise for ($first, $($rest),+) { - fn on_finalise(n: Number) { - $first::on_finalise(n); - $($rest::on_finalise(n);)+ - } - } - tuple_impl!($($rest,)+); - } -} - -#[allow(non_snake_case)] -tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z,); - -/// Abstraction around hashing -pub trait Hash: 'static + MaybeSerializeDebug + Clone + Eq + PartialEq { // Stupid bug in the Rust compiler believes derived - // traits must be fulfilled by all type parameters. - /// The hash type produced. - type Output: Member + AsRef<[u8]>; - - /// Produce the hash of some byte-slice. - fn hash(s: &[u8]) -> Self::Output; - - /// Produce the hash of some codec-encodable value. - fn hash_of(s: &S) -> Self::Output { - Encode::using_encoded(s, Self::hash) - } - - /// Produce the patricia-trie root of a mapping from indices to byte slices. - fn enumerated_trie_root(items: &[&[u8]]) -> Self::Output; - - /// Iterator-based version of `enumerated_trie_root`. - fn ordered_trie_root< - I: IntoIterator, - A: AsRef<[u8]> - >(input: I) -> Self::Output; - - /// The Patricia tree root of the given mapping as an iterator. - fn trie_root< - I: IntoIterator, - A: AsRef<[u8]> + Ord, - B: AsRef<[u8]> - >(input: I) -> Self::Output; - - /// Acquire the global storage root. - fn storage_root() -> Self::Output; -} - -/// Blake2-256 Hash implementation. -#[derive(PartialEq, Eq, Clone)] -#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] -pub struct BlakeTwo256; - -impl Hash for BlakeTwo256 { - type Output = substrate_primitives::H256; - fn hash(s: &[u8]) -> Self::Output { - runtime_io::blake2_256(s).into() - } - fn enumerated_trie_root(items: &[&[u8]]) -> Self::Output { - runtime_io::enumerated_trie_root::(items).into() - } - fn trie_root< - I: IntoIterator, - A: AsRef<[u8]> + Ord, - B: AsRef<[u8]> - >(input: I) -> Self::Output { - runtime_io::trie_root::(input).into() - } - fn ordered_trie_root< - I: IntoIterator, - A: AsRef<[u8]> - >(input: I) -> Self::Output { - runtime_io::ordered_trie_root::(input).into() - } - fn storage_root() -> Self::Output { - runtime_io::storage_root().into() - } -} - -/// Something that can be checked for equality and printed out to a debug channel if bad. -pub trait CheckEqual { - fn check_equal(&self, other: &Self); -} - -impl CheckEqual for substrate_primitives::H256 { - #[cfg(feature = "std")] - fn check_equal(&self, other: &Self) { - use substrate_primitives::hexdisplay::HexDisplay; - if &self.0 != &other.0 { - println!("Hash: given={}, expected={}", HexDisplay::from(&self.0), HexDisplay::from(&other.0)); - } - } - - #[cfg(not(feature = "std"))] - fn check_equal(&self, other: &Self) { - if self != other { - runtime_io::print("Hash not equal"); - runtime_io::print(&self.0[..]); - runtime_io::print(&other.0[..]); - } - } -} - -#[cfg(feature = "std")] -pub trait MaybeSerializeDebugButNotDeserialize: Serialize + Debug {} -#[cfg(feature = "std")] -impl MaybeSerializeDebugButNotDeserialize for T {} - -#[cfg(not(feature = "std"))] -pub trait MaybeSerializeDebugButNotDeserialize {} -#[cfg(not(feature = "std"))] -impl MaybeSerializeDebugButNotDeserialize for T {} - -#[cfg(feature = "std")] -pub trait MaybeSerializeDebug: Serialize + DeserializeOwned + Debug {} -#[cfg(feature = "std")] -impl MaybeSerializeDebug for T {} - -#[cfg(not(feature = "std"))] -pub trait MaybeSerializeDebug {} -#[cfg(not(feature = "std"))] -impl MaybeSerializeDebug for T {} - -#[cfg(feature = "std")] -pub trait MaybeDisplay: Display {} -#[cfg(feature = "std")] -impl MaybeDisplay for T {} - -#[cfg(not(feature = "std"))] -pub trait MaybeDisplay {} -#[cfg(not(feature = "std"))] -impl MaybeDisplay for T {} - -pub trait Member: Send + Sync + Sized + MaybeSerializeDebug + Eq + PartialEq + Clone + 'static {} -impl Member for T {} - -/// 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`. -/// -/// You can also create a `new` one from those fields. -pub trait Header: Clone + Send + Sync + Codec + Eq + MaybeSerializeDebug + 'static { - type Number: Member + ::rstd::hash::Hash + Copy + MaybeDisplay + SimpleArithmetic + Codec; - type Hash: Member + ::rstd::hash::Hash + Copy + MaybeDisplay + Default + SimpleBitOps + Codec + AsRef<[u8]>; - type Hashing: Hash; - type Digest: Digest; - - fn new( - number: Self::Number, - extrinsics_root: Self::Hash, - state_root: Self::Hash, - parent_hash: Self::Hash, - digest: Self::Digest - ) -> Self; - - fn number(&self) -> &Self::Number; - fn set_number(&mut self, Self::Number); - - fn extrinsics_root(&self) -> &Self::Hash; - fn set_extrinsics_root(&mut self, Self::Hash); - - fn state_root(&self) -> &Self::Hash; - fn set_state_root(&mut self, Self::Hash); - - fn parent_hash(&self) -> &Self::Hash; - fn set_parent_hash(&mut self, Self::Hash); - - fn digest(&self) -> &Self::Digest; - fn set_digest(&mut self, Self::Digest); - - fn hash(&self) -> Self::Hash { - ::hash_of(self) - } -} - -/// Something which fulfills the abstract idea of a Substrate block. It has types for an -/// `Extrinsic` piece of information as well as a `Header`. -/// -/// You can get an iterator over each of the `extrinsics` and retrieve the `header`. -pub trait Block: Clone + Send + Sync + Codec + Eq + MaybeSerializeDebug + 'static { - type Extrinsic: Member + Codec; - type Header: Header; - type Hash: Member + ::rstd::hash::Hash + Copy + MaybeDisplay + Default + SimpleBitOps + Codec + AsRef<[u8]>; - - fn header(&self) -> &Self::Header; - fn extrinsics(&self) -> &[Self::Extrinsic]; - fn deconstruct(self) -> (Self::Header, Vec); - fn new(header: Self::Header, extrinsics: Vec) -> Self; - fn hash(&self) -> Self::Hash { - <::Hashing as Hash>::hash_of(self.header()) - } -} - -/// Extract the hashing type for a block. -pub type HashFor = <::Header as Header>::Hashing; -/// Extract the number type for a block. -pub type NumberFor = <::Header as Header>::Number; - -/// A "checkable" piece of information, used by the standard Substrate Executive in order to -/// check the validity of a piece of extrinsic information, usually by verifying the signature. -/// Implement for pieces of information that require some additional context `Context` in order to be -/// checked. -pub trait Checkable: Sized { - /// Returned if `check_with` succeeds. - type Checked; - - fn check_with(self, context: Context) -> Result; -} - -/// A "checkable" piece of information, used by the standard Substrate Executive in order to -/// check the validity of a piece of extrinsic information, usually by verifying the signature. -/// Implement for pieces of information that don't require additional context in order to be -/// checked. -pub trait BlindCheckable: Sized { - /// Returned if `check` succeeds. - type Checked; - - fn check(self) -> Result; -} - -// Every `BlindCheckable` is also a `Checkable` for arbitrary `Context`. -impl Checkable for T { - type Checked = ::Checked; - fn check_with(self, _: Context) -> Result { - BlindCheckable::check(self) - } -} - -/// 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 functioon -/// call. -/// -/// Also provides information on to whom this information is attributable and an index that allows -/// each piece of attributable information to be disambiguated. -pub trait Applyable: Sized + Send + Sync { - type AccountId: Member + MaybeDisplay; - type Index: Member + MaybeDisplay + SimpleArithmetic; - fn index(&self) -> &Self::Index; - fn sender(&self) -> Option<&Self::AccountId>; - fn apply(self) -> Result<(), &'static str>; -} - -/// Something that acts like a `Digest` - it can have `Log`s `push`ed onto it and these `Log`s are -/// each `Codec`. -pub trait Digest: Member + Default { - type Item: DigestItem; - fn logs(&self) -> &[Self::Item]; - fn push(&mut self, item: Self::Item); -} - -/// Single digest item. Could be any type that implements `Member` and provides methods -/// for casting member to 'system' log items, known to substrate. -/// -/// If the runtime does not supports some 'system' items, use `()` as a stub. -pub trait DigestItem: Member { - type AuthorityId; - - /// Returns Some if the entry is the `AuthoritiesChange` entry. - fn as_authorities_change(&self) -> Option<&[Self::AuthorityId]> { - None - } -} diff --git a/substrate/runtime/session/Cargo.toml b/substrate/runtime/session/Cargo.toml deleted file mode 100644 index c2acfc1de2638..0000000000000 --- a/substrate/runtime/session/Cargo.toml +++ /dev/null @@ -1,40 +0,0 @@ -[package] -name = "substrate-runtime-session" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -hex-literal = "0.1.0" -serde = { version = "1.0", default_features = false } -serde_derive = { version = "1.0", optional = true } -safe-mix = { version = "1.0", default_features = false} -substrate-keyring = { path = "../../keyring", optional = true } -substrate-codec = { path = "../../codec", default_features = false } -substrate-codec-derive = { path = "../../codec/derive", default_features = false } -substrate-primitives = { path = "../../primitives", default_features = false } -substrate-runtime-std = { path = "../../runtime-std", default_features = false } -substrate-runtime-io = { path = "../../runtime-io", default_features = false } -substrate-runtime-support = { path = "../../runtime-support", default_features = false } -substrate-runtime-primitives = { path = "../primitives", default_features = false } -substrate-runtime-consensus = { path = "../consensus", default_features = false } -substrate-runtime-system = { path = "../system", default_features = false } -substrate-runtime-timestamp = { path = "../timestamp", default_features = false } - -[features] -default = ["std"] -std = [ - "serde/std", - "serde_derive", - "safe-mix/std", - "substrate-keyring", - "substrate-codec/std", - "substrate-codec-derive/std", - "substrate-primitives/std", - "substrate-runtime-std/std", - "substrate-runtime-io/std", - "substrate-runtime-support/std", - "substrate-runtime-primitives/std", - "substrate-runtime-consensus/std", - "substrate-runtime-system/std", - "substrate-runtime-timestamp/std" -] diff --git a/substrate/runtime/session/src/lib.rs b/substrate/runtime/session/src/lib.rs deleted file mode 100644 index 007dd43729f7c..0000000000000 --- a/substrate/runtime/session/src/lib.rs +++ /dev/null @@ -1,467 +0,0 @@ -// Copyright 2017 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 . - -//! Session manager: is told the validators and allows them to manage their session keys for the -//! consensus module. - -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(feature = "std")] -extern crate serde; - -#[cfg(feature = "std")] -#[macro_use] -extern crate serde_derive; - -#[cfg(any(feature = "std", test))] -extern crate substrate_keyring as keyring; - -#[cfg(any(feature = "std", test))] -extern crate substrate_primitives; - -#[cfg_attr(feature = "std", macro_use)] -extern crate substrate_runtime_std as rstd; - -#[macro_use] -extern crate substrate_runtime_support as runtime_support; - -#[macro_use] -extern crate substrate_codec_derive; - -extern crate substrate_runtime_io as runtime_io; -extern crate substrate_codec as codec; -extern crate substrate_runtime_primitives as primitives; -extern crate substrate_runtime_consensus as consensus; -extern crate substrate_runtime_system as system; -extern crate substrate_runtime_timestamp as timestamp; - -use rstd::prelude::*; -use primitives::traits::{Zero, One, OnFinalise, Convert, As}; -use runtime_support::{StorageValue, StorageMap}; -use runtime_support::dispatch::Result; -use system::{ensure_signed, ensure_root}; - -#[cfg(any(feature = "std", test))] -use std::collections::HashMap; - -/// A session has changed. -pub trait OnSessionChange { - /// Session has changed. - fn on_session_change(time_elapsed: T, should_reward: bool); -} - -impl OnSessionChange for () { - fn on_session_change(_: T, _: bool) {} -} - -pub trait Trait: timestamp::Trait { - type ConvertAccountIdToSessionKey: Convert; - type OnSessionChange: OnSessionChange; - type Event: From> + Into<::Event>; -} - -decl_module! { - pub struct Module for enum Call where origin: T::Origin { - fn set_key(origin, key: T::SessionKey) -> Result; - - fn set_length(origin, new: T::BlockNumber) -> Result; - fn force_new_session(origin, apply_rewards: bool) -> Result; - } -} - -/// An event in this module. -decl_event!( - pub enum Event with RawEvent - where ::BlockNumber - { - /// New session has happened. Note that the argument is the session index, not the block - /// number as the type might suggest. - NewSession(BlockNumber), - } -); - -decl_storage! { - trait Store for Module as Session { - - /// The current set of validators. - pub Validators get(validators): required Vec; - /// Current length of the session. - pub SessionLength get(length): required T::BlockNumber; - /// Current index of the session. - pub CurrentIndex get(current_index): required T::BlockNumber; - /// Timestamp when current session started. - pub CurrentStart get(current_start): required T::Moment; - - /// New session is being forced is this entry exists; in which case, the boolean value is whether - /// the new session should be considered a normal rotation (rewardable) or exceptional (slashable). - pub ForcingNewSession get(forcing_new_session): bool; - /// Block at which the session length last changed. - LastLengthChange: T::BlockNumber; - /// The next key for a given validator. - NextKeyFor: map [ T::AccountId => T::SessionKey ]; - /// The next session length. - NextSessionLength: T::BlockNumber; - } -} - -impl Module { - - /// Deposit one of this module's events. - fn deposit_event(event: Event) { - >::deposit_event(::Event::from(event).into()); - } - - /// The number of validators currently. - pub fn validator_count() -> u32 { - >::get().len() as u32 // TODO: can probably optimised - } - - /// The last length change, if there was one, zero if not. - pub fn last_length_change() -> T::BlockNumber { - >::get().unwrap_or_else(T::BlockNumber::zero) - } - - /// Sets the session key of `_validator` to `_key`. This doesn't take effect until the next - /// session. - fn set_key(origin: T::Origin, key: T::SessionKey) -> Result { - let who = ensure_signed(origin)?; - // set new value for next session - >::insert(who, key); - Ok(()) - } - - /// Set a new era length. Won't kick in until the next era change (at current length). - fn set_length(origin: T::Origin, new: T::BlockNumber) -> Result { - ensure_root(origin)?; - >::put(new); - Ok(()) - } - - /// Forces a new session. - pub fn force_new_session(origin: T::Origin, apply_rewards: bool) -> Result { - ensure_root(origin)?; - Self::apply_force_new_session(apply_rewards) - } - - // INTERNAL API (available to other runtime modules) - - /// Forces a new session, no origin. - pub fn apply_force_new_session(apply_rewards: bool) -> Result { - >::put(apply_rewards); - Ok(()) - } - - /// Set the current set of validators. - /// - /// Called by `staking::next_era()` only. `next_session` should be called after this in order to - /// update the session keys to the next validator set. - pub fn set_validators(new: &[T::AccountId]) { - >::put(&new.to_vec()); // TODO: optimise. - >::set_authorities( - &new.iter().cloned().map(T::ConvertAccountIdToSessionKey::convert).collect::>() - ); - } - - /// Hook to be called after transaction processing. - pub fn check_rotate_session(block_number: T::BlockNumber) { - // do this last, after the staking system has had chance to switch out the authorities for the - // new set. - // check block number and call next_session if necessary. - let is_final_block = ((block_number - Self::last_length_change()) % Self::length()).is_zero(); - let (should_end_session, apply_rewards) = >::take() - .map_or((is_final_block, is_final_block), |apply_rewards| (true, apply_rewards)); - if should_end_session { - Self::rotate_session(is_final_block, apply_rewards); - } - } - - /// Move onto next session: register the new authority set. - pub fn rotate_session(is_final_block: bool, apply_rewards: bool) { - let now = >::get(); - let time_elapsed = now.clone() - Self::current_start(); - let session_index = >::get() + One::one(); - - Self::deposit_event(RawEvent::NewSession(session_index)); - - // Increment current session index. - >::put(session_index); - >::put(now); - - // Enact era length change. - let len_changed = if let Some(next_len) = >::take() { - >::put(next_len); - true - } else { - false - }; - if len_changed || !is_final_block { - let block_number = >::block_number(); - >::put(block_number); - } - - T::OnSessionChange::on_session_change(time_elapsed, apply_rewards); - - // Update any changes in session keys. - Self::validators().iter().enumerate().for_each(|(i, v)| { - if let Some(n) = >::take(v) { - >::set_authority(i as u32, &n); - } - }); - } - - /// Get the time that should have elapsed over a session if everything was working perfectly. - pub fn ideal_session_duration() -> T::Moment { - let block_period = >::block_period(); - let session_length = >::sa(Self::length()); - session_length * block_period - } - - /// Number of blocks remaining in this session, not counting this one. If the session is - /// due to rotate at the end of this block, then it will return 0. If the just began, then - /// it will return `Self::length() - 1`. - pub fn blocks_remaining() -> T::BlockNumber { - let length = Self::length(); - let length_minus_1 = length - One::one(); - let block_number = >::block_number(); - length_minus_1 - (block_number - Self::last_length_change() + length_minus_1) % length - } -} - -impl OnFinalise for Module { - fn on_finalise(n: T::BlockNumber) { - Self::check_rotate_session(n); - } -} - -#[cfg(any(feature = "std", test))] -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -#[serde(deny_unknown_fields)] -pub struct GenesisConfig { - pub session_length: T::BlockNumber, - pub validators: Vec, -} - -#[cfg(any(feature = "std", test))] -impl Default for GenesisConfig { - fn default() -> Self { - use primitives::traits::As; - GenesisConfig { - session_length: T::BlockNumber::sa(1000), - validators: vec![], - } - } -} - -#[cfg(any(feature = "std", test))] -impl primitives::BuildStorage for GenesisConfig -{ - fn build_storage(self) -> ::std::result::Result, Vec>, String> { - - use codec::Encode; - use primitives::traits::As; - Ok(map![ - Self::hash(>::key()).to_vec() => self.session_length.encode(), - Self::hash(>::key()).to_vec() => T::BlockNumber::sa(0).encode(), - Self::hash(>::key()).to_vec() => T::Moment::zero().encode(), - Self::hash(>::key()).to_vec() => self.validators.encode() - ]) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use runtime_io::with_externalities; - use substrate_primitives::{H256, Blake2Hasher}; - use primitives::BuildStorage; - use primitives::traits::{Identity, BlakeTwo256}; - use primitives::testing::{Digest, Header}; - - impl_outer_origin!{ - pub enum Origin for Test {} - } - - #[derive(Clone, Eq, PartialEq)] - pub struct Test; - impl consensus::Trait for Test { - const NOTE_OFFLINE_POSITION: u32 = 1; - type Log = u64; - type SessionKey = u64; - type OnOfflineValidator = (); - } - impl system::Trait for Test { - type Origin = Origin; - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type Digest = Digest; - type AccountId = u64; - type Header = Header; - type Event = (); - } - impl timestamp::Trait for Test { - const TIMESTAMP_SET_POSITION: u32 = 0; - type Moment = u64; - } - impl Trait for Test { - type ConvertAccountIdToSessionKey = Identity; - type OnSessionChange = (); - type Event = (); - } - - type System = system::Module; - type Consensus = consensus::Module; - type Session = Module; - - fn new_test_ext() -> runtime_io::TestExternalities { - let mut t = system::GenesisConfig::::default().build_storage().unwrap(); - t.extend(consensus::GenesisConfig::{ - code: vec![], - authorities: vec![1, 2, 3], - }.build_storage().unwrap()); - t.extend(timestamp::GenesisConfig::{ - period: 5, - }.build_storage().unwrap()); - t.extend(GenesisConfig::{ - session_length: 2, - validators: vec![1, 2, 3], - }.build_storage().unwrap()); - t.into() - } - - #[test] - fn simple_setup_should_work() { - with_externalities(&mut new_test_ext(), || { - assert_eq!(Consensus::authorities(), vec![1, 2, 3]); - assert_eq!(Session::length(), 2); - assert_eq!(Session::validators(), vec![1, 2, 3]); - }); - } - - #[test] - fn should_work_with_early_exit() { - with_externalities(&mut new_test_ext(), || { - System::set_block_number(1); - assert_ok!(Session::set_length(Origin::ROOT, 10)); - assert_eq!(Session::blocks_remaining(), 1); - Session::check_rotate_session(1); - - System::set_block_number(2); - assert_eq!(Session::blocks_remaining(), 0); - Session::check_rotate_session(2); - assert_eq!(Session::length(), 10); - - System::set_block_number(7); - assert_eq!(Session::current_index(), 1); - assert_eq!(Session::blocks_remaining(), 5); - assert_ok!(Session::force_new_session(Origin::ROOT, false)); - Session::check_rotate_session(7); - - System::set_block_number(8); - assert_eq!(Session::current_index(), 2); - assert_eq!(Session::blocks_remaining(), 9); - Session::check_rotate_session(8); - - System::set_block_number(17); - assert_eq!(Session::current_index(), 2); - assert_eq!(Session::blocks_remaining(), 0); - Session::check_rotate_session(17); - - System::set_block_number(18); - assert_eq!(Session::current_index(), 3); - }); - } - - #[test] - fn session_length_change_should_work() { - with_externalities(&mut new_test_ext(), || { - // Block 1: Change to length 3; no visible change. - System::set_block_number(1); - assert_ok!(Session::set_length(Origin::ROOT, 3)); - Session::check_rotate_session(1); - assert_eq!(Session::length(), 2); - assert_eq!(Session::current_index(), 0); - - // Block 2: Length now changed to 3. Index incremented. - System::set_block_number(2); - assert_ok!(Session::set_length(Origin::ROOT, 3)); - Session::check_rotate_session(2); - assert_eq!(Session::length(), 3); - assert_eq!(Session::current_index(), 1); - - // Block 3: Length now changed to 3. Index incremented. - System::set_block_number(3); - Session::check_rotate_session(3); - assert_eq!(Session::length(), 3); - assert_eq!(Session::current_index(), 1); - - // Block 4: Change to length 2; no visible change. - System::set_block_number(4); - assert_ok!(Session::set_length(Origin::ROOT, 2)); - Session::check_rotate_session(4); - assert_eq!(Session::length(), 3); - assert_eq!(Session::current_index(), 1); - - // Block 5: Length now changed to 2. Index incremented. - System::set_block_number(5); - Session::check_rotate_session(5); - assert_eq!(Session::length(), 2); - assert_eq!(Session::current_index(), 2); - - // Block 6: No change. - System::set_block_number(6); - Session::check_rotate_session(6); - assert_eq!(Session::length(), 2); - assert_eq!(Session::current_index(), 2); - - // Block 7: Next index. - System::set_block_number(7); - Session::check_rotate_session(7); - assert_eq!(Session::length(), 2); - assert_eq!(Session::current_index(), 3); - }); - } - - #[test] - fn session_change_should_work() { - with_externalities(&mut new_test_ext(), || { - // Block 1: No change - System::set_block_number(1); - Session::check_rotate_session(1); - assert_eq!(Consensus::authorities(), vec![1, 2, 3]); - - // Block 2: Session rollover, but no change. - System::set_block_number(2); - Session::check_rotate_session(2); - assert_eq!(Consensus::authorities(), vec![1, 2, 3]); - - // Block 3: Set new key for validator 2; no visible change. - System::set_block_number(3); - assert_ok!(Session::set_key(Origin::signed(2), 5)); - assert_eq!(Consensus::authorities(), vec![1, 2, 3]); - - Session::check_rotate_session(3); - assert_eq!(Consensus::authorities(), vec![1, 2, 3]); - - // Block 4: Session rollover, authority 2 changes. - System::set_block_number(4); - Session::check_rotate_session(4); - assert_eq!(Consensus::authorities(), vec![1, 5, 3]); - }); - } -} diff --git a/substrate/runtime/staking/Cargo.toml b/substrate/runtime/staking/Cargo.toml deleted file mode 100644 index 78ea8b2958e95..0000000000000 --- a/substrate/runtime/staking/Cargo.toml +++ /dev/null @@ -1,45 +0,0 @@ -[package] -name = "substrate-runtime-staking" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -hex-literal = "0.1.0" -serde = { version = "1.0", default_features = false } -serde_derive = { version = "1.0", optional = true } -safe-mix = { version = "1.0", default_features = false} -substrate-keyring = { path = "../../keyring", optional = true } -substrate-codec = { path = "../../codec", default_features = false } -substrate-codec-derive = { path = "../../codec/derive", default_features = false } -substrate-primitives = { path = "../../primitives", default_features = false } -substrate-runtime-std = { path = "../../runtime-std", default_features = false } -substrate-runtime-io = { path = "../../runtime-io", default_features = false } -substrate-runtime-sandbox = { path = "../../runtime-sandbox", default_features = false } -substrate-runtime-support = { path = "../../runtime-support", default_features = false } -substrate-runtime-primitives = { path = "../primitives", default_features = false } -substrate-runtime-balances = { path = "../balances", default_features = false } -substrate-runtime-consensus = { path = "../consensus", default_features = false } -substrate-runtime-system = { path = "../system", default_features = false } -substrate-runtime-session = { path = "../session", default_features = false } -substrate-runtime-timestamp = { path = "../timestamp", default_features = false } - -[features] -default = ["std"] -std = [ - "serde/std", - "serde_derive", - "safe-mix/std", - "substrate-keyring", - "substrate-codec/std", - "substrate-codec-derive/std", - "substrate-primitives/std", - "substrate-runtime-std/std", - "substrate-runtime-io/std", - "substrate-runtime-sandbox/std", - "substrate-runtime-support/std", - "substrate-runtime-primitives/std", - "substrate-runtime-balances/std", - "substrate-runtime-session/std", - "substrate-runtime-system/std", - "substrate-runtime-timestamp/std" -] diff --git a/substrate/runtime/staking/src/genesis_config.rs b/substrate/runtime/staking/src/genesis_config.rs deleted file mode 100644 index e2b50ca1229ad..0000000000000 --- a/substrate/runtime/staking/src/genesis_config.rs +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2017 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 . - -//! Build a staking genesis block. - -#![cfg(feature = "std")] - -use std::collections::HashMap; -use rstd::prelude::*; -use codec::Encode; -use runtime_support::StorageValue; -use primitives::traits::As; -use substrate_primitives::Blake2Hasher; -use {runtime_io, primitives}; -use super::{Trait, Intentions, CurrentEra, OfflineSlashGrace, MinimumValidatorCount, - BondingDuration, SessionsPerEra, ValidatorCount, SessionReward, OfflineSlash}; - -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -#[serde(deny_unknown_fields)] -pub struct GenesisConfig { - pub sessions_per_era: T::BlockNumber, - pub current_era: T::BlockNumber, - pub intentions: Vec, - pub validator_count: u32, - pub minimum_validator_count: u32, - pub bonding_duration: T::BlockNumber, - pub session_reward: T::Balance, - pub offline_slash: T::Balance, - pub offline_slash_grace: u32, -} - -impl Default for GenesisConfig { - fn default() -> Self { - GenesisConfig { - sessions_per_era: T::BlockNumber::sa(1000), - current_era: T::BlockNumber::sa(0), - intentions: vec![], - validator_count: 0, - minimum_validator_count: 0, - bonding_duration: T::BlockNumber::sa(1000), - session_reward: T::Balance::sa(0), - offline_slash: T::Balance::sa(0), - offline_slash_grace: 0, - } - } -} - -impl primitives::BuildStorage for GenesisConfig { - fn build_storage(self) -> ::std::result::Result, Vec>, String> { - let r: runtime_io::TestExternalities = map![ - Self::hash(>::key()).to_vec() => self.intentions.encode(), - Self::hash(>::key()).to_vec() => self.sessions_per_era.encode(), - Self::hash(>::key()).to_vec() => self.validator_count.encode(), - Self::hash(>::key()).to_vec() => self.minimum_validator_count.encode(), - Self::hash(>::key()).to_vec() => self.bonding_duration.encode(), - Self::hash(>::key()).to_vec() => self.current_era.encode(), - Self::hash(>::key()).to_vec() => self.session_reward.encode(), - Self::hash(>::key()).to_vec() => self.offline_slash.encode(), - Self::hash(>::key()).to_vec() => self.offline_slash_grace.encode() - ]; - Ok(r.into()) - } -} diff --git a/substrate/runtime/staking/src/lib.rs b/substrate/runtime/staking/src/lib.rs deleted file mode 100644 index f9737d6a3c26a..0000000000000 --- a/substrate/runtime/staking/src/lib.rs +++ /dev/null @@ -1,576 +0,0 @@ -// Copyright 2017 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 . - -//! Staking manager: Periodically determines the best set of validators. - -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(feature = "std")] -extern crate serde; - -#[cfg(feature = "std")] -#[macro_use] -extern crate serde_derive; - -#[macro_use] -extern crate substrate_runtime_support as runtime_support; - -#[cfg_attr(feature = "std", macro_use)] -extern crate substrate_runtime_std as rstd; - -#[macro_use] -extern crate substrate_codec_derive; - -extern crate substrate_codec as codec; -extern crate substrate_primitives; -extern crate substrate_runtime_io as runtime_io; -extern crate substrate_runtime_primitives as primitives; -extern crate substrate_runtime_balances as balances; -extern crate substrate_runtime_consensus as consensus; -extern crate substrate_runtime_sandbox as sandbox; -extern crate substrate_runtime_session as session; -extern crate substrate_runtime_system as system; -extern crate substrate_runtime_timestamp as timestamp; - -use rstd::prelude::*; -use runtime_support::{Parameter, StorageValue, StorageMap}; -use runtime_support::dispatch::Result; -use session::OnSessionChange; -use primitives::traits::{Zero, One, Bounded, OnFinalise, - As, Lookup}; -use balances::{address::Address, OnDilution}; -use system::{ensure_root, ensure_signed}; - -mod mock; - -mod tests; -mod genesis_config; - -#[cfg(feature = "std")] -pub use genesis_config::GenesisConfig; - -const DEFAULT_MINIMUM_VALIDATOR_COUNT: usize = 4; - -#[derive(PartialEq, Clone)] -#[cfg_attr(test, derive(Debug))] -pub enum LockStatus { - Liquid, - LockedUntil(BlockNumber), - Bonded, -} - -/// Preference of what happens on a slash event. -#[derive(PartialEq, Eq, Clone, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] -pub struct ValidatorPrefs { - /// Validator should ensure this many more slashes than is necessary before being unstaked. - pub unstake_threshold: u32, - // Reward that validator takes up-front; only the rest is split between themself and nominators. - pub validator_payment: Balance, -} - -impl Default for ValidatorPrefs { - fn default() -> Self { - ValidatorPrefs { - unstake_threshold: 3, - validator_payment: Default::default(), - } - } -} - -pub trait Trait: balances::Trait + session::Trait { - /// Some tokens minted. - type OnRewardMinted: OnDilution<::Balance>; - - /// The overarching event type. - type Event: From> + Into<::Event>; -} - -decl_module! { - #[cfg_attr(feature = "std", serde(bound(deserialize = "T::Balance: ::serde::de::DeserializeOwned")))] - pub struct Module for enum Call where origin: T::Origin { - fn stake(origin) -> Result; - fn unstake(origin, intentions_index: u32) -> Result; - fn nominate(origin, target: Address) -> Result; - fn unnominate(origin, target_index: u32) -> Result; - fn register_preferences(origin, intentions_index: u32, prefs: ValidatorPrefs) -> Result; - - fn set_sessions_per_era(origin, new: T::BlockNumber) -> Result; - fn set_bonding_duration(origin, new: T::BlockNumber) -> Result; - fn set_validator_count(origin, new: u32) -> Result; - fn force_new_era(origin, apply_rewards: bool) -> Result; - fn set_offline_slash_grace(origin, new: u32) -> Result; - } -} - -/// An event in this module. -decl_event!( - pub enum Event with RawEvent - where ::Balance, ::AccountId - { - /// All validators have been rewarded by the given balance. - Reward(Balance), - /// One validator (and their nominators) has been given a offline-warning (they're still - /// within their grace). The accrued number of slashes is recorded, too. - OfflineWarning(AccountId, u32), - /// One validator (and their nominators) has been slashed by the given amount. - OfflineSlash(AccountId, Balance), - } -); - -pub type PairOf = (T, T); - -decl_storage! { - trait Store for Module as Staking { - - /// The ideal number of staking participants. - pub ValidatorCount get(validator_count): required u32; - /// Minimum number of staking participants before emergency conditions are imposed. - pub MinimumValidatorCount: u32; - /// The length of a staking era in sessions. - pub SessionsPerEra get(sessions_per_era): required T::BlockNumber; - /// Maximum reward, per validator, that is provided per acceptable session. - pub SessionReward get(session_reward): required T::Balance; - /// Slash, per validator that is taken for the first time they are found to be offline. - pub OfflineSlash get(offline_slash): required T::Balance; - /// Number of instances of offline reports before slashing begins for validators. - pub OfflineSlashGrace get(offline_slash_grace): default u32; - /// The length of the bonding duration in blocks. - pub BondingDuration get(bonding_duration): required T::BlockNumber; - - /// The current era index. - pub CurrentEra get(current_era): required T::BlockNumber; - /// Preferences that a validator has. - pub ValidatorPreferences get(validator_preferences): default map [ T::AccountId => ValidatorPrefs ]; - /// All the accounts with a desire to stake. - pub Intentions get(intentions): default Vec; - /// All nominator -> nominee relationships. - pub Nominating get(nominating): map [ T::AccountId => T::AccountId ]; - /// Nominators for a particular account. - pub NominatorsFor get(nominators_for): default map [ T::AccountId => Vec ]; - /// Nominators for a particular account that is in action right now. - pub CurrentNominatorsFor get(current_nominators_for): default map [ T::AccountId => Vec ]; - /// The next value of sessions per era. - pub NextSessionsPerEra get(next_sessions_per_era): T::BlockNumber; - /// The session index at which the era length last changed. - pub LastEraLengthChange get(last_era_length_change): default T::BlockNumber; - - /// The highest and lowest staked validator slashable balances. - pub StakeRange get(stake_range): default PairOf; - - /// The block at which the `who`'s funds become entirely liquid. - pub Bondage get(bondage): default map [ T::AccountId => T::BlockNumber ]; - /// The number of times a given validator has been reported offline. This gets decremented by one each era that passes. - pub SlashCount get(slash_count): default map [ T::AccountId => u32 ]; - - /// We are forcing a new era. - pub ForcingNewEra get(forcing_new_era): (); - } -} - -impl Module { - - /// Deposit one of this module's events. - fn deposit_event(event: Event) { - >::deposit_event(::Event::from(event).into()); - } - - // PUBLIC IMMUTABLES - - /// MinimumValidatorCount getter, introduces a default. - pub fn minimum_validator_count() -> usize { - >::get().map(|v| v as usize).unwrap_or(DEFAULT_MINIMUM_VALIDATOR_COUNT) - } - - /// The length of a staking era in blocks. - pub fn era_length() -> T::BlockNumber { - Self::sessions_per_era() * >::length() - } - - /// Balance of a (potential) validator that includes all nominators. - pub fn nomination_balance(who: &T::AccountId) -> T::Balance { - Self::nominators_for(who).iter() - .map(>::total_balance) - .fold(Zero::zero(), |acc, x| acc + x) - } - - /// The total balance that can be slashed from an account. - pub fn slashable_balance(who: &T::AccountId) -> T::Balance { - Self::nominators_for(who).iter() - .map(>::total_balance) - .fold(>::total_balance(who), |acc, x| acc + x) - } - - /// The block at which the `who`'s funds become entirely liquid. - pub fn unlock_block(who: &T::AccountId) -> LockStatus { - match Self::bondage(who) { - i if i == T::BlockNumber::max_value() => LockStatus::Bonded, - i if i <= >::block_number() => LockStatus::Liquid, - i => LockStatus::LockedUntil(i), - } - } - - // PUBLIC DISPATCH - - /// Declare the desire to stake for the transactor. - /// - /// Effects will be felt at the beginning of the next era. - fn stake(origin: T::Origin) -> Result { - let who = ensure_signed(origin)?; - ensure!(Self::nominating(&who).is_none(), "Cannot stake if already nominating."); - let mut intentions = >::get(); - // can't be in the list twice. - ensure!(intentions.iter().find(|&t| t == &who).is_none(), "Cannot stake if already staked."); - - >::insert(&who, T::BlockNumber::max_value()); - intentions.push(who); - >::put(intentions); - Ok(()) - } - - /// Retract the desire to stake for the transactor. - /// - /// Effects will be felt at the beginning of the next era. - fn unstake(origin: T::Origin, intentions_index: u32) -> Result { - let who = ensure_signed(origin)?; - // unstake fails in degenerate case of having too few existing staked parties - if Self::intentions().len() <= Self::minimum_validator_count() { - return Err("cannot unstake when there are too few staked participants") - } - Self::apply_unstake(&who, intentions_index as usize) - } - - fn nominate(origin: T::Origin, target: Address) -> Result { - let who = ensure_signed(origin)?; - let target = >::lookup(target)?; - - ensure!(Self::nominating(&who).is_none(), "Cannot nominate if already nominating."); - ensure!(Self::intentions().iter().find(|&t| t == &who).is_none(), "Cannot nominate if already staked."); - - // update nominators_for - let mut t = Self::nominators_for(&target); - t.push(who.clone()); - >::insert(&target, t); - - // update nominating - >::insert(&who, &target); - - // Update bondage - >::insert(&who, T::BlockNumber::max_value()); - - Ok(()) - } - - /// Will panic if called when source isn't currently nominating target. - /// Updates Nominating, NominatorsFor and NominationBalance. - fn unnominate(origin: T::Origin, target_index: u32) -> Result { - let source = ensure_signed(origin)?; - let target_index = target_index as usize; - - let target = >::get(&source).ok_or("Account must be nominating")?; - - let mut t = Self::nominators_for(&target); - if t.get(target_index) != Some(&source) { - return Err("Invalid target index") - } - - // Ok - all valid. - - // update nominators_for - t.swap_remove(target_index); - >::insert(&target, t); - - // update nominating - >::remove(&source); - - // update bondage - >::insert(source, >::block_number() + Self::bonding_duration()); - Ok(()) - } - - /// Set the given account's preference for slashing behaviour should they be a validator. - /// - /// An error (no-op) if `Self::intentions()[intentions_index] != origin`. - fn register_preferences( - origin: T::Origin, - intentions_index: u32, - prefs: ValidatorPrefs - ) -> Result { - let who = ensure_signed(origin)?; - - if Self::intentions().get(intentions_index as usize) != Some(&who) { - return Err("Invalid index") - } - - >::insert(who, prefs); - - Ok(()) - } - - // PRIV DISPATCH - - /// Set the number of sessions in an era. - fn set_sessions_per_era(origin: T::Origin, new: T::BlockNumber) -> Result { - ensure_root(origin)?; - >::put(&new); - Ok(()) - } - - /// The length of the bonding duration in eras. - fn set_bonding_duration(origin: T::Origin, new: T::BlockNumber) -> Result { - ensure_root(origin)?; - >::put(&new); - Ok(()) - } - - /// The length of a staking era in sessions. - fn set_validator_count(origin: T::Origin, new: u32) -> Result { - ensure_root(origin)?; - >::put(&new); - Ok(()) - } - - /// Force there to be a new era. This also forces a new session immediately after. - /// `apply_rewards` should be true for validators to get the session reward. - fn force_new_era(origin: T::Origin, apply_rewards: bool) -> Result { - ensure_root(origin)?; - Self::apply_force_new_era(apply_rewards) - } - - // Just force_new_era without origin check. - fn apply_force_new_era(apply_rewards: bool) -> Result { - >::put(()); - >::apply_force_new_session(apply_rewards) - } - - - /// Set the offline slash grace period. - fn set_offline_slash_grace(origin: T::Origin, new: u32) -> Result { - ensure_root(origin)?; - >::put(&new); - Ok(()) - } - - // PUBLIC MUTABLES (DANGEROUS) - - /// Slash a given validator by a specific amount. Removes the slash from their balance by preference, - /// and reduces the nominators' balance if needed. - fn slash_validator(v: &T::AccountId, slash: T::Balance) { - // skip the slash in degenerate case of having only 4 staking participants despite having a larger - // desired number of validators (validator_count). - if Self::intentions().len() <= Self::minimum_validator_count() { - return - } - - if let Some(rem) = >::slash(v, slash) { - let noms = Self::current_nominators_for(v); - let total = noms.iter().map(>::total_balance).fold(T::Balance::zero(), |acc, x| acc + x); - if !total.is_zero() { - let safe_mul_rational = |b| b * rem / total;// TODO: avoid overflow - for n in noms.iter() { - let _ = >::slash(n, safe_mul_rational(>::total_balance(n))); // best effort - not much that can be done on fail. - } - } - } - } - - /// Reward a given validator by a specific amount. Add the reward to their, and their nominators' - /// balance, pro-rata. - fn reward_validator(who: &T::AccountId, reward: T::Balance) { - let off_the_table = reward.min(Self::validator_preferences(who).validator_payment); - let reward = reward - off_the_table; - let validator_cut = if reward.is_zero() { - Zero::zero() - } else { - let noms = Self::current_nominators_for(who); - let total = noms.iter() - .map(>::total_balance) - .fold(>::total_balance(who), |acc, x| acc + x) - .max(One::one()); - let safe_mul_rational = |b| b * reward / total;// TODO: avoid overflow - for n in noms.iter() { - let _ = >::reward(n, safe_mul_rational(>::total_balance(n))); - } - safe_mul_rational(>::total_balance(who)) - }; - let _ = >::reward(who, validator_cut + off_the_table); - } - - /// Actually carry out the unstake operation. - /// Assumes `intentions()[intentions_index] == who`. - fn apply_unstake(who: &T::AccountId, intentions_index: usize) -> Result { - let mut intentions = Self::intentions(); - if intentions.get(intentions_index) != Some(who) { - return Err("Invalid index"); - } - intentions.swap_remove(intentions_index); - >::put(intentions); - >::remove(who); - >::remove(who); - >::insert(who, >::block_number() + Self::bonding_duration()); - Ok(()) - } - - /// Get the reward for the session, assuming it ends with this block. - fn this_session_reward(actual_elapsed: T::Moment) -> T::Balance { - let ideal_elapsed = >::ideal_session_duration(); - let per65536: u64 = (T::Moment::sa(65536u64) * ideal_elapsed.clone() / actual_elapsed.max(ideal_elapsed)).as_(); - Self::session_reward() * T::Balance::sa(per65536) / T::Balance::sa(65536u64) - } - - /// Session has just changed. We need to determine whether we pay a reward, slash and/or - /// move to a new era. - fn new_session(actual_elapsed: T::Moment, should_reward: bool) { - if should_reward { - // apply good session reward - let reward = Self::this_session_reward(actual_elapsed); - let validators = >::validators(); - for v in validators.iter() { - Self::reward_validator(v, reward); - } - Self::deposit_event(RawEvent::Reward(reward)); - let total_minted = reward * >::sa(validators.len()); - let total_rewarded_stake = Self::stake_range().0 * >::sa(validators.len()); - T::OnRewardMinted::on_dilution(total_minted, total_rewarded_stake); - } - - let session_index = >::current_index(); - if >::take().is_some() - || ((session_index - Self::last_era_length_change()) % Self::sessions_per_era()).is_zero() - { - Self::new_era(); - } - } - - /// The era has changed - enact new staking set. - /// - /// 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() { - // Increment current era. - >::put(&(>::get() + One::one())); - - // Enact era length change. - if let Some(next_spe) = Self::next_sessions_per_era() { - if next_spe != Self::sessions_per_era() { - >::put(&next_spe); - >::put(&>::current_index()); - } - } - - // evaluate desired staking amounts and nominations and optimise to find the best - // combination of validators, then use session::internal::set_validators(). - // for now, this just orders would-be stakers by their balances and chooses the top-most - // >::get() of them. - // TODO: this is not sound. this should be moved to an off-chain solution mechanism. - let mut intentions = Self::intentions() - .into_iter() - .map(|v| (Self::slashable_balance(&v), v)) - .collect::>(); - - // Avoid reevaluate validator set if it would leave us with fewer than the minimum - // needed validators - if intentions.len() < Self::minimum_validator_count() { - return - } - - intentions.sort_unstable_by(|&(ref b1, _), &(ref b2, _)| b2.cmp(&b1)); - - >::put( - if !intentions.is_empty() { - let n = >::get() as usize; - (intentions[0].0, intentions[n - 1].0) - } else { - (Zero::zero(), Zero::zero()) - } - ); - let vals = &intentions.into_iter() - .map(|(_, v)| v) - .take(>::get() as usize) - .collect::>(); - for v in >::validators().iter() { - >::remove(v); - let slash_count = >::take(v); - if slash_count > 1 { - >::insert(v, slash_count - 1); - } - } - for v in vals.iter() { - >::insert(v, Self::nominators_for(v)); - } - >::set_validators(vals); - } -} - -impl OnFinalise for Module { - fn on_finalise(_n: T::BlockNumber) { - } -} - -impl OnSessionChange for Module { - fn on_session_change(elapsed: T::Moment, should_reward: bool) { - Self::new_session(elapsed, should_reward); - } -} - -impl balances::EnsureAccountLiquid for Module { - fn ensure_account_liquid(who: &T::AccountId) -> Result { - if Self::bondage(who) <= >::block_number() { - Ok(()) - } else { - Err("cannot transfer illiquid funds") - } - } -} - -impl balances::OnFreeBalanceZero for Module { - fn on_free_balance_zero(who: &T::AccountId) { - >::remove(who); - } -} - -impl consensus::OnOfflineValidator for Module { - fn on_offline_validator(validator_index: usize) { - let v = >::validators()[validator_index].clone(); - let slash_count = Self::slash_count(&v); - >::insert(v.clone(), slash_count + 1); - let grace = Self::offline_slash_grace(); - - let event = if slash_count >= grace { - let instances = slash_count - grace; - let slash = Self::offline_slash() << instances; - let next_slash = slash << 1u32; - let _ = Self::slash_validator(&v, slash); - if instances >= Self::validator_preferences(&v).unstake_threshold - || Self::slashable_balance(&v) < next_slash - { - if let Some(pos) = Self::intentions().into_iter().position(|x| &x == &v) { - Self::apply_unstake(&v, pos) - .expect("pos derived correctly from Self::intentions(); \ - apply_unstake can only fail if pos wrong; \ - Self::intentions() doesn't change; qed"); - } - let _ = Self::apply_force_new_era(false); - } - RawEvent::OfflineSlash(v, slash) - } else { - RawEvent::OfflineWarning(v, slash_count) - }; - Self::deposit_event(event); - } -} diff --git a/substrate/runtime/staking/src/mock.rs b/substrate/runtime/staking/src/mock.rs deleted file mode 100644 index 7dda4fe7ebec4..0000000000000 --- a/substrate/runtime/staking/src/mock.rs +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! Test utilities - -#![cfg(test)] - -use primitives::BuildStorage; -use primitives::traits::{Identity}; -use primitives::testing::{Digest, Header}; -use substrate_primitives::{H256, Blake2Hasher}; -use runtime_io; -use {GenesisConfig, Module, Trait, consensus, session, system, timestamp, balances}; - -impl_outer_origin!{ - pub enum Origin for Test {} -} - -// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. -#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] -pub struct Test; -impl consensus::Trait for Test { - const NOTE_OFFLINE_POSITION: u32 = 1; - type Log = u64; - type SessionKey = u64; - type OnOfflineValidator = (); -} -impl system::Trait for Test { - type Origin = Origin; - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Hashing = ::primitives::traits::BlakeTwo256; - type Digest = Digest; - type AccountId = u64; - type Header = Header; - type Event = (); -} -impl balances::Trait for Test { - type Balance = u64; - type AccountIndex = u64; - type OnFreeBalanceZero = Staking; - type EnsureAccountLiquid = Staking; - type Event = (); -} -impl session::Trait for Test { - type ConvertAccountIdToSessionKey = Identity; - type OnSessionChange = Staking; - type Event = (); -} -impl timestamp::Trait for Test { - const TIMESTAMP_SET_POSITION: u32 = 0; - type Moment = u64; -} -impl Trait for Test { - type OnRewardMinted = (); - type Event = (); -} - -pub fn new_test_ext(ext_deposit: u64, session_length: u64, sessions_per_era: u64, current_era: u64, monied: bool, reward: u64) -> runtime_io::TestExternalities { - let mut t = system::GenesisConfig::::default().build_storage().unwrap(); - let balance_factor = if ext_deposit > 0 { - 256 - } else { - 1 - }; - t.extend(consensus::GenesisConfig::{ - code: vec![], - authorities: vec![], - }.build_storage().unwrap()); - t.extend(session::GenesisConfig::{ - session_length, - validators: vec![10, 20], - }.build_storage().unwrap()); - t.extend(balances::GenesisConfig::{ - balances: if monied { - if reward > 0 { - vec![(1, 10 * balance_factor), (2, 20 * balance_factor), (3, 30 * balance_factor), (4, 40 * balance_factor), (10, balance_factor), (20, balance_factor)] - } else { - vec![(1, 10 * balance_factor), (2, 20 * balance_factor), (3, 30 * balance_factor), (4, 40 * balance_factor)] - } - } else { - vec![(10, balance_factor), (20, balance_factor)] - }, - transaction_base_fee: 0, - transaction_byte_fee: 0, - existential_deposit: ext_deposit, - transfer_fee: 0, - creation_fee: 0, - reclaim_rebate: 0, - }.build_storage().unwrap()); - t.extend(GenesisConfig::{ - sessions_per_era, - current_era, - intentions: vec![10, 20], - validator_count: 2, - minimum_validator_count: 0, - bonding_duration: sessions_per_era * session_length * 3, - session_reward: reward, - offline_slash: if monied { 20 } else { 0 }, - offline_slash_grace: 0, - }.build_storage().unwrap()); - t.extend(timestamp::GenesisConfig::{ - period: 5 - }.build_storage().unwrap()); - t.into() -} - -pub type System = system::Module; -pub type Balances = balances::Module; -pub type Session = session::Module; -pub type Timestamp = timestamp::Module; -pub type Staking = Module; diff --git a/substrate/runtime/staking/src/tests.rs b/substrate/runtime/staking/src/tests.rs deleted file mode 100644 index 66da1ed1737be..0000000000000 --- a/substrate/runtime/staking/src/tests.rs +++ /dev/null @@ -1,502 +0,0 @@ -// Copyright 2017 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 consensus::OnOfflineValidator; -use runtime_io::with_externalities; -use mock::{Balances, Session, Staking, System, Timestamp, Test, new_test_ext, Origin}; - -#[test] -fn note_null_offline_should_work() { - with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || { - assert_eq!(Staking::offline_slash_grace(), 0); - assert_eq!(Staking::slash_count(&10), 0); - assert_eq!(Balances::free_balance(&10), 1); - ::system::ExtrinsicIndex::::put(1); - assert_eq!(Staking::slash_count(&10), 0); - assert_eq!(Balances::free_balance(&10), 1); - assert!(Staking::forcing_new_era().is_none()); - }); -} - -#[test] -fn note_offline_should_work() { - with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || { - Balances::set_free_balance(&10, 70); - assert_eq!(Staking::offline_slash_grace(), 0); - assert_eq!(Staking::slash_count(&10), 0); - assert_eq!(Balances::free_balance(&10), 70); - ::system::ExtrinsicIndex::::put(1); - Staking::on_offline_validator(0); - assert_eq!(Staking::slash_count(&10), 1); - assert_eq!(Balances::free_balance(&10), 50); - assert!(Staking::forcing_new_era().is_none()); - }); -} - -#[test] -fn note_offline_exponent_should_work() { - with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || { - Balances::set_free_balance(&10, 150); - assert_eq!(Staking::offline_slash_grace(), 0); - assert_eq!(Staking::slash_count(&10), 0); - assert_eq!(Balances::free_balance(&10), 150); - ::system::ExtrinsicIndex::::put(1); - Staking::on_offline_validator(0); - assert_eq!(Staking::slash_count(&10), 1); - assert_eq!(Balances::free_balance(&10), 130); - ::system::ExtrinsicIndex::::put(1); - Staking::on_offline_validator(0); - assert_eq!(Staking::slash_count(&10), 2); - assert_eq!(Balances::free_balance(&10), 90); - assert!(Staking::forcing_new_era().is_none()); - }); -} - -#[test] -fn note_offline_grace_should_work() { - with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || { - Balances::set_free_balance(&10, 70); - Balances::set_free_balance(&20, 70); - assert_ok!(Staking::set_offline_slash_grace(Origin::ROOT, 1)); - assert_eq!(Staking::offline_slash_grace(), 1); - - assert_eq!(Staking::slash_count(&10), 0); - assert_eq!(Balances::free_balance(&10), 70); - - ::system::ExtrinsicIndex::::put(1); - Staking::on_offline_validator(0); - assert_eq!(Staking::slash_count(&10), 1); - assert_eq!(Balances::free_balance(&10), 70); - assert_eq!(Staking::slash_count(&20), 0); - assert_eq!(Balances::free_balance(&20), 70); - - ::system::ExtrinsicIndex::::put(1); - Staking::on_offline_validator(0); - Staking::on_offline_validator(1); - assert_eq!(Staking::slash_count(&10), 2); - assert_eq!(Balances::free_balance(&10), 50); - assert_eq!(Staking::slash_count(&20), 1); - assert_eq!(Balances::free_balance(&20), 70); - assert!(Staking::forcing_new_era().is_none()); - }); -} - -#[test] -fn note_offline_force_unstake_session_change_should_work() { - with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || { - Balances::set_free_balance(&10, 70); - Balances::set_free_balance(&20, 70); - assert_ok!(Staking::stake(Origin::signed(1))); - - assert_eq!(Staking::slash_count(&10), 0); - assert_eq!(Balances::free_balance(&10), 70); - assert_eq!(Staking::intentions(), vec![10, 20, 1]); - assert_eq!(Session::validators(), vec![10, 20]); - - ::system::ExtrinsicIndex::::put(1); - Staking::on_offline_validator(0); - assert_eq!(Balances::free_balance(&10), 50); - assert_eq!(Staking::slash_count(&10), 1); - assert_eq!(Staking::intentions(), vec![10, 20, 1]); - - ::system::ExtrinsicIndex::::put(1); - Staking::on_offline_validator(0); - assert_eq!(Staking::intentions(), vec![1, 20]); - assert_eq!(Balances::free_balance(&10), 10); - assert!(Staking::forcing_new_era().is_some()); - }); -} - -#[test] -fn note_offline_auto_unstake_session_change_should_work() { - with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || { - Balances::set_free_balance(&10, 7000); - Balances::set_free_balance(&20, 7000); - assert_ok!(Staking::register_preferences(Origin::signed(10), 0, ValidatorPrefs { unstake_threshold: 1, validator_payment: 0 })); - - assert_eq!(Staking::intentions(), vec![10, 20]); - - ::system::ExtrinsicIndex::::put(1); - Staking::on_offline_validator(0); - Staking::on_offline_validator(1); - assert_eq!(Balances::free_balance(&10), 6980); - assert_eq!(Balances::free_balance(&20), 6980); - assert_eq!(Staking::intentions(), vec![10, 20]); - assert!(Staking::forcing_new_era().is_none()); - - ::system::ExtrinsicIndex::::put(1); - Staking::on_offline_validator(0); - Staking::on_offline_validator(1); - assert_eq!(Balances::free_balance(&10), 6940); - assert_eq!(Balances::free_balance(&20), 6940); - assert_eq!(Staking::intentions(), vec![20]); - assert!(Staking::forcing_new_era().is_some()); - - ::system::ExtrinsicIndex::::put(1); - Staking::on_offline_validator(1); - assert_eq!(Balances::free_balance(&10), 6940); - assert_eq!(Balances::free_balance(&20), 6860); - assert_eq!(Staking::intentions(), vec![20]); - - ::system::ExtrinsicIndex::::put(1); - Staking::on_offline_validator(1); - assert_eq!(Balances::free_balance(&10), 6940); - assert_eq!(Balances::free_balance(&20), 6700); - assert_eq!(Staking::intentions(), vec![0u64; 0]); - }); -} - - -#[test] -fn rewards_should_work() { - with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || { - assert_eq!(Staking::era_length(), 9); - assert_eq!(Staking::sessions_per_era(), 3); - assert_eq!(Staking::last_era_length_change(), 0); - assert_eq!(Staking::current_era(), 0); - assert_eq!(Session::current_index(), 0); - assert_eq!(Balances::total_balance(&10), 1); - - System::set_block_number(3); - Timestamp::set_timestamp(15); // on time. - Session::check_rotate_session(System::block_number()); - assert_eq!(Staking::current_era(), 0); - assert_eq!(Session::current_index(), 1); - assert_eq!(Balances::total_balance(&10), 11); - System::set_block_number(6); - Timestamp::set_timestamp(31); // a little late - Session::check_rotate_session(System::block_number()); - assert_eq!(Staking::current_era(), 0); - assert_eq!(Session::current_index(), 2); - assert_eq!(Balances::total_balance(&10), 20); // less reward - System::set_block_number(9); - Timestamp::set_timestamp(50); // very late - Session::check_rotate_session(System::block_number()); - assert_eq!(Staking::current_era(), 1); - assert_eq!(Session::current_index(), 3); - assert_eq!(Balances::total_balance(&10), 27); // much less reward - }); -} - -#[test] -fn slashing_should_work() { - with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || { - assert_eq!(Staking::era_length(), 9); - assert_eq!(Staking::sessions_per_era(), 3); - assert_eq!(Staking::last_era_length_change(), 0); - assert_eq!(Staking::current_era(), 0); - assert_eq!(Session::current_index(), 0); - assert_eq!(Balances::total_balance(&10), 1); - - System::set_block_number(3); - Session::check_rotate_session(System::block_number()); - assert_eq!(Staking::current_era(), 0); - assert_eq!(Session::current_index(), 1); - assert_eq!(Balances::total_balance(&10), 11); - - System::set_block_number(6); - Session::check_rotate_session(System::block_number()); - assert_eq!(Staking::current_era(), 0); - assert_eq!(Session::current_index(), 2); - assert_eq!(Balances::total_balance(&10), 21); - - System::set_block_number(7); - ::system::ExtrinsicIndex::::put(1); - Staking::on_offline_validator(0); - Staking::on_offline_validator(1); - assert_eq!(Balances::total_balance(&10), 1); - }); -} - - - -#[test] -fn staking_should_work() { - with_externalities(&mut new_test_ext(0, 1, 2, 0, true, 0), || { - - assert_eq!(Staking::era_length(), 2); - assert_eq!(Staking::validator_count(), 2); - assert_eq!(Session::validators(), vec![10, 20]); - - assert_ok!(Staking::set_bonding_duration(Origin::ROOT, 2)); - assert_eq!(Staking::bonding_duration(), 2); - - // Block 1: Add three validators. No obvious change. - System::set_block_number(1); - assert_ok!(Staking::stake(Origin::signed(1))); - assert_ok!(Staking::stake(Origin::signed(2))); - assert_ok!(Staking::stake(Origin::signed(4))); - Session::check_rotate_session(System::block_number()); - assert_eq!(Staking::current_era(), 0); - assert_eq!(Session::validators(), vec![10, 20]); - - // Block 2: New validator set now. - System::set_block_number(2); - Session::check_rotate_session(System::block_number()); - assert_eq!(Staking::current_era(), 1); - assert_eq!(Session::validators(), vec![4, 2]); - - // Block 3: Unstake highest, introduce another staker. No change yet. - System::set_block_number(3); - assert_ok!(Staking::stake(Origin::signed(3))); - assert_ok!(Staking::unstake(Origin::signed(4), Staking::intentions().iter().position(|&x| x == 4).unwrap() as u32)); - assert_eq!(Staking::current_era(), 1); - Session::check_rotate_session(System::block_number()); - - // Block 4: New era - validators change. - System::set_block_number(4); - Session::check_rotate_session(System::block_number()); - assert_eq!(Staking::current_era(), 2); - assert_eq!(Session::validators(), vec![3, 2]); - - // Block 5: Transfer stake from highest to lowest. No change yet. - System::set_block_number(5); - assert_ok!(Balances::transfer(Origin::signed(4), 1.into(), 40)); - Session::check_rotate_session(System::block_number()); - - // Block 6: Lowest now validator. - System::set_block_number(6); - Session::check_rotate_session(System::block_number()); - assert_eq!(Session::validators(), vec![1, 3]); - - // Block 7: Unstake three. No change yet. - System::set_block_number(7); - assert_ok!(Staking::unstake(Origin::signed(3), Staking::intentions().iter().position(|&x| x == 3).unwrap() as u32)); - Session::check_rotate_session(System::block_number()); - assert_eq!(Session::validators(), vec![1, 3]); - - // Block 8: Back to one and two. - System::set_block_number(8); - Session::check_rotate_session(System::block_number()); - assert_eq!(Session::validators(), vec![1, 2]); - }); -} - -#[test] -fn nominating_and_rewards_should_work() { - with_externalities(&mut new_test_ext(0, 1, 1, 0, true, 10), || { - assert_eq!(Staking::era_length(), 1); - assert_eq!(Staking::validator_count(), 2); - assert_eq!(Staking::bonding_duration(), 3); - assert_eq!(Session::validators(), vec![10, 20]); - - System::set_block_number(1); - assert_ok!(Staking::stake(Origin::signed(1))); - assert_ok!(Staking::stake(Origin::signed(2))); - assert_ok!(Staking::stake(Origin::signed(3))); - assert_ok!(Staking::nominate(Origin::signed(4), 1.into())); - Session::check_rotate_session(System::block_number()); - assert_eq!(Staking::current_era(), 1); - assert_eq!(Session::validators(), vec![1, 3]); // 4 + 1, 3 - assert_eq!(Balances::total_balance(&1), 10); - assert_eq!(Balances::total_balance(&2), 20); - assert_eq!(Balances::total_balance(&3), 30); - assert_eq!(Balances::total_balance(&4), 40); - - System::set_block_number(2); - assert_ok!(Staking::unnominate(Origin::signed(4), 0)); - Session::check_rotate_session(System::block_number()); - assert_eq!(Staking::current_era(), 2); - assert_eq!(Session::validators(), vec![3, 2]); - assert_eq!(Balances::total_balance(&1), 12); - assert_eq!(Balances::total_balance(&2), 20); - assert_eq!(Balances::total_balance(&3), 40); - assert_eq!(Balances::total_balance(&4), 48); - - System::set_block_number(3); - assert_ok!(Staking::stake(Origin::signed(4))); - assert_ok!(Staking::unstake(Origin::signed(3), Staking::intentions().iter().position(|&x| x == 3).unwrap() as u32)); - assert_ok!(Staking::nominate(Origin::signed(3), 1.into())); - Session::check_rotate_session(System::block_number()); - assert_eq!(Session::validators(), vec![1, 4]); - assert_eq!(Balances::total_balance(&1), 12); - assert_eq!(Balances::total_balance(&2), 30); - assert_eq!(Balances::total_balance(&3), 50); - assert_eq!(Balances::total_balance(&4), 48); - - System::set_block_number(4); - Session::check_rotate_session(System::block_number()); - assert_eq!(Balances::total_balance(&1), 13); - assert_eq!(Balances::total_balance(&2), 30); - assert_eq!(Balances::total_balance(&3), 58); - assert_eq!(Balances::total_balance(&4), 58); - }); -} - -#[test] -fn rewards_with_off_the_table_should_work() { - with_externalities(&mut new_test_ext(0, 1, 1, 0, true, 10), || { - System::set_block_number(1); - assert_ok!(Staking::stake(Origin::signed(1))); - assert_ok!(Staking::nominate(Origin::signed(2), 1.into())); - assert_ok!(Staking::stake(Origin::signed(3))); - Session::check_rotate_session(System::block_number()); - assert_eq!(Session::validators(), vec![1, 3]); // 1 + 2, 3 - assert_eq!(Balances::total_balance(&1), 10); - assert_eq!(Balances::total_balance(&2), 20); - assert_eq!(Balances::total_balance(&3), 30); - - System::set_block_number(2); - assert_ok!(Staking::register_preferences(Origin::signed(1), Staking::intentions().into_iter().position(|i| i == 1).unwrap() as u32, ValidatorPrefs { unstake_threshold: 3, validator_payment: 4 })); - Session::check_rotate_session(System::block_number()); - assert_eq!(Balances::total_balance(&1), 16); - assert_eq!(Balances::total_balance(&2), 24); - assert_eq!(Balances::total_balance(&3), 40); - }); -} - -#[test] -fn nominating_slashes_should_work() { - with_externalities(&mut new_test_ext(0, 2, 2, 0, true, 10), || { - assert_eq!(Staking::era_length(), 4); - assert_eq!(Staking::validator_count(), 2); - assert_eq!(Staking::bonding_duration(), 12); - assert_eq!(Session::validators(), vec![10, 20]); - - System::set_block_number(2); - Session::check_rotate_session(System::block_number()); - - Timestamp::set_timestamp(15); - System::set_block_number(4); - assert_ok!(Staking::stake(Origin::signed(1))); - assert_ok!(Staking::stake(Origin::signed(3))); - assert_ok!(Staking::nominate(Origin::signed(2), 3.into())); - assert_ok!(Staking::nominate(Origin::signed(4), 1.into())); - Session::check_rotate_session(System::block_number()); - - assert_eq!(Staking::current_era(), 1); - assert_eq!(Session::validators(), vec![1, 3]); // 1 + 4, 3 + 2 - assert_eq!(Balances::total_balance(&1), 10); - assert_eq!(Balances::total_balance(&2), 20); - assert_eq!(Balances::total_balance(&3), 30); - assert_eq!(Balances::total_balance(&4), 40); - - System::set_block_number(5); - ::system::ExtrinsicIndex::::put(1); - Staking::on_offline_validator(0); - Staking::on_offline_validator(1); - assert_eq!(Balances::total_balance(&1), 0); - assert_eq!(Balances::total_balance(&2), 20); - assert_eq!(Balances::total_balance(&3), 10); - assert_eq!(Balances::total_balance(&4), 30); - }); -} - -#[test] -fn double_staking_should_fail() { - with_externalities(&mut new_test_ext(0, 1, 2, 0, true, 0), || { - System::set_block_number(1); - assert_ok!(Staking::stake(Origin::signed(1))); - assert_noop!(Staking::stake(Origin::signed(1)), "Cannot stake if already staked."); - assert_noop!(Staking::nominate(Origin::signed(1), 1.into()), "Cannot nominate if already staked."); - assert_ok!(Staking::nominate(Origin::signed(2), 1.into())); - assert_noop!(Staking::stake(Origin::signed(2)), "Cannot stake if already nominating."); - assert_noop!(Staking::nominate(Origin::signed(2), 1.into()), "Cannot nominate if already nominating."); - }); -} - -#[test] -fn staking_eras_work() { - with_externalities(&mut new_test_ext(0, 1, 2, 0, true, 0), || { - assert_eq!(Staking::era_length(), 2); - assert_eq!(Staking::sessions_per_era(), 2); - assert_eq!(Staking::last_era_length_change(), 0); - assert_eq!(Staking::current_era(), 0); - assert_eq!(Session::current_index(), 0); - - // Block 1: No change. - System::set_block_number(1); - Session::check_rotate_session(System::block_number()); - assert_eq!(Session::current_index(), 1); - assert_eq!(Staking::sessions_per_era(), 2); - assert_eq!(Staking::last_era_length_change(), 0); - assert_eq!(Staking::current_era(), 0); - - // Block 2: Simple era change. - System::set_block_number(2); - Session::check_rotate_session(System::block_number()); - assert_eq!(Session::current_index(), 2); - assert_eq!(Staking::sessions_per_era(), 2); - assert_eq!(Staking::last_era_length_change(), 0); - assert_eq!(Staking::current_era(), 1); - - // Block 3: Schedule an era length change; no visible changes. - System::set_block_number(3); - assert_ok!(Staking::set_sessions_per_era(Origin::ROOT, 3)); - Session::check_rotate_session(System::block_number()); - assert_eq!(Session::current_index(), 3); - assert_eq!(Staking::sessions_per_era(), 2); - assert_eq!(Staking::last_era_length_change(), 0); - assert_eq!(Staking::current_era(), 1); - - // Block 4: Era change kicks in. - System::set_block_number(4); - Session::check_rotate_session(System::block_number()); - assert_eq!(Session::current_index(), 4); - assert_eq!(Staking::sessions_per_era(), 3); - assert_eq!(Staking::last_era_length_change(), 4); - assert_eq!(Staking::current_era(), 2); - - // Block 5: No change. - System::set_block_number(5); - Session::check_rotate_session(System::block_number()); - assert_eq!(Session::current_index(), 5); - assert_eq!(Staking::sessions_per_era(), 3); - assert_eq!(Staking::last_era_length_change(), 4); - assert_eq!(Staking::current_era(), 2); - - // Block 6: No change. - System::set_block_number(6); - Session::check_rotate_session(System::block_number()); - assert_eq!(Session::current_index(), 6); - assert_eq!(Staking::sessions_per_era(), 3); - assert_eq!(Staking::last_era_length_change(), 4); - assert_eq!(Staking::current_era(), 2); - - // Block 7: Era increment. - System::set_block_number(7); - Session::check_rotate_session(System::block_number()); - assert_eq!(Session::current_index(), 7); - assert_eq!(Staking::sessions_per_era(), 3); - assert_eq!(Staking::last_era_length_change(), 4); - assert_eq!(Staking::current_era(), 3); - }); -} - -#[test] -fn staking_balance_transfer_when_bonded_should_not_work() { - with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || { - Balances::set_free_balance(&1, 111); - assert_ok!(Staking::stake(Origin::signed(1))); - assert_noop!(Balances::transfer(Origin::signed(1), 2.into(), 69), "cannot transfer illiquid funds"); - }); -} - -#[test] -fn deducting_balance_when_bonded_should_not_work() { - with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || { - Balances::set_free_balance(&1, 111); - >::insert(1, 2); - System::set_block_number(1); - assert_eq!(Staking::unlock_block(&1), LockStatus::LockedUntil(2)); - assert_noop!(Balances::reserve(&1, 69), "cannot transfer illiquid funds"); - }); -} diff --git a/substrate/runtime/system/Cargo.toml b/substrate/runtime/system/Cargo.toml deleted file mode 100644 index 28388bd5390f9..0000000000000 --- a/substrate/runtime/system/Cargo.toml +++ /dev/null @@ -1,32 +0,0 @@ -[package] -name = "substrate-runtime-system" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -hex-literal = "0.1.0" -serde = { version = "1.0", default_features = false } -serde_derive = { version = "1.0", optional = true } -safe-mix = { version = "1.0", default_features = false} -substrate-codec = { path = "../../codec", default_features = false } -substrate-codec-derive = { path = "../../codec/derive", default_features = false } -substrate-primitives = { path = "../../primitives", default_features = false } -substrate-runtime-std = { path = "../../runtime-std", default_features = false } -substrate-runtime-io = { path = "../../runtime-io", default_features = false } -substrate-runtime-support = { path = "../../runtime-support", default_features = false } -substrate-runtime-primitives = { path = "../primitives", default_features = false } - -[features] -default = ["std"] -std = [ - "serde/std", - "serde_derive", - "safe-mix/std", - "substrate-codec/std", - "substrate-codec-derive/std", - "substrate-primitives/std", - "substrate-runtime-std/std", - "substrate-runtime-io/std", - "substrate-runtime-support/std", - "substrate-runtime-primitives/std", -] diff --git a/substrate/runtime/system/src/lib.rs b/substrate/runtime/system/src/lib.rs deleted file mode 100644 index ae8ceff6a6047..0000000000000 --- a/substrate/runtime/system/src/lib.rs +++ /dev/null @@ -1,426 +0,0 @@ -// Copyright 2017 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 . - -//! System manager: Handles lowest level stuff like depositing logs, basic set up and take down of -//! temporary storage entries, access to old block hashes. - -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(any(feature = "std", test))] -extern crate substrate_primitives; - -#[cfg_attr(any(feature = "std", test), macro_use)] -extern crate substrate_runtime_std as rstd; - -#[macro_use] -extern crate substrate_runtime_support as runtime_support; - -#[cfg(feature = "std")] -extern crate serde; - -#[cfg(feature = "std")] -#[macro_use] -extern crate serde_derive; - -#[macro_use] -extern crate substrate_codec_derive; - -extern crate substrate_codec as codec; -extern crate substrate_runtime_io as runtime_io; -extern crate substrate_runtime_primitives as primitives; -extern crate safe_mix; - -use rstd::prelude::*; -use primitives::traits::{self, CheckEqual, SimpleArithmetic, SimpleBitOps, Zero, One, Bounded, - Hash, Member, MaybeDisplay, EnsureOrigin}; -use runtime_support::{StorageValue, StorageMap, Parameter}; -use safe_mix::TripletMix; - -#[cfg(any(feature = "std", test))] -use rstd::marker::PhantomData; -#[cfg(any(feature = "std", test))] -use codec::Encode; - -#[cfg(any(feature = "std", test))] -use runtime_io::{twox_128, TestExternalities, Blake2Hasher}; - -/// Compute the extrinsics root of a list of extrinsics. -pub fn extrinsics_root(extrinsics: &[E]) -> H::Output { - extrinsics_data_root::(extrinsics.iter().map(codec::Encode::encode).collect()) -} - -/// Compute the extrinsics root of a list of extrinsics. -pub fn extrinsics_data_root(xts: Vec>) -> H::Output { - let xts = xts.iter().map(Vec::as_slice).collect::>(); - H::enumerated_trie_root(&xts) -} - -pub trait Trait: Eq + Clone { - type Origin: Into>> + From>; - type Index: Parameter + Member + Default + MaybeDisplay + SimpleArithmetic + Copy; - type BlockNumber: Parameter + Member + MaybeDisplay + SimpleArithmetic + Default + Bounded + Copy + rstd::hash::Hash; - type Hash: Parameter + Member + MaybeDisplay + SimpleBitOps + Default + Copy + CheckEqual + rstd::hash::Hash + AsRef<[u8]>; - type Hashing: Hash; - type Digest: Parameter + Member + Default + traits::Digest; - type AccountId: Parameter + Member + MaybeDisplay + Ord + Default; - type Header: Parameter + traits::Header< - Number = Self::BlockNumber, - Hash = Self::Hash, - Digest = Self::Digest - >; - type Event: Parameter + Member + From; -} - -pub type DigestItemOf = <::Digest as traits::Digest>::Item; - -decl_module! { - pub struct Module for enum Call where origin: T::Origin {} -} - -/// A phase of a block's execution. -#[derive(Encode, Decode)] -#[cfg_attr(feature = "std", derive(Serialize, PartialEq, Eq, Clone, Debug))] -pub enum Phase { - /// Applying an extrinsic. - ApplyExtrinsic(u32), - /// The end. - Finalization, -} - -/// Record of an event happening. -#[derive(Encode, Decode)] -#[cfg_attr(feature = "std", derive(Serialize, PartialEq, Eq, Clone, Debug))] -pub struct EventRecord { - /// The phase of the block it happened in. - pub phase: Phase, - /// The event itself. - pub event: E, -} - -/// Event for the system module. -decl_event!( - pub enum Event { - /// An extrinsic completed successfully. - ExtrinsicSuccess, - /// An extrinsic failed. - ExtrinsicFailed, - } -); - -/// Origin for the system module. -#[derive(PartialEq, Eq, Clone)] -#[cfg_attr(feature = "std", derive(Debug))] -pub enum RawOrigin { - /// The system itself ordained this dispatch to happen: this is the highest privilege level. - Root, - /// It is signed by some public key and we provide the AccountId. - Signed(AccountId), - /// It is signed by nobody but included and agreed upon by the validators anyway: it's "inherently" true. - Inherent, -} - -impl From> for RawOrigin { - fn from(s: Option) -> RawOrigin { - match s { - Some(who) => RawOrigin::Signed(who), - None => RawOrigin::Inherent, - } - } -} - -/// Exposed trait-generic origin type. -pub type Origin = RawOrigin<::AccountId>; - -decl_storage! { - trait Store for Module as System { - - pub AccountNonce get(account_nonce): default map [ T::AccountId => T::Index ]; - - ExtrinsicCount: u32; - pub BlockHash get(block_hash): required map [ T::BlockNumber => T::Hash ]; - pub ExtrinsicIndex get(extrinsic_index): u32; - ExtrinsicData get(extrinsic_data): required map [ u32 => Vec ]; - RandomSeed get(random_seed): required T::Hash; - /// The current block number being processed. Set by `execute_block`. - Number get(block_number): required T::BlockNumber; - ParentHash get(parent_hash): required T::Hash; - ExtrinsicsRoot get(extrinsics_root): required T::Hash; - Digest get(digest): default T::Digest; - - Events get(events): default Vec>; - } -} - -pub struct EnsureRoot(::rstd::marker::PhantomData); -impl>>, AccountId> EnsureOrigin for EnsureRoot { - type Success = (); - fn ensure_origin(o: O) -> Result { - ensure_root(o) - } -} - -/// Ensure that the origin `o` represents a signed extrinsic (i.e. transaction). -/// Returns `Ok` with the account that signed the extrinsic or an `Err` otherwise. -pub fn ensure_signed(o: OuterOrigin) -> Result - where OuterOrigin: Into>> -{ - match o.into() { - Some(RawOrigin::Signed(t)) => Ok(t), - _ => Err("bad origin: expected to be a signed origin"), - } -} - -/// Ensure that the origin `o` represents the root. Returns `Ok` or an `Err` otherwise. -pub fn ensure_root(o: OuterOrigin) -> Result<(), &'static str> - where OuterOrigin: Into>> -{ - match o.into() { - Some(RawOrigin::Root) => Ok(()), - _ => Err("bad origin: expected to be a root origin"), - } -} - -/// Ensure that the origin `o` represents an unsigned extrinsic. Returns `Ok` or an `Err` otherwise. -pub fn ensure_inherent(o: OuterOrigin) -> Result<(), &'static str> - where OuterOrigin: Into>> -{ - match o.into() { - Some(RawOrigin::Inherent) => Ok(()), - _ => Err("bad origin: expected to be an inherent origin"), - } -} - -impl Module { - /// Start the execution of a particular block. - pub fn initialise(number: &T::BlockNumber, parent_hash: &T::Hash, txs_root: &T::Hash) { - // populate environment. - >::put(number); - >::put(parent_hash); - >::insert(*number - One::one(), parent_hash); - >::put(txs_root); - >::put(Self::calculate_random()); - >::put(0u32); - >::kill(); - } - - /// Remove temporary "environment" entries in storage. - pub fn finalise() -> T::Header { - >::kill(); - >::kill(); - - let number = >::take(); - let parent_hash = >::take(); - let digest = >::take(); - let extrinsics_root = >::take(); - let storage_root = T::Hashing::storage_root(); - - // > stays to be inspected by the client. - - ::new(number, extrinsics_root, storage_root, parent_hash, digest) - } - - /// Deposits a log and ensures it matches the blocks log data. - pub fn deposit_log(item: ::Item) { - let mut l = >::get(); - traits::Digest::push(&mut l, item); - >::put(l); - } - - /// Deposits an event onto this block's event record. - pub fn deposit_event(event: T::Event) { - let phase = >::get().map_or(Phase::Finalization, |c| Phase::ApplyExtrinsic(c)); - let mut events = Self::events(); - events.push(EventRecord { phase, event }); - >::put(events); - } - - /// Calculate the current block's random seed. - fn calculate_random() -> T::Hash { - assert!(Self::block_number() > Zero::zero(), "Block number may never be zero"); - (0..81) - .scan( - Self::block_number() - One::one(), - |c, _| { if *c > Zero::zero() { *c -= One::one() }; Some(*c) - }) - .map(Self::block_hash) - .triplet_mix() - } - - /// Get the basic externalities for this module, useful for tests. - #[cfg(any(feature = "std", test))] - pub fn externalities() -> TestExternalities { - map![ - twox_128(&>::key_for(T::BlockNumber::zero())).to_vec() => [69u8; 32].encode(), // TODO: replace with Hash::default().encode - twox_128(>::key()).to_vec() => T::BlockNumber::one().encode(), - twox_128(>::key()).to_vec() => [69u8; 32].encode(), // TODO: replace with Hash::default().encode - twox_128(>::key()).to_vec() => T::Hash::default().encode() - ] - } - - /// Set the block number to something in particular. Can be used as an alternative to - /// `initialise` for tests that don't need to bother with the other environment entries. - #[cfg(any(feature = "std", test))] - pub fn set_block_number(n: T::BlockNumber) { - >::put(n); - } - - /// Set the parent hash number to something in particular. Can be used as an alternative to - /// `initialise` for tests that don't need to bother with the other environment entries. - #[cfg(any(feature = "std", test))] - pub fn set_parent_hash(n: T::Hash) { - >::put(n); - } - - /// Set the random seed to something in particular. Can be used as an alternative to - /// `initialise` for tests that don't need to bother with the other environment entries. - #[cfg(any(feature = "std", test))] - pub fn set_random_seed(seed: T::Hash) { - >::put(seed); - } - - /// Increment a particular account's nonce by 1. - pub fn inc_account_nonce(who: &T::AccountId) { - >::insert(who, Self::account_nonce(who) + T::Index::one()); - } - - /// Note what the extrinsic data of the current extrinsic index is. If this is called, then - /// ensure `derive_extrinsics` is also called before block-building is completed. - pub fn note_extrinsic(encoded_xt: Vec) { - >::insert(>::get().unwrap_or_default(), encoded_xt); - } - - /// To be called immediately after an extrinsic has been applied. - pub fn note_applied_extrinsic(r: &Result<(), &'static str>) { - Self::deposit_event(match r { - Ok(_) => Event::ExtrinsicSuccess, - Err(_) => Event::ExtrinsicFailed, - }.into()); - >::put(>::get().unwrap_or_default() + 1u32); - } - - /// To be called immediately after `note_applied_extrinsic` of the last extrinsic of the block - /// has been called. - pub fn note_finished_extrinsics() { - >::put(>::get().unwrap_or_default()); - >::kill(); - } - - /// Remove all extrinsics data and save the extrinsics trie root. - pub fn derive_extrinsics() { - let extrinsics = (0..>::get().unwrap_or_default()).map(>::take).collect(); - let xts_root = extrinsics_data_root::(extrinsics); - >::put(xts_root); - } -} - -#[cfg(any(feature = "std", test))] -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -#[serde(deny_unknown_fields)] -pub struct GenesisConfig(PhantomData); - -#[cfg(any(feature = "std", test))] -impl Default for GenesisConfig { - fn default() -> Self { - GenesisConfig(PhantomData) - } -} - -#[cfg(any(feature = "std", test))] -impl primitives::BuildStorage for GenesisConfig -{ - fn build_storage(self) -> Result { - use codec::Encode; - - Ok(map![ - Self::hash(&>::key_for(T::BlockNumber::zero())).to_vec() => [69u8; 32].encode(), - Self::hash(>::key()).to_vec() => 1u64.encode(), - Self::hash(>::key()).to_vec() => [69u8; 32].encode(), - Self::hash(>::key()).to_vec() => [0u8; 32].encode(), - Self::hash(>::key()).to_vec() => [0u8; 4].encode() - ]) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use runtime_io::with_externalities; - use substrate_primitives::H256; - use primitives::BuildStorage; - use primitives::traits::BlakeTwo256; - use primitives::testing::{Digest, Header}; - - impl_outer_origin!{ - pub enum Origin for Test where system = super {} - } - - #[derive(Clone, Eq, PartialEq)] - pub struct Test; - impl Trait for Test { - type Origin = Origin; - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type Digest = Digest; - type AccountId = u64; - type Header = Header; - type Event = u16; - } - - impl From for u16 { - fn from(e: Event) -> u16 { - match e { - Event::ExtrinsicSuccess => 100, - Event::ExtrinsicFailed => 101, - } - } - } - - type System = Module; - - - - fn new_test_ext() -> runtime_io::TestExternalities { - GenesisConfig::::default().build_storage().unwrap().into() - } - - #[test] - fn deposit_event_should_work() { - with_externalities(&mut new_test_ext(), || { - System::initialise(&1, &[0u8; 32].into(), &[0u8; 32].into()); - System::note_finished_extrinsics(); - System::deposit_event(1u16); - System::finalise(); - assert_eq!(System::events(), vec![EventRecord { phase: Phase::Finalization, event: 1u16 }]); - - System::initialise(&2, &[0u8; 32].into(), &[0u8; 32].into()); - System::deposit_event(42u16); - System::note_applied_extrinsic(&Ok(())); - System::note_applied_extrinsic(&Err("")); - System::note_finished_extrinsics(); - System::deposit_event(3u16); - System::finalise(); - assert_eq!(System::events(), vec![ - EventRecord { phase: Phase::ApplyExtrinsic(0), event: 42u16 }, - EventRecord { phase: Phase::ApplyExtrinsic(0), event: 100u16 }, - EventRecord { phase: Phase::ApplyExtrinsic(1), event: 101u16 }, - EventRecord { phase: Phase::Finalization, event: 3u16 } - ]); - }); - } -} diff --git a/substrate/runtime/timestamp/Cargo.toml b/substrate/runtime/timestamp/Cargo.toml deleted file mode 100644 index c87da900bfe19..0000000000000 --- a/substrate/runtime/timestamp/Cargo.toml +++ /dev/null @@ -1,35 +0,0 @@ -[package] -name = "substrate-runtime-timestamp" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -hex-literal = "0.1.0" -serde = { version = "1.0", default_features = false } -serde_derive = { version = "1.0", optional = true } -substrate-runtime-std = { path = "../../runtime-std", default_features = false } -substrate-runtime-io = { path = "../../runtime-io", default_features = false } -substrate-runtime-support = { path = "../../runtime-support", default_features = false } -substrate-runtime-primitives = { path = "../primitives", default_features = false } -substrate-codec = { path = "../../codec", default_features = false } -substrate-primitives = { path = "../../primitives", default_features = false } -substrate-runtime-system = { path = "../system", default_features = false } -substrate-runtime-consensus = { path = "../consensus", default_features = false } - -[dev-dependencies] -substrate-runtime-io = { path = "../../runtime-io", default_features = true } - -[features] -default = ["std"] -std = [ - "substrate-runtime-std/std", - "substrate-runtime-io/std", - "substrate-runtime-support/std", - "substrate-runtime-primitives/std", - "substrate-runtime-consensus/std", - "serde/std", - "serde_derive", - "substrate-codec/std", - "substrate-primitives/std", - "substrate-runtime-system/std", -] diff --git a/substrate/runtime/timestamp/src/lib.rs b/substrate/runtime/timestamp/src/lib.rs deleted file mode 100644 index b586dae19b914..0000000000000 --- a/substrate/runtime/timestamp/src/lib.rs +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! Timestamp manager: just handles the current timestamp. - -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg_attr(any(feature = "std", test), macro_use)] -extern crate substrate_runtime_std as rstd; - -#[macro_use] -extern crate substrate_runtime_support as runtime_support; - -#[cfg(any(feature = "std", test))] -extern crate substrate_runtime_io as runtime_io; - -#[cfg(feature = "std")] -#[macro_use] -extern crate serde_derive; - -#[cfg(test)] -extern crate substrate_primitives; -extern crate substrate_runtime_primitives as runtime_primitives; -extern crate substrate_runtime_system as system; -extern crate substrate_runtime_consensus as consensus; -extern crate substrate_codec as codec; - -use runtime_support::{StorageValue, Parameter}; -use runtime_support::dispatch::Result; -use runtime_primitives::traits::{OnFinalise, SimpleArithmetic, As, Zero}; -use system::ensure_inherent; - -pub trait Trait: consensus::Trait + system::Trait { - // the position of the required timestamp-set extrinsic. - const TIMESTAMP_SET_POSITION: u32; - - type Moment: Parameter + Default + SimpleArithmetic + As; -} - -decl_module! { - pub struct Module for enum Call where origin: T::Origin { - fn set(origin, now: T::Moment) -> Result; - } -} - -decl_storage! { - trait Store for Module as Timestamp { - pub Now get(now): required T::Moment; - /// The minimum (and advised) period between blocks. - pub BlockPeriod get(block_period): required T::Moment; - - /// Did the timestamp get updated in this block? - DidUpdate: default bool; - } -} - -impl Module { - pub fn get() -> T::Moment { - Self::now() - } - - /// Set the current time. - fn set(origin: T::Origin, now: T::Moment) -> Result { - ensure_inherent(origin)?; - assert!(!::DidUpdate::exists(), "Timestamp must be updated only once in the block"); - assert!( - >::extrinsic_index() == Some(T::TIMESTAMP_SET_POSITION), - "Timestamp extrinsic must be at position {} in the block", - T::TIMESTAMP_SET_POSITION - ); - assert!( - Self::now().is_zero() || now >= Self::now() + Self::block_period(), - "Timestamp but increment by at least between sequential blocks" - ); - ::Now::put(now); - ::DidUpdate::put(true); - Ok(()) - } - - /// Set the timestamp to something in particular. Only used for tests. - #[cfg(any(feature = "std", test))] - pub fn set_timestamp(now: T::Moment) { - ::Now::put(now); - } -} - -impl OnFinalise for Module { - fn on_finalise(_n: T::BlockNumber) { - assert!(::DidUpdate::take(), "Timestamp must be updated once in the block"); - } -} - -#[cfg(any(feature = "std", test))] -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -#[serde(deny_unknown_fields)] -pub struct GenesisConfig { - pub period: T::Moment, -} - -#[cfg(any(feature = "std", test))] -impl Default for GenesisConfig { - fn default() -> Self { - GenesisConfig { - period: T::Moment::sa(5), - } - } -} - -#[cfg(any(feature = "std", test))] -impl runtime_primitives::BuildStorage for GenesisConfig -{ - fn build_storage(self) -> ::std::result::Result { - use codec::Encode; - Ok(map![ - Self::hash(>::key()).to_vec() => self.period.encode(), - Self::hash(>::key()).to_vec() => T::Moment::sa(0).encode() - ]) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - use runtime_io::with_externalities; - use substrate_primitives::H256; - use runtime_primitives::BuildStorage; - use runtime_primitives::traits::{BlakeTwo256}; - use runtime_primitives::testing::{Digest, Header}; - - impl_outer_origin! { - pub enum Origin for Test {} - } - - #[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 Digest = Digest; - type AccountId = u64; - type Header = Header; - type Event = (); - } - impl consensus::Trait for Test { - const NOTE_OFFLINE_POSITION: u32 = 1; - type Log = u64; - type SessionKey = u64; - type OnOfflineValidator = (); - } - impl Trait for Test { - const TIMESTAMP_SET_POSITION: u32 = 0; - type Moment = u64; - } - type Timestamp = Module; - - #[test] - fn timestamp_works() { - let mut t = system::GenesisConfig::::default().build_storage().unwrap(); - t.extend(GenesisConfig:: { period: 0 }.build_storage().unwrap()); - let mut t = runtime_io::TestExternalities::from(t); - with_externalities(&mut t, || { - Timestamp::set_timestamp(42); - assert_ok!(Timestamp::dispatch(Call::set(69), Origin::INHERENT)); - assert_eq!(Timestamp::now(), 69); - }); - } - - #[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(); - t.extend(GenesisConfig:: { period: 5 }.build_storage().unwrap()); - let mut t = runtime_io::TestExternalities::from(t); - with_externalities(&mut t, || { - Timestamp::set_timestamp(42); - assert_ok!(Timestamp::dispatch(Call::set(69), Origin::INHERENT)); - let _ = Timestamp::dispatch(Call::set(70), Origin::INHERENT); - }); - } - - #[test] - #[should_panic(expected = "Timestamp but increment by at least between sequential blocks")] - fn block_period_is_enforced() { - let mut t = system::GenesisConfig::::default().build_storage().unwrap(); - t.extend(GenesisConfig:: { period: 5 }.build_storage().unwrap()); - let mut t = runtime_io::TestExternalities::from(t); - with_externalities(&mut t, || { - Timestamp::set_timestamp(42); - let _ = Timestamp::dispatch(Call::set(46), Origin::INHERENT); - }); - } -} diff --git a/substrate/runtime/treasury/Cargo.toml b/substrate/runtime/treasury/Cargo.toml deleted file mode 100644 index 0c76961434c2c..0000000000000 --- a/substrate/runtime/treasury/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ -[package] -name = "substrate-runtime-treasury" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -hex-literal = "0.1.0" -serde = { version = "1.0", default_features = false } -serde_derive = { version = "1.0", optional = true } -substrate-runtime-std = { path = "../../runtime-std", default_features = false } -substrate-runtime-io = { path = "../../runtime-io", default_features = false } -substrate-runtime-support = { path = "../../runtime-support", default_features = false } -substrate-runtime-primitives = { path = "../primitives", default_features = false } -substrate-codec = { path = "../../codec", default_features = false } -substrate-codec-derive = { path = "../../codec/derive", default_features = false } -substrate-primitives = { path = "../../primitives", default_features = false } -substrate-runtime-system = { path = "../system", default_features = false } -substrate-runtime-balances = { path = "../balances", default_features = false } - -[features] -default = ["std"] -std = [ - "substrate-runtime-std/std", - "substrate-runtime-io/std", - "substrate-runtime-support/std", - "substrate-runtime-primitives/std", - "substrate-runtime-balances/std", - "serde/std", - "serde_derive", - "substrate-codec/std", - "substrate-codec-derive/std", - "substrate-primitives/std", - "substrate-runtime-system/std", -] diff --git a/substrate/runtime/treasury/src/lib.rs b/substrate/runtime/treasury/src/lib.rs deleted file mode 100644 index 1edf33a563066..0000000000000 --- a/substrate/runtime/treasury/src/lib.rs +++ /dev/null @@ -1,546 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! The Treasury: Keeps account of the taxed cash and handles its deployment. - -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg_attr(feature = "std", macro_use)] -extern crate substrate_runtime_std as rstd; - -#[macro_use] -extern crate substrate_runtime_support as runtime_support; - -#[cfg(feature = "std")] -extern crate substrate_runtime_io as runtime_io; - -#[cfg(feature = "std")] -#[macro_use] -extern crate serde_derive; - -#[macro_use] -extern crate substrate_codec_derive; - -extern crate substrate_codec as codec; -#[cfg(test)] -extern crate substrate_primitives; -extern crate substrate_runtime_primitives as runtime_primitives; -extern crate substrate_runtime_system as system; -extern crate substrate_runtime_balances as balances; - -use rstd::prelude::*; -use runtime_support::{StorageValue, StorageMap}; -use runtime_support::dispatch::Result; -use runtime_primitives::{Permill, traits::{OnFinalise, Zero, EnsureOrigin}}; -use balances::OnDilution; -use system::{ensure_signed, ensure_root}; - -/// 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 -/// should be added to our implied traits list. -/// -/// `system::Trait` should always be included in our implied traits. -pub trait Trait: balances::Trait { - /// Origin from which approvals must come. - type ApproveOrigin: EnsureOrigin; - - /// Origin from which rejections must come. - type RejectOrigin: EnsureOrigin; - - /// The overarching event type. - type Event: From> + Into<::Event>; -} - -type ProposalIndex = u32; - -// The module declaration. This states the entry points that we handle. The -// macro takes care of the marshalling of arguments and dispatch. -decl_module! { - // Simple declaration of the `Module` type. Lets the macro know what its working on. - pub struct Module for enum Call where origin: T::Origin { - // 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 - // proposal is awarded. - fn propose_spend(origin, value: T::Balance, beneficiary: T::AccountId) -> Result; - - // Set the balance of funds available to spend. - fn set_pot(origin, new_pot: T::Balance) -> Result; - - // (Re-)configure this module. - fn configure(origin, proposal_bond: Permill, proposal_bond_minimum: T::Balance, spend_period: T::BlockNumber, burn: Permill) -> Result; - - // Reject a proposed spend. The original deposit will be slashed. - fn reject_proposal(origin, roposal_id: ProposalIndex) -> Result; - - // Approve a proposal. At a later time, the proposal will be allocated to the beneficiary - // and the original deposit will be returned. - fn approve_proposal(origin, proposal_id: ProposalIndex) -> Result; - } -} - -/// A spending proposal. -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] -#[derive(Encode, Decode, Clone, PartialEq, Eq)] -pub struct Proposal { - proposer: AccountId, - value: Balance, - beneficiary: AccountId, - bond: Balance, -} - -decl_storage! { - trait Store for Module as Treasury { - // Config... - - /// Proportion of funds that should be bonded in order to place a proposal. An accepted - /// proposal gets these back. A rejected proposal doesn't. - ProposalBond get(proposal_bond): required Permill; - - /// Minimum amount of funds that should be placed ina deposit for making a proposal. - ProposalBondMinimum get(proposal_bond_minimum): required T::Balance; - - /// Period between successive spends. - SpendPeriod get(spend_period): required T::BlockNumber; - - /// Percentage of spare funds (if any) that are burnt per spend period. - Burn get(burn): required Permill; - - // State... - - /// Total funds available to this module for spending. - Pot get(pot): default T::Balance; - - /// Number of proposals that have been made. - ProposalCount get(proposal_count): default ProposalIndex; - - /// Proposals that have been made. - Proposals get(proposals): map [ ProposalIndex => Proposal ]; - - /// Proposal indices that have been approved but not yet awarded. - Approvals get(approvals): default Vec; - } -} - -/// An event in this module. -decl_event!( - pub enum Event with RawEvent - where ::Balance, ::AccountId - { - /// New proposal. - Proposed(ProposalIndex), - /// We have ended a spend period and will now allocate funds. - Spending(Balance), - /// Some funds have been allocated. - Awarded(ProposalIndex, Balance, AccountId), - /// Some of our funds have been burnt. - Burnt(Balance), - /// Spending has finished; this is the amount that rolls over until next spend. - Rollover(Balance), - } -); - -impl Module { - /// Deposit one of this module's events. - fn deposit_event(event: Event) { - >::deposit_event(::Event::from(event).into()); - } - - // Implement Calls and add public immutables and private mutables. - - fn propose_spend(origin: T::Origin, value: T::Balance, beneficiary: T::AccountId) -> Result { - let proposer = ensure_signed(origin)?; - - let bond = Self::calculate_bond(value); - >::reserve(&proposer, bond) - .map_err(|_| "Proposer's balance too low")?; - - let c = Self::proposal_count(); - >::put(c + 1); - >::insert(c, Proposal { proposer, value, beneficiary, bond }); - - Self::deposit_event(RawEvent::Proposed(c)); - - Ok(()) - } - - fn reject_proposal(origin: T::Origin, proposal_id: ProposalIndex) -> Result { - T::RejectOrigin::ensure_origin(origin)?; - - let proposal = >::take(proposal_id).ok_or("No proposal at that index")?; - - let value = proposal.bond; - let _ = >::slash_reserved(&proposal.proposer, value); - - Ok(()) - } - - fn approve_proposal(origin: T::Origin, proposal_id: ProposalIndex) -> Result { - T::ApproveOrigin::ensure_origin(origin)?; - - ensure!(>::exists(proposal_id), "No proposal at that index"); - - >::mutate(|v| v.push(proposal_id)); - - Ok(()) - } - - fn set_pot(origin: T::Origin, new_pot: T::Balance) -> Result { - ensure_root(origin)?; - // Put the new value into storage. - >::put(new_pot); - - // All good. - Ok(()) - } - - fn configure( - origin: T::Origin, - proposal_bond: Permill, - proposal_bond_minimum: T::Balance, - spend_period: T::BlockNumber, - burn: Permill - ) -> Result { - ensure_root(origin)?; - >::put(proposal_bond); - >::put(proposal_bond_minimum); - >::put(spend_period); - >::put(burn); - Ok(()) - } - - /// The needed bond for a proposal whose spend is `value`. - fn calculate_bond(value: T::Balance) -> T::Balance { - Self::proposal_bond_minimum().max(Self::proposal_bond().times(value)) - } - - // Spend some money! - fn spend_funds() { - let mut budget_remaining = Self::pot(); - Self::deposit_event(RawEvent::Spending(budget_remaining)); - - let mut missed_any = false; - let remaining_approvals: Vec<_> = >::get().into_iter().filter(|&index| { - // Should always be true, but shouldn't panic if false or we're screwed. - if let Some(p) = Self::proposals(index) { - if p.value <= budget_remaining { - budget_remaining -= p.value; - >::remove(index); - - // return their deposit. - let _ = >::unreserve(&p.proposer, p.bond); - - // provide the allocation. - >::increase_free_balance_creating(&p.beneficiary, p.value); - - Self::deposit_event(RawEvent::Awarded(index, p.value, p.beneficiary)); - false - } else { - missed_any = true; - true - } - } else { - false - } - }).collect(); - >::put(remaining_approvals); - - if !missed_any { - // burn some proportion of the remaining budget if we run a surplus. - let burn = Self::burn().times(budget_remaining); - budget_remaining -= burn; - Self::deposit_event(RawEvent::Burnt(burn)) - } - - Self::deposit_event(RawEvent::Rollover(budget_remaining)); - - >::put(budget_remaining); - } -} - -impl OnDilution for Module { - fn on_dilution(minted: T::Balance, portion: T::Balance) { - // Mint extra funds for the treasury to keep the ratio of portion to total_issuance equal - // pre dilution and post-dilution. - if !minted.is_zero() && !portion.is_zero() { - let total_issuance = >::total_issuance(); - let funding = (total_issuance - portion) / portion * minted; - >::mutate(|x| *x += funding); - } - } -} - -impl OnFinalise for Module { - fn on_finalise(n: T::BlockNumber) { - // Check to see if we should spend some funds! - if (n % Self::spend_period()).is_zero() { - Self::spend_funds(); - } - } -} - -#[cfg(feature = "std")] -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -#[serde(deny_unknown_fields)] -/// The genesis block configuration type. This is a simple default-capable struct that -/// contains any fields with which this module can be configured at genesis time. -pub struct GenesisConfig { - pub proposal_bond: Permill, - pub proposal_bond_minimum: T::Balance, - pub spend_period: T::BlockNumber, - pub burn: Permill, -} - -#[cfg(feature = "std")] -impl Default for GenesisConfig { - fn default() -> Self { - GenesisConfig { - proposal_bond: Default::default(), - proposal_bond_minimum: Default::default(), - spend_period: runtime_primitives::traits::One::one(), - burn: Default::default(), - } - } -} - -#[cfg(feature = "std")] -impl runtime_primitives::BuildStorage for GenesisConfig -{ - fn build_storage(self) -> ::std::result::Result { - use codec::Encode; - Ok(map![ - Self::hash(>::key()).to_vec() => self.proposal_bond.encode(), - Self::hash(>::key()).to_vec() => self.proposal_bond_minimum.encode(), - Self::hash(>::key()).to_vec() => self.spend_period.encode(), - Self::hash(>::key()).to_vec() => self.burn.encode() - ]) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - use runtime_io::with_externalities; - use substrate_primitives::{H256, Blake2Hasher}; - use runtime_primitives::BuildStorage; - use runtime_primitives::traits::{BlakeTwo256}; - use runtime_primitives::testing::{Digest, Header}; - - impl_outer_origin! { - pub enum Origin for Test {} - } - - #[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 Digest = Digest; - type AccountId = u64; - type Header = Header; - type Event = (); - } - impl balances::Trait for Test { - type Balance = u64; - type AccountIndex = u64; - type OnFreeBalanceZero = (); - type EnsureAccountLiquid = (); - type Event = (); - } - impl Trait for Test { - type ApproveOrigin = system::EnsureRoot; - type RejectOrigin = system::EnsureRoot; - type Event = (); - } - type Balances = balances::Module; - type Treasury = Module; - - fn new_test_ext() -> runtime_io::TestExternalities { - let mut t = system::GenesisConfig::::default().build_storage().unwrap(); - 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, - reclaim_rebate: 0, - }.build_storage().unwrap()); - t.extend(GenesisConfig::{ - proposal_bond: Permill::from_percent(5), - proposal_bond_minimum: 1, - spend_period: 2, - burn: Permill::from_percent(50), - }.build_storage().unwrap()); - 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); - }); - } - - #[test] - fn minting_works() { - with_externalities(&mut new_test_ext(), || { - // Check that accumulate works when we have Some value in Dummy already. - Treasury::on_dilution(100, 100); - assert_eq!(Treasury::pot(), 100); - }); - } - - #[test] - fn spend_proposal_takes_min_deposit() { - with_externalities(&mut new_test_ext(), || { - assert_ok!(Treasury::propose_spend(Origin::signed(0), 1, 3)); - assert_eq!(Balances::free_balance(&0), 99); - assert_eq!(Balances::reserved_balance(&0), 1); - }); - } - - #[test] - fn spend_proposal_takes_proportional_deposit() { - with_externalities(&mut new_test_ext(), || { - assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3)); - assert_eq!(Balances::free_balance(&0), 95); - assert_eq!(Balances::reserved_balance(&0), 5); - }); - } - - #[test] - fn spend_proposal_fails_when_proposer_poor() { - with_externalities(&mut new_test_ext(), || { - assert_noop!(Treasury::propose_spend(Origin::signed(2), 100, 3), "Proposer's balance too low"); - }); - } - - #[test] - fn accepted_spend_proposal_ignored_outside_spend_period() { - with_externalities(&mut new_test_ext(), || { - Treasury::on_dilution(100, 100); - - assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3)); - assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); - - >::on_finalise(1); - assert_eq!(Balances::free_balance(&3), 0); - assert_eq!(Treasury::pot(), 100); - }); - } - - #[test] - fn unused_pot_should_diminish() { - with_externalities(&mut new_test_ext(), || { - Treasury::on_dilution(100, 100); - - >::on_finalise(2); - assert_eq!(Treasury::pot(), 50); - }); - } - - #[test] - fn rejected_spend_proposal_ignored_on_spend_period() { - with_externalities(&mut new_test_ext(), || { - Treasury::on_dilution(100, 100); - - assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3)); - assert_ok!(Treasury::reject_proposal(Origin::ROOT, 0)); - - >::on_finalise(2); - assert_eq!(Balances::free_balance(&3), 0); - assert_eq!(Treasury::pot(), 50); - }); - } - - #[test] - fn reject_already_rejected_spend_proposal_fails() { - with_externalities(&mut new_test_ext(), || { - Treasury::on_dilution(100, 100); - - assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3)); - assert_ok!(Treasury::reject_proposal(Origin::ROOT, 0)); - assert_noop!(Treasury::reject_proposal(Origin::ROOT, 0), "No proposal at that index"); - }); - } - - #[test] - fn reject_non_existant_spend_proposal_fails() { - with_externalities(&mut new_test_ext(), || { - assert_noop!(Treasury::reject_proposal(Origin::ROOT, 0), "No proposal at that index"); - }); - } - - #[test] - fn accept_non_existant_spend_proposal_fails() { - with_externalities(&mut new_test_ext(), || { - assert_noop!(Treasury::approve_proposal(Origin::ROOT, 0), "No proposal at that index"); - }); - } - - #[test] - fn accept_already_rejected_spend_proposal_fails() { - with_externalities(&mut new_test_ext(), || { - Treasury::on_dilution(100, 100); - - assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3)); - assert_ok!(Treasury::reject_proposal(Origin::ROOT, 0)); - assert_noop!(Treasury::approve_proposal(Origin::ROOT, 0), "No proposal at that index"); - }); - } - - #[test] - fn accepted_spend_proposal_enacted_on_spend_period() { - with_externalities(&mut new_test_ext(), || { - Treasury::on_dilution(100, 100); - - assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3)); - assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); - - >::on_finalise(2); - assert_eq!(Balances::free_balance(&3), 100); - assert_eq!(Treasury::pot(), 0); - }); - } - - #[test] - fn pot_underflow_should_not_diminish() { - with_externalities(&mut new_test_ext(), || { - Treasury::on_dilution(100, 100); - - assert_ok!(Treasury::propose_spend(Origin::signed(0), 150, 3)); - assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); - - >::on_finalise(2); - assert_eq!(Treasury::pot(), 100); - - Treasury::on_dilution(100, 100); - >::on_finalise(4); - assert_eq!(Balances::free_balance(&3), 150); - assert_eq!(Treasury::pot(), 25); - }); - } -} diff --git a/substrate/runtime/version/Cargo.toml b/substrate/runtime/version/Cargo.toml deleted file mode 100644 index 2f471fe6ed085..0000000000000 --- a/substrate/runtime/version/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "substrate-runtime-version" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -serde = { version = "1.0", default_features = false } -serde_derive = { version = "1.0", optional = true } -substrate-codec = { path = "../../codec", default_features = false } -substrate-codec-derive = { path = "../../codec/derive", default_features = false } -substrate-runtime-std = { path = "../../runtime-std", default_features = false } -substrate-runtime-support = { path = "../../runtime-support", default_features = false } - -[features] -default = ["std"] -std = [ - "serde/std", - "serde_derive", - "substrate-codec/std", - "substrate-runtime-std/std", - "substrate-runtime-support/std", -] diff --git a/substrate/runtime/version/src/lib.rs b/substrate/runtime/version/src/lib.rs deleted file mode 100644 index c8e3b8f4a6dc1..0000000000000 --- a/substrate/runtime/version/src/lib.rs +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2017 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 . - -//! Version module for runtime; Provide a function that returns runtime version. - -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(feature = "std")] -extern crate serde; - -#[cfg(feature = "std")] -#[macro_use] -extern crate serde_derive; - -#[allow(unused_imports)] -#[macro_use] -extern crate substrate_runtime_std as rstd; - -#[macro_use] -extern crate substrate_codec_derive; - -extern crate substrate_codec as codec; - -#[cfg(feature = "std")] -use std::fmt; - -#[cfg(feature = "std")] -pub type VersionString = ::std::borrow::Cow<'static, str>; -#[cfg(not(feature = "std"))] -pub type VersionString = &'static str; - -#[cfg(feature = "std")] -#[macro_export] -macro_rules! ver_str { - ( $y:expr ) => {{ ::std::borrow::Cow::Borrowed($y) }} -} - -#[cfg(not(feature = "std"))] -#[macro_export] -macro_rules! ver_str { - ( $y:expr ) => {{ $y }} -} - -/// Runtime version. -/// This should not be thought of as classic Semver (major/minor/tiny). -/// This triplet have different semantics and mis-interpretation could cause problems. -/// In particular: bug fixes should result in an increment of `spec_version` and possibly `authoring_version`, -/// absolutely not `impl_version` since they change the semantics of the runtime. -#[derive(Clone, PartialEq, Eq, Encode)] -#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize, Decode))] -pub struct RuntimeVersion { - /// Identifies the different Substrate runtimes. There'll be at least polkadot and demo. - /// A different on-chain spec_name to that of the native runtime would normally result - /// in node not attempting to sync or author blocks. - pub spec_name: VersionString, - - /// Name of the implementation of the spec. This is of little consequence for the node - /// and serves only to differentiate code of different implementation teams. For this - /// codebase, it will be parity-polkadot. If there were a non-Rust implementation of the - /// Polkadot runtime (e.g. C++), then it would identify itself with an accordingly different - /// `impl_name`. - pub impl_name: VersionString, - - /// `authoring_version` is the version of the authorship interface. An authoring node - /// will not attempt to author blocks unless this is equal to its native runtime. - pub authoring_version: u32, - - /// Version of the runtime specification. A full-node will not attempt to use its native - /// runtime in substitute for the on-chain Wasm runtime unless all of `spec_name`, - /// `spec_version` and `authoring_version` are the same between Wasm and native. - pub spec_version: u32, - - /// Version of the implementation of the specification. Nodes are free to ignore this; it - /// serves only as an indication that the code is different; as long as the other two versions - /// are the same then while the actual code may be different, it is nonetheless required to - /// do the same thing. - /// Non-consensus-breaking optimisations are about the only changes that could be made which - /// would result in only the `impl_version` changing. - pub impl_version: u32, -} - -// TODO: remove this after PoC-2 -#[cfg(feature = "std")] -impl Default for RuntimeVersion { - fn default() -> RuntimeVersion { - RuntimeVersion { - spec_name: ver_str!("polkadot"), - impl_name: ver_str!("parity-polkadot"), - authoring_version: 0, - spec_version: 0, - impl_version: 0, - } - } -} - -#[cfg(feature = "std")] -impl fmt::Display for RuntimeVersion { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}-{}:{}({}-{})", self.spec_name, self.spec_version, self.authoring_version, self.impl_name, self.impl_version) - } -} - -#[cfg(feature = "std")] -impl RuntimeVersion { - /// Check if this version matches other version for calling into runtime. - pub fn can_call_with(&self, other: &RuntimeVersion) -> bool { - self.spec_version == other.spec_version && - self.spec_name == other.spec_name && - self.authoring_version == other.authoring_version - } - - /// Check if this version matches other version for authoring blocks. - pub fn can_author_with(&self, other: &RuntimeVersion) -> bool { - self.authoring_version == other.authoring_version && - self.spec_name == other.spec_name - } -} diff --git a/substrate/serializer/Cargo.toml b/substrate/serializer/Cargo.toml deleted file mode 100644 index 412586599e6ce..0000000000000 --- a/substrate/serializer/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "substrate-serializer" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -serde = { version = "1.0", default_features = false } -serde_json = "1.0" diff --git a/substrate/serializer/README.adoc b/substrate/serializer/README.adoc deleted file mode 100644 index 7b780bae43677..0000000000000 --- a/substrate/serializer/README.adoc +++ /dev/null @@ -1,14 +0,0 @@ - -= Serializer - -.Summary -[source, toml] ----- -include::Cargo.toml[lines=2..5] ----- - -.Description ----- -include::src/lib.rs[tag=description] ----- - diff --git a/substrate/serializer/src/lib.rs b/substrate/serializer/src/lib.rs deleted file mode 100644 index 48afc57497354..0000000000000 --- a/substrate/serializer/src/lib.rs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -// tag::description[] -//! Substrate customizable serde serializer. -//! -//! The idea is that we can later change the implementation -//! to something more compact, but for now we're using JSON. -// end::description[] - -#![warn(missing_docs)] - -extern crate serde; -extern crate serde_json; - -pub use serde_json::{from_str, from_slice, from_reader, Result, Error}; - -const PROOF: &str = "Serializers are infallible; qed"; - -/// Serialize the given data structure as a pretty-printed String of JSON. -pub fn to_string_pretty(value: &T) -> String { - serde_json::to_string_pretty(value).expect(PROOF) -} - -/// Serialize the given data structure as a JSON byte vector. -pub fn encode(value: &T) -> Vec { - serde_json::to_vec(value).expect(PROOF) -} - -/// Serialize the given data structure as JSON into the IO stream. -pub fn to_writer(writer: W, value: &T) -> Result<()> { - serde_json::to_writer(writer, value) -} diff --git a/substrate/service/Cargo.toml b/substrate/service/Cargo.toml deleted file mode 100644 index a7e1d65c88425..0000000000000 --- a/substrate/service/Cargo.toml +++ /dev/null @@ -1,31 +0,0 @@ -[package] -name = "substrate-service" -version = "0.3.0" -authors = ["Parity Technologies "] - -[dependencies] -futures = "0.1.17" -parking_lot = "0.4" -error-chain = "0.12" -lazy_static = "1.0" -log = "0.3" -slog = "^2" -tokio = "0.1.7" -exit-future = "0.1" -serde = "1.0" -serde_json = "1.0" -serde_derive = "1.0" -target_info = "0.1" -substrate-keystore = { path = "../../substrate/keystore" } -substrate-runtime-io = { path = "../../substrate/runtime-io" } -substrate-runtime-primitives = { path = "../../substrate/runtime/primitives" } -substrate-primitives = { path = "../../substrate/primitives" } -substrate-network = { path = "../../substrate/network" } -substrate-client = { path = "../../substrate/client" } -substrate-client-db = { path = "../../substrate/client/db" } -substrate-codec = { path = "../../substrate/codec" } -substrate-executor = { path = "../../substrate/executor" } -substrate-extrinsic-pool = { path = "../../substrate/extrinsic-pool" } -substrate-rpc = { path = "../../substrate/rpc" } -substrate-rpc-servers = { path = "../../substrate/rpc-servers" } -substrate-telemetry = { path = "../../substrate/telemetry" } diff --git a/substrate/service/README.adoc b/substrate/service/README.adoc deleted file mode 100644 index 4d74c098b2e17..0000000000000 --- a/substrate/service/README.adoc +++ /dev/null @@ -1,14 +0,0 @@ - -= Service - -.Summary -[source, toml] ----- -include::Cargo.toml[lines=2..5] ----- - -.Description ----- -include::src/lib.rs[tag=description] ----- - diff --git a/substrate/service/src/chain_ops.rs b/substrate/service/src/chain_ops.rs deleted file mode 100644 index aeefda6a676a0..0000000000000 --- a/substrate/service/src/chain_ops.rs +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2017 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 . - -//! Chain utilities. - -use std::{self, io::{Read, Write}}; -use futures::Future; -use serde_json; - -use client::BlockOrigin; -use runtime_primitives::generic::{SignedBlock, BlockId}; -use runtime_primitives::traits::{As}; -use components::{ServiceFactory, FactoryFullConfiguration, FactoryBlockNumber, RuntimeGenesis}; -use new_client; -use codec::{Decode, Encode}; -use error; -use chain_spec::ChainSpec; - -/// Export a range of blocks to a binary stream. -pub fn export_blocks(config: FactoryFullConfiguration, exit: E, mut output: W, from: FactoryBlockNumber, to: Option>, json: bool) -> error::Result<()> - where F: ServiceFactory, E: Future + Send + 'static, W: Write, -{ - let client = new_client::(config)?; - let mut block = from; - - let last = match to { - Some(v) if v == As::sa(0) => As::sa(1), - Some(v) => v, - None => client.info()?.chain.best_number, - }; - - if last < block { - return Err("Invalid block range specified".into()); - } - - let (exit_send, exit_recv) = std::sync::mpsc::channel(); - ::std::thread::spawn(move || { - let _ = exit.wait(); - let _ = exit_send.send(()); - }); - info!("Exporting blocks from #{} to #{}", block, last); - if !json { - output.write(&(last - block + As::sa(1)).encode())?; - } - - loop { - if exit_recv.try_recv().is_ok() { - break; - } - match client.block(&BlockId::number(block))? { - Some(block) => { - if json { - serde_json::to_writer(&mut output, &block).map_err(|e| format!("Eror writing JSON: {}", e))?; - } else { - output.write(&block.encode())?; - } - }, - None => break, - } - if block.as_() % 10000 == 0 { - info!("#{}", block); - } - if block == last { - break; - } - block += As::sa(1); - } - Ok(()) -} - -/// Import blocks from a binary stream. -pub fn import_blocks(config: FactoryFullConfiguration, exit: E, mut input: R) -> error::Result<()> - where F: ServiceFactory, E: Future + Send + 'static, R: Read, -{ - let client = new_client::(config)?; - - let (exit_send, exit_recv) = std::sync::mpsc::channel(); - ::std::thread::spawn(move || { - let _ = exit.wait(); - let _ = exit_send.send(()); - }); - - let count: u32 = Decode::decode(&mut input).ok_or("Error reading file")?; - info!("Importing {} blocks", count); - let mut block = 0; - for _ in 0 .. count { - if exit_recv.try_recv().is_ok() { - break; - } - match SignedBlock::decode(&mut input) { - Some(block) => { - let header = client.check_justification(block.block.header, block.justification.into())?; - client.import_block(BlockOrigin::File, header, Some(block.block.extrinsics))?; - }, - None => { - warn!("Error reading block data."); - break; - } - } - block += 1; - if block % 1000 == 0 { - info!("#{}", block); - } - } - info!("Imported {} blocks. Best: #{}", block, client.info()?.chain.best_number); - - Ok(()) -} - -/// Revert the chain. -pub fn revert_chain(config: FactoryFullConfiguration, blocks: FactoryBlockNumber) -> error::Result<()> - where F: ServiceFactory, -{ - let client = new_client::(config)?; - let reverted = client.revert(blocks)?; - let info = client.info()?.chain; - info!("Reverted {} blocks. Best: #{} ({})", reverted, info.best_number, info.best_hash); - Ok(()) -} - -/// Build a chain spec json -pub fn build_spec(spec: ChainSpec, raw: bool) -> error::Result - where G: RuntimeGenesis, -{ - Ok(spec.to_json(raw)?) -} diff --git a/substrate/service/src/chain_spec.rs b/substrate/service/src/chain_spec.rs deleted file mode 100644 index 6ccd0545b4435..0000000000000 --- a/substrate/service/src/chain_spec.rs +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! Substrate chain configurations. - -use std::collections::HashMap; -use std::fs::File; -use std::path::PathBuf; -use primitives::storage::{StorageKey, StorageData}; -use runtime_primitives::{BuildStorage, StorageMap}; -use serde_json as json; -use components::RuntimeGenesis; - -enum GenesisSource { - File(PathBuf), - Embedded(&'static [u8]), - Factory(fn() -> G), -} - -impl GenesisSource { - fn resolve(&self) -> Result, String> { - #[derive(Serialize, Deserialize)] - struct GenesisContainer { - genesis: Genesis, - } - - match *self { - GenesisSource::File(ref path) => { - let file = File::open(path).map_err(|e| format!("Error opening spec file: {}", e))?; - let genesis: GenesisContainer = json::from_reader(file).map_err(|e| format!("Error parsing spec file: {}", e))?; - Ok(genesis.genesis) - }, - GenesisSource::Embedded(buf) => { - let genesis: GenesisContainer = json::from_reader(buf).map_err(|e| format!("Error parsing embedded file: {}", e))?; - Ok(genesis.genesis) - }, - GenesisSource::Factory(f) => Ok(Genesis::Runtime(f())), - } - } -} - -impl<'a, G: RuntimeGenesis> BuildStorage for &'a ChainSpec { - fn build_storage(self) -> Result { - match self.genesis.resolve()? { - Genesis::Runtime(gc) => gc.build_storage(), - Genesis::Raw(map) => Ok(map.into_iter().map(|(k, v)| (k.0, v.0)).collect()), - } - } -} - -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -#[serde(deny_unknown_fields)] -enum Genesis { - Runtime(G), - Raw(HashMap), -} - -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -struct ChainSpecFile { - pub name: String, - pub id: String, - pub boot_nodes: Vec, - pub telemetry_url: Option, -} - -/// A configuration of a chain. Can be used to build a genesis block. -pub struct ChainSpec { - spec: ChainSpecFile, - genesis: GenesisSource, -} - -impl ChainSpec { - pub fn boot_nodes(&self) -> &[String] { - &self.spec.boot_nodes - } - - pub fn name(&self) -> &str { - &self.spec.name - } - - pub fn id(&self) -> &str { - &self.spec.id - } - - pub fn telemetry_url(&self) -> Option<&str> { - self.spec.telemetry_url.as_ref().map(String::as_str) - } - - /// Parse json content into a `ChainSpec` - pub fn from_embedded(json: &'static [u8]) -> Result { - let spec = json::from_slice(json).map_err(|e| format!("Error parsing spec file: {}", e))?; - Ok(ChainSpec { - spec, - genesis: GenesisSource::Embedded(json), - }) - } - - /// Parse json file into a `ChainSpec` - pub fn from_json_file(path: PathBuf) -> Result { - let file = File::open(&path).map_err(|e| format!("Error opening spec file: {}", e))?; - let spec = json::from_reader(file).map_err(|e| format!("Error parsing spec file: {}", e))?; - Ok(ChainSpec { - spec, - genesis: GenesisSource::File(path), - }) - } - - /// Create hardcoded spec. - pub fn from_genesis( - name: &str, - id: &str, - constructor: fn() -> G, - boot_nodes: Vec, - telemetry_url: Option<&str> - ) -> Self - { - let spec = ChainSpecFile { - name: name.to_owned(), - id: id.to_owned(), - boot_nodes: boot_nodes, - telemetry_url: telemetry_url.map(str::to_owned), - }; - ChainSpec { - spec, - genesis: GenesisSource::Factory(constructor), - } - } - - /// Dump to json string. - pub fn to_json(self, raw: bool) -> Result { - #[derive(Serialize, Deserialize)] - struct Container { - #[serde(flatten)] - spec: ChainSpecFile, - #[serde(flatten)] - genesis: Genesis, - - }; - let genesis = match (raw, self.genesis.resolve()?) { - (true, Genesis::Runtime(g)) => { - let storage = g.build_storage()?.into_iter() - .map(|(k, v)| (StorageKey(k), StorageData(v))) - .collect(); - - Genesis::Raw(storage) - }, - (_, genesis) => genesis, - }; - let spec = Container { - spec: self.spec, - genesis, - }; - json::to_string_pretty(&spec).map_err(|e| format!("Error generating spec json: {}", e)) - } -} diff --git a/substrate/service/src/components.rs b/substrate/service/src/components.rs deleted file mode 100644 index 38088b905069d..0000000000000 --- a/substrate/service/src/components.rs +++ /dev/null @@ -1,256 +0,0 @@ -// Copyright 2017 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 . - -//! Polkadot service components. - -use std::fmt; -use std::sync::Arc; -use std::marker::PhantomData; -use serde::{Serialize, de::DeserializeOwned}; -use chain_spec::ChainSpec; -use client_db; -use client::{self, Client}; -use error; -use network::{self, OnDemand}; -use substrate_executor::{NativeExecutor, NativeExecutionDispatch}; -use extrinsic_pool::{self, Options as ExtrinsicPoolOptions, Pool as ExtrinsicPool}; -use runtime_primitives::{traits::Block as BlockT, traits::Header as HeaderT, BuildStorage}; -use config::Configuration; -use primitives::{Blake2Hasher, RlpCodec, H256}; - -// Type aliases. -// These exist mainly to avoid typing `::Foo` all over the code. -/// Network service type for a factory. -pub type NetworkService = network::Service< - ::Block, - ::NetworkProtocol, - ::ExtrinsicHash, ->; - -/// Code executor type for a factory. -pub type CodeExecutor = NativeExecutor<::RuntimeDispatch>; - -/// Full client backend type for a factory. -pub type FullBackend = client_db::Backend<::Block>; - -/// Full client executor type for a factory. -pub type FullExecutor = client::LocalCallExecutor< - client_db::Backend<::Block>, - CodeExecutor, ->; - -/// Light client backend type for a factory. -pub type LightBackend = client::light::backend::Backend< - client_db::light::LightStorage<::Block>, - network::OnDemand<::Block, NetworkService>, ->; - -/// Light client executor type for a factory. -pub type LightExecutor = client::light::call_executor::RemoteCallExecutor< - client::light::blockchain::Blockchain< - client_db::light::LightStorage<::Block>, - network::OnDemand<::Block, NetworkService> - >, - network::OnDemand<::Block, NetworkService>, - Blake2Hasher, - RlpCodec, ->; - -/// Full client type for a factory. -pub type FullClient = Client, FullExecutor, ::Block>; - -/// Light client type for a factory. -pub type LightClient = Client, LightExecutor, ::Block>; - -/// `ChainSpec` specialization for a factory. -pub type FactoryChainSpec = ChainSpec<::Genesis>; - -/// `Genesis` specialization for a factory. -pub type FactoryGenesis = ::Genesis; - -/// `Block` type for a factory. -pub type FactoryBlock = ::Block; - -/// `Number` type for a factory. -pub type FactoryBlockNumber = < as BlockT>::Header as HeaderT>::Number; - -/// Full `Configuration` type for a factory. -pub type FactoryFullConfiguration = Configuration<::Configuration, FactoryGenesis>; - -/// Client type for `Components`. -pub type ComponentClient = Client< - ::Backend, - ::Executor, - FactoryBlock<::Factory> ->; - -/// Block type for `Components` -pub type ComponentBlock = <::Factory as ServiceFactory>::Block; - -/// Extrinsic hash type for `Components` -pub type ComponentExHash = <::ExtrinsicPoolApi as extrinsic_pool::ChainApi>::Hash; - -/// Extrinsic type. -pub type ComponentExtrinsic = as BlockT>::Extrinsic; - -/// Extrinsic pool API type for `Components`. -pub type PoolApi = ::ExtrinsicPoolApi; - -/// A set of traits for the runtime genesis config. -pub trait RuntimeGenesis: Serialize + DeserializeOwned + BuildStorage {} -impl RuntimeGenesis for T {} - -/// A collection of types and methods to build a service on top of the substrate service. -pub trait ServiceFactory: 'static { - /// Block type. - type Block: BlockT; - /// Extrinsic hash type. - type ExtrinsicHash: ::std::hash::Hash + Eq + Copy + fmt::Debug + fmt::LowerHex + Serialize + DeserializeOwned + ::std::str::FromStr + Send + Sync + Default + 'static; - /// Network protocol extensions. - type NetworkProtocol: network::specialization::Specialization; - /// Chain runtime. - type RuntimeDispatch: NativeExecutionDispatch + Send + Sync + 'static; - /// Extrinsic pool backend type for the full client. - type FullExtrinsicPoolApi: extrinsic_pool::ChainApi + Send + 'static; - /// Extrinsic pool backend type for the light client. - type LightExtrinsicPoolApi: extrinsic_pool::ChainApi + 'static; - /// Genesis configuration for the runtime. - type Genesis: RuntimeGenesis; - /// Other configuration for service members. - type Configuration: Default; - - /// Network protocol id. - const NETWORK_PROTOCOL_ID: network::ProtocolId; - - //TODO: replace these with a constructor trait. that ExtrinsicPool implements. - /// Extrinsic pool constructor for the full client. - fn build_full_extrinsic_pool(config: ExtrinsicPoolOptions, client: Arc>) - -> Result, error::Error>; - /// Extrinsic pool constructor for the light client. - fn build_light_extrinsic_pool(config: ExtrinsicPoolOptions, client: Arc>) - -> Result, error::Error>; - - /// Build network protocol. - fn build_network_protocol(config: &FactoryFullConfiguration) - -> Result; -} - -/// A collection of types and function to generalise over full / light client type. -pub trait Components: 'static { - /// Associated service factory. - type Factory: ServiceFactory; - /// Client backend. - type Backend: 'static + client::backend::Backend, Blake2Hasher, RlpCodec>; - /// Client executor. - type Executor: 'static + client::CallExecutor, Blake2Hasher, RlpCodec> + Send + Sync; - /// Extrinsic pool type. - type ExtrinsicPoolApi: 'static + extrinsic_pool::ChainApi::ExtrinsicHash, Block=FactoryBlock>; - - /// Create client. - fn build_client( - config: &FactoryFullConfiguration, - executor: CodeExecutor, - ) - -> Result<( - Arc>, - Option, NetworkService>>> - ), error::Error>; - - /// Create extrinsic pool. - fn build_extrinsic_pool(config: ExtrinsicPoolOptions, client: Arc>) - -> Result, error::Error>; -} - -/// A struct that implement `Components` for the full client. -pub struct FullComponents { - _factory: PhantomData, -} - -impl Components for FullComponents { - type Factory = Factory; - type Executor = FullExecutor; - type Backend = FullBackend; - type ExtrinsicPoolApi = ::FullExtrinsicPoolApi; - - fn build_client( - config: &FactoryFullConfiguration, - executor: CodeExecutor, - ) - -> Result<( - Arc>, - Option, NetworkService>>> - ), error::Error> - { - let db_settings = client_db::DatabaseSettings { - cache_size: None, - path: config.database_path.as_str().into(), - pruning: config.pruning.clone(), - }; - Ok((Arc::new(client_db::new_client(db_settings, executor, &config.chain_spec, config.execution_strategy)?), None)) - } - - fn build_extrinsic_pool(config: ExtrinsicPoolOptions, client: Arc>) - -> Result, error::Error> - { - Factory::build_full_extrinsic_pool(config, client) - } -} - -/// A struct that implement `Components` for the light client. -pub struct LightComponents { - _factory: PhantomData, -} - -impl Components for LightComponents - where - <::Block as BlockT>::Hash: From, - H256: From<<::Block as BlockT>::Hash>, -{ - type Factory = Factory; - type Executor = LightExecutor; - type Backend = LightBackend; - type ExtrinsicPoolApi = ::LightExtrinsicPoolApi; - - fn build_client( - config: &FactoryFullConfiguration, - executor: CodeExecutor, - ) - -> Result<( - Arc>, - Option, - NetworkService>>> - ), error::Error> - { - let db_settings = client_db::DatabaseSettings { - cache_size: None, - path: config.database_path.as_str().into(), - pruning: config.pruning.clone(), - }; - let db_storage = client_db::light::LightStorage::new(db_settings)?; - let light_blockchain = client::light::new_light_blockchain(db_storage); - let fetch_checker = Arc::new(client::light::new_fetch_checker::<_, Blake2Hasher, RlpCodec>(executor)); - let fetcher = Arc::new(network::OnDemand::new(fetch_checker)); - let client_backend = client::light::new_light_backend(light_blockchain, fetcher.clone()); - let client = client::light::new_light(client_backend, fetcher.clone(), &config.chain_spec)?; - Ok((Arc::new(client), Some(fetcher))) - } - - fn build_extrinsic_pool(config: ExtrinsicPoolOptions, client: Arc>) - -> Result, error::Error> - { - Factory::build_light_extrinsic_pool(config, client) - } -} diff --git a/substrate/service/src/config.rs b/substrate/service/src/config.rs deleted file mode 100644 index fd0db61f8dd9a..0000000000000 --- a/substrate/service/src/config.rs +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2017 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 . - -//! Service configuration. - -use std::net::SocketAddr; -use extrinsic_pool; -use chain_spec::ChainSpec; -pub use client::ExecutionStrategy; -pub use network::Roles; -pub use network::NetworkConfiguration; -pub use client_db::PruningMode; -use runtime_primitives::BuildStorage; -use serde::{Serialize, de::DeserializeOwned}; -use target_info::Target; - -/// Service configuration. -pub struct Configuration { - /// Implementation name - pub impl_name: &'static str, - /// Implementation version - pub impl_version: &'static str, - /// Git commit if any. - pub impl_commit: &'static str, - /// Node roles. - pub roles: Roles, - /// Extrinsic pool configuration. - pub extrinsic_pool: extrinsic_pool::Options, - /// Network configuration. - pub network: NetworkConfiguration, - /// Path to key files. - pub keystore_path: String, - /// Path to the database. - pub database_path: String, - /// Pruning settings. - pub pruning: PruningMode, - /// Additional key seeds. - pub keys: Vec, - /// Chain configuration. - pub chain_spec: ChainSpec, - /// Custom configuration. - pub custom: C, - /// Telemetry server URL, optional - only `Some` if telemetry reporting is enabled - pub telemetry: Option, - /// Node name. - pub name: String, - /// Execution strategy. - pub execution_strategy: ExecutionStrategy, - /// RPC over HTTP binding address. `None` if disabled. - pub rpc_http: Option, - /// RPC over Websockets binding address. `None` if disabled. - pub rpc_ws: Option, - /// Telemetry service URL. `None` if disabled. - pub telemetry_url: Option, -} - -impl Configuration { - /// Create default config for given chain spec. - pub fn default_with_spec(chain_spec: ChainSpec) -> Self { - let mut configuration = Configuration { - impl_name: "parity-substrate", - impl_version: "0.0.0", - impl_commit: "", - chain_spec, - name: Default::default(), - roles: Roles::FULL, - extrinsic_pool: Default::default(), - network: Default::default(), - keystore_path: Default::default(), - database_path: Default::default(), - keys: Default::default(), - custom: Default::default(), - telemetry: Default::default(), - pruning: PruningMode::default(), - execution_strategy: ExecutionStrategy::Both, - rpc_http: None, - rpc_ws: None, - telemetry_url: None, - }; - configuration.network.boot_nodes = configuration.chain_spec.boot_nodes().to_vec(); - configuration.telemetry_url = configuration.chain_spec.telemetry_url().map(str::to_owned); - configuration - } - - /// Returns full version string of this configuration. - pub fn full_version(&self) -> String { - full_version_from_strs(self.impl_version, self.impl_commit) - } - - /// Implementation id and version. - pub fn client_id(&self) -> String { - format!("{}/v{}", self.impl_name, self.full_version()) - } -} - -/// Returns platform info -pub fn platform() -> String { - let env = Target::env(); - let env_dash = if env.is_empty() { "" } else { "-" }; - format!("{}-{}{}{}", Target::arch(), Target::os(), env_dash, env) -} - -/// Returns full version string, using supplied version and commit. -pub fn full_version_from_strs(impl_version: &str, impl_commit: &str) -> String { - let commit_dash = if impl_commit.is_empty() { "" } else { "-" }; - format!("{}{}{}-{}", impl_version, commit_dash, impl_commit, platform()) -} - diff --git a/substrate/service/src/error.rs b/substrate/service/src/error.rs deleted file mode 100644 index abc09cb0c3c45..0000000000000 --- a/substrate/service/src/error.rs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2017 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 . - -//! Errors that can occur during the service operation. - -use client; -use network; -use keystore; - -error_chain! { - foreign_links { - Io(::std::io::Error) #[doc="IO error"]; - } - links { - Client(client::error::Error, client::error::ErrorKind) #[doc="Client error"]; - Network(network::error::Error, network::error::ErrorKind) #[doc="Network error"]; - Keystore(keystore::Error, keystore::ErrorKind) #[doc="Keystore error"]; - } - - errors { - } -} diff --git a/substrate/service/src/lib.rs b/substrate/service/src/lib.rs deleted file mode 100644 index d7c1e90e599d9..0000000000000 --- a/substrate/service/src/lib.rs +++ /dev/null @@ -1,424 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -// tag::description[] -//! Substrate service. Starts a thread that spins the network, the client and the extrinsic pool. -//! Manages communication between them. -// end::description[] - -#![warn(unused_extern_crates)] - -extern crate futures; -extern crate exit_future; -extern crate serde; -extern crate serde_json; -extern crate substrate_keystore as keystore; -extern crate substrate_primitives as primitives; -extern crate substrate_runtime_primitives as runtime_primitives; -extern crate substrate_network as network; -extern crate substrate_executor; -extern crate substrate_client as client; -extern crate substrate_client_db as client_db; -extern crate substrate_codec as codec; -extern crate substrate_extrinsic_pool as extrinsic_pool; -extern crate substrate_rpc; -extern crate substrate_rpc_servers as rpc; -extern crate target_info; -extern crate tokio; - -#[macro_use] -extern crate substrate_telemetry as tel; -#[macro_use] -extern crate error_chain; -#[macro_use] -extern crate slog; // needed until we can reexport `slog_info` from `substrate_telemetry` -#[macro_use] -extern crate log; -#[macro_use] -extern crate serde_derive; - -mod components; -mod error; -mod chain_spec; -pub mod config; -pub mod chain_ops; - -use std::io; -use std::net::SocketAddr; -use std::sync::Arc; -use std::collections::HashMap; -use futures::prelude::*; -use keystore::Store as Keystore; -use client::BlockchainEvents; -use runtime_primitives::traits::{Header, As}; -use runtime_primitives::generic::BlockId; -use exit_future::Signal; -use tokio::runtime::TaskExecutor; -use substrate_executor::NativeExecutor; -use codec::{Encode, Decode}; - -pub use self::error::{ErrorKind, Error}; -pub use config::{Configuration, Roles, PruningMode}; -pub use chain_spec::ChainSpec; -pub use extrinsic_pool::{Pool as ExtrinsicPool, Options as ExtrinsicPoolOptions, ChainApi, VerifiedTransaction, IntoPoolError}; -pub use client::ExecutionStrategy; - -pub use components::{ServiceFactory, FullBackend, FullExecutor, LightBackend, - LightExecutor, Components, PoolApi, ComponentClient, - ComponentBlock, FullClient, LightClient, FullComponents, LightComponents, - CodeExecutor, NetworkService, FactoryChainSpec, FactoryBlock, - FactoryFullConfiguration, RuntimeGenesis, FactoryGenesis, - ComponentExHash, ComponentExtrinsic, -}; - -/// Substrate service. -pub struct Service { - client: Arc>, - network: Option>>, - extrinsic_pool: Arc>, - keystore: Keystore, - exit: ::exit_future::Exit, - signal: Option, - _rpc_http: Option, - _rpc_ws: Option, - _telemetry: Option, -} - -/// Creates bare client without any networking. -pub fn new_client(config: FactoryFullConfiguration) - -> Result>>, error::Error> -{ - let executor = NativeExecutor::new(); - let (client, _) = components::FullComponents::::build_client( - &config, - executor, - )?; - Ok(client) -} - -impl Service - where - Components: components::Components, -{ - /// Creates a new service. - pub fn new( - config: FactoryFullConfiguration, - task_executor: TaskExecutor, - ) - -> Result - { - let (signal, exit) = ::exit_future::signal(); - - // Create client - let executor = NativeExecutor::new(); - - let mut keystore = Keystore::open(config.keystore_path.as_str().into())?; - 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("")?; - let public_key = key.public(); - info!("Generated a new keypair: {:?}", public_key); - - public_key - } - }; - - let (client, on_demand) = Components::build_client(&config, executor)?; - let best_header = client.best_block_header()?; - - let version = config.full_version(); - info!("Best block: #{}", best_header.number()); - telemetry!("node.start"; "height" => best_header.number().as_(), "best" => ?best_header.hash()); - - let network_protocol = ::build_network_protocol(&config)?; - let extrinsic_pool = Arc::new( - Components::build_extrinsic_pool(config.extrinsic_pool, client.clone())? - ); - let extrinsic_pool_adapter = ExtrinsicPoolAdapter:: { - imports_external_transactions: !config.roles == Roles::LIGHT, - pool: extrinsic_pool.clone(), - client: client.clone(), - }; - - let network_params = network::Params { - config: network::ProtocolConfig { - roles: config.roles, - }, - network_config: config.network, - chain: client.clone(), - on_demand: on_demand.clone() - .map(|d| d as Arc>>), - transaction_pool: Arc::new(extrinsic_pool_adapter), - specialization: network_protocol, - }; - - let network = network::Service::new(network_params, Components::Factory::NETWORK_PROTOCOL_ID)?; - on_demand.map(|on_demand| on_demand.set_service_link(Arc::downgrade(&network))); - - { - // block notifications - let network = network.clone(); - let txpool = extrinsic_pool.clone(); - - let events = client.import_notification_stream() - .for_each(move |notification| { - network.on_block_imported(notification.hash, ¬ification.header); - txpool.cull(&BlockId::hash(notification.hash)) - .map_err(|e| warn!("Error removing extrinsics: {:?}", e))?; - Ok(()) - }) - .select(exit.clone()) - .then(|_| Ok(())); - task_executor.spawn(events); - } - - { - // extrinsic notifications - let network = network.clone(); - let events = extrinsic_pool.import_notification_stream() - // TODO [ToDr] Consider throttling? - .for_each(move |_| { - network.trigger_repropagate(); - Ok(()) - }) - .select(exit.clone()) - .then(|_| Ok(())); - - task_executor.spawn(events); - } - - // RPC - let rpc_config = RpcConfig { - chain_name: config.chain_spec.name().to_string(), - impl_name: config.impl_name, - impl_version: config.impl_version, - }; - - let (rpc_http, rpc_ws) = { - let handler = || { - let client = client.clone(); - let chain = rpc::apis::chain::Chain::new(client.clone(), task_executor.clone()); - let state = rpc::apis::state::State::new(client.clone(), task_executor.clone()); - let author = rpc::apis::author::Author::new(client.clone(), extrinsic_pool.clone(), task_executor.clone()); - rpc::rpc_handler::, ComponentExHash, _, _, _, _, _>( - state, - chain, - author, - rpc_config.clone(), - ) - }; - ( - maybe_start_server(config.rpc_http, |address| rpc::start_http(address, handler()))?, - maybe_start_server(config.rpc_ws, |address| rpc::start_ws(address, handler()))?, - ) - }; - - // Telemetry - let telemetry = match config.telemetry_url { - Some(url) => { - let is_authority = config.roles == Roles::AUTHORITY; - let pubkey = format!("{}", public_key); - let name = config.name.clone(); - let impl_name = config.impl_name.to_owned(); - let version = version.clone(); - let chain_name = config.chain_spec.name().to_owned(); - Some(tel::init_telemetry(tel::TelemetryConfig { - url: url, - on_connect: Box::new(move || { - telemetry!("system.connected"; - "name" => name.clone(), - "implementation" => impl_name.clone(), - "version" => version.clone(), - "config" => "", - "chain" => chain_name.clone(), - "pubkey" => &pubkey, - "authority" => is_authority - ); - }), - })) - }, - None => None, - }; - - Ok(Service { - client: client, - network: Some(network), - extrinsic_pool: extrinsic_pool, - signal: Some(signal), - keystore: keystore, - exit, - _rpc_http: rpc_http, - _rpc_ws: rpc_ws, - _telemetry: telemetry, - }) - } - - /// Get shared client instance. - pub fn client(&self) -> Arc> { - self.client.clone() - } - - /// Get shared network instance. - pub fn network(&self) -> Arc> { - self.network.as_ref().expect("self.network always Some").clone() - } - - /// Get shared extrinsic pool instance. - pub fn extrinsic_pool(&self) -> Arc> { - self.extrinsic_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 Drop for Service where Components: components::Components { - fn drop(&mut self) { - debug!(target: "service", "Substrate service shutdown"); - - drop(self.network.take()); - - if let Some(signal) = self.signal.take() { - signal.fire(); - } - } -} - -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, - }) -} - -#[derive(Clone)] -struct RpcConfig { - chain_name: String, - impl_name: &'static str, - impl_version: &'static str, -} - -impl substrate_rpc::system::SystemApi for RpcConfig { - fn system_name(&self) -> substrate_rpc::system::error::Result { - Ok(self.impl_name.into()) - } - - fn system_version(&self) -> substrate_rpc::system::error::Result { - Ok(self.impl_version.into()) - } - - fn system_chain(&self) -> substrate_rpc::system::error::Result { - Ok(self.chain_name.clone()) - } -} - -/// Transaction pool adapter. -pub struct ExtrinsicPoolAdapter { - imports_external_transactions: bool, - pool: Arc>, - client: Arc>, -} - -impl ExtrinsicPoolAdapter { - fn best_block_id(&self) -> Option>> { - self.client.info() - .map(|info| BlockId::hash(info.chain.best_hash)) - .map_err(|e| { - debug!("Error getting best block: {:?}", e); - }) - .ok() - } -} - -impl network::TransactionPool, ComponentBlock> for ExtrinsicPoolAdapter { - fn transactions(&self) -> Vec<(ComponentExHash, ComponentExtrinsic)> { - let best_block_id = match self.best_block_id() { - Some(id) => id, - None => return vec![], - }; - self.pool.cull_and_get_pending(&best_block_id, |pending| pending - .map(|t| { - let hash = t.hash().clone(); - let ex: ComponentExtrinsic = t.original.clone(); - (hash, ex) - }) - .collect() - ).unwrap_or_else(|e| { - warn!("Error retrieving pending set: {}", e); - vec![] - }) - } - - fn import(&self, transaction: &ComponentExtrinsic) -> Option> { - if !self.imports_external_transactions { - return None; - } - - let encoded = transaction.encode(); - if let Some(uxt) = Decode::decode(&mut &encoded[..]) { - let best_block_id = self.best_block_id()?; - match self.pool.submit_one(&best_block_id, uxt) { - Ok(xt) => Some(*xt.hash()), - Err(e) => match e.into_pool_error() { - Ok(e) => match e.kind() { - extrinsic_pool::ErrorKind::AlreadyImported(hash) => - Some(::std::str::FromStr::from_str(&hash).map_err(|_| {}) - .expect("Hash string is always valid")), - _ => { - debug!("Error adding transaction to the pool: {:?}", e); - None - }, - }, - Err(e) => { - debug!("Error converting pool error: {:?}", e); - None - } - } - } - } else { - debug!("Error decoding transaction"); - None - } - } - - fn on_broadcasted(&self, propagations: HashMap, Vec>) { - self.pool.on_broadcasted(propagations) - } -} diff --git a/substrate/state-db/Cargo.toml b/substrate/state-db/Cargo.toml deleted file mode 100644 index d3e4c878cf50b..0000000000000 --- a/substrate/state-db/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "substrate-state-db" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -parking_lot = "0.5" -log = "0.4" -substrate-primitives = { path = "../../substrate/primitives" } -substrate-codec = { path = "../../substrate/codec" } -substrate-codec-derive = { path = "../../substrate/codec/derive" } - -[dev-dependencies] -env_logger = "0.4" diff --git a/substrate/state-db/README.adoc b/substrate/state-db/README.adoc deleted file mode 100644 index f9934ed8d9ac6..0000000000000 --- a/substrate/state-db/README.adoc +++ /dev/null @@ -1,13 +0,0 @@ - -= State DB - -.Summary -[source, toml] ----- -include::Cargo.toml[lines=2..5] ----- - -.Description ----- -include::src/lib.rs[tag=description] ----- diff --git a/substrate/state-db/src/lib.rs b/substrate/state-db/src/lib.rs deleted file mode 100644 index 7e7dd3369c918..0000000000000 --- a/substrate/state-db/src/lib.rs +++ /dev/null @@ -1,407 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -// tag::description[] -//! State database maintenance. Handles finalization and pruning in the database. The input to -//! this module is a `ChangeSet` which is basically a list of key-value pairs (trie nodes) that -//! were added or deleted during block execution. -//! -//! # Finalization. -//! Finalization window tracks a tree of blocks identified by header hash. The in-memory -//! overlay allows to get any node that was was inserted in any any of the blocks within the window. -//! The tree is journaled to the backing database and rebuilt on startup. -//! Finalization function select one root from the top of the tree and discards all other roots and -//! their subtrees. -//! -//! # Pruning. -//! See `RefWindow` for pruning algorithm details. `StateDb` prunes on each finalization until pruning -//! constraints are satisfied. -//! -// end::description[] - -#[macro_use] extern crate log; -#[macro_use] extern crate substrate_codec_derive; -extern crate parking_lot; -extern crate substrate_codec as codec; -extern crate substrate_primitives as primitives; - -mod unfinalized; -mod pruning; -#[cfg(test)] mod test; - -use std::fmt; -use parking_lot::RwLock; -use codec::Codec; -use std::collections::HashSet; -use unfinalized::UnfinalizedOverlay; -use pruning::RefWindow; - -/// Database value type. -pub type DBValue = Vec; - -/// Basic set of requirements for the Block hash and node key types. -pub trait Hash: Send + Sync + Sized + Eq + PartialEq + Clone + Default + fmt::Debug + Codec + std::hash::Hash + 'static {} -impl Hash for T {} - -/// Backend database trait. Read-only. -pub trait MetaDb { - type Error: fmt::Debug; - - /// Get meta value, such as the journal. - fn get_meta(&self, key: &[u8]) -> Result, Self::Error>; -} - - -/// Backend database trait. Read-only. -pub trait HashDb { - type Hash: Hash; - type Error: fmt::Debug; - - /// Get state trie node. - fn get(&self, key: &Self::Hash) -> Result, Self::Error>; -} - -/// Error type. -pub enum Error { - /// Database backend error. - Db(E), - /// `Codec` decoding error. - Decoding, -} - -impl fmt::Debug for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - match self { - Error::Db(e) => e.fmt(f), - Error::Decoding => write!(f, "Error decoding slicable value"), - } - } -} - -/// A set of state node changes. -#[derive(Default, Debug, Clone)] -pub struct ChangeSet { - /// Inserted nodes. - pub inserted: Vec<(H, DBValue)>, - /// Deleted nodes. - pub deleted: Vec, -} - - -/// A set of changes to the backing database. -#[derive(Default, Debug, Clone)] -pub struct CommitSet { - /// State node changes. - pub data: ChangeSet, - /// Metadata changes. - pub meta: ChangeSet>, -} - -/// Pruning constraints. If none are specified pruning is -#[derive(Default, Debug, Clone)] -pub struct Constraints { - /// Maximum blocks. Defaults to 0 when unspecified, effectively keeping only unfinalized states. - pub max_blocks: Option, - /// Maximum memory in the pruning overlay. - pub max_mem: Option, -} - -/// Pruning mode. -#[derive(Debug, Clone)] -pub enum PruningMode { - /// Maintain a pruning window. - Constrained(Constraints), - /// No pruning. Finalization is a no-op. - ArchiveAll, - /// Finalization discards unfinalized nodes. All the finalized nodes are kept in the DB. - ArchiveCanonical, -} - -impl PruningMode { - /// Create a mode that keeps given number of blocks. - pub fn keep_blocks(n: u32) -> PruningMode { - PruningMode::Constrained(Constraints { - max_blocks: Some(n), - max_mem: None, - }) - } -} - -impl Default for PruningMode { - fn default() -> Self { - PruningMode::keep_blocks(256) - } -} - -fn to_meta_key(suffix: &[u8], data: &S) -> Vec { - let mut buffer = data.encode(); - buffer.extend(suffix); - buffer -} - -struct StateDbSync { - mode: PruningMode, - unfinalized: UnfinalizedOverlay, - pruning: Option>, - pinned: HashSet, -} - -impl StateDbSync { - pub fn new(mode: PruningMode, db: &D) -> Result, Error> { - trace!("StateDb settings: {:?}", mode); - let unfinalized: UnfinalizedOverlay = UnfinalizedOverlay::new(db)?; - let pruning: Option> = match mode { - PruningMode::Constrained(Constraints { - max_mem: Some(_), - .. - }) => unimplemented!(), - PruningMode::Constrained(_) => Some(RefWindow::new(db)?), - PruningMode::ArchiveAll | PruningMode::ArchiveCanonical => None, - }; - Ok(StateDbSync { - mode, - unfinalized, - pruning: pruning, - pinned: Default::default(), - }) - } - - pub fn insert_block(&mut self, hash: &BlockHash, number: u64, parent_hash: &BlockHash, mut changeset: ChangeSet) -> CommitSet { - if number == 0 { - return CommitSet { - data: changeset, - meta: Default::default(), - } - } - match self.mode { - PruningMode::ArchiveAll => { - changeset.deleted.clear(); - // write changes immediately - CommitSet { - data: changeset, - meta: Default::default(), - } - }, - PruningMode::Constrained(_) | PruningMode::ArchiveCanonical => { - self.unfinalized.insert(hash, number, parent_hash, changeset) - } - } - } - - pub fn finalize_block(&mut self, hash: &BlockHash) -> CommitSet { - // clear the temporary overlay from the previous finalization. - self.unfinalized.clear_overlay(); - let mut commit = match self.mode { - PruningMode::ArchiveAll => { - CommitSet::default() - }, - PruningMode::ArchiveCanonical => { - let mut commit = self.unfinalized.finalize(hash); - commit.data.deleted.clear(); - commit - }, - PruningMode::Constrained(_) => { - self.unfinalized.finalize(hash) - }, - }; - if let Some(ref mut pruning) = self.pruning { - pruning.note_finalized(hash, &mut commit); - } - self.prune(&mut commit); - commit - } - - pub fn best_finalized(&self) -> u64 { - return self.unfinalized.last_finalized_block_number() - } - - pub fn is_pruned(&self, number: u64) -> bool { - self.pruning.as_ref().map_or(false, |pruning| number < pruning.pending()) - } - - fn prune(&mut self, commit: &mut CommitSet) { - if let (&mut Some(ref mut pruning), &PruningMode::Constrained(ref constraints)) = (&mut self.pruning, &self.mode) { - loop { - if pruning.window_size() <= constraints.max_blocks.unwrap_or(0) as u64 { - break; - } - - if constraints.max_mem.map_or(false, |m| pruning.mem_used() > m) { - break; - } - - let pinned = &self.pinned; - if pruning.next_hash().map_or(false, |h| pinned.contains(&h)) { - break; - } - pruning.prune_one(commit); - } - } - } - - /// Revert all unfinalized blocks with the best block number. - /// Returns a database commit or `None` if not possible. - /// For archive an empty commit set is returned. - pub fn revert_one(&mut self) -> Option> { - match self.mode { - PruningMode::ArchiveAll => { - Some(CommitSet::default()) - }, - PruningMode::ArchiveCanonical | PruningMode::Constrained(_) => { - self.unfinalized.revert_one() - }, - } - } - - pub fn pin(&mut self, hash: &BlockHash) { - self.pinned.insert(hash.clone()); - } - - pub fn unpin(&mut self, hash: &BlockHash) { - self.pinned.remove(hash); - } - - pub fn get>(&self, key: &Key, db: &D) -> Result, Error> { - if let Some(value) = self.unfinalized.get(key) { - return Ok(Some(value)); - } - db.get(key).map_err(|e| Error::Db(e)) - } -} - -/// State DB maintenance. See module description. -/// Can be shared across threads. -pub struct StateDb { - db: RwLock>, -} - -impl StateDb { - /// Creates a new instance. Does not expect any metadata in the database. - pub fn new(mode: PruningMode, db: &D) -> Result, Error> { - Ok(StateDb { - db: RwLock::new(StateDbSync::new(mode, db)?) - }) - } - - /// Add a new unfinalized block. - pub fn insert_block(&self, hash: &BlockHash, number: u64, parent_hash: &BlockHash, changeset: ChangeSet) -> CommitSet { - self.db.write().insert_block(hash, number, parent_hash, changeset) - } - - /// Finalize a previously inserted block. - pub fn finalize_block(&self, hash: &BlockHash) -> CommitSet { - self.db.write().finalize_block(hash) - } - - /// Prevents pruning of specified block and its descendants. - pub fn pin(&self, hash: &BlockHash) { - self.db.write().pin(hash) - } - - /// Allows pruning of specified block. - pub fn unpin(&self, hash: &BlockHash) { - self.db.write().unpin(hash) - } - - /// Get a value from unfinalized/pruning overlay or the backing DB. - pub fn get>(&self, key: &Key, db: &D) -> Result, Error> { - self.db.read().get(key, db) - } - - /// Revert all unfinalized blocks with the best block number. - /// Returns a database commit or `None` if not possible. - /// For archive an empty commit set is returned. - pub fn revert_one(&self) -> Option> { - self.db.write().revert_one() - } - - /// Returns last finalized block number. - pub fn best_finalized(&self) -> u64 { - return self.db.read().best_finalized() - } - - /// Check if block is pruned away. - pub fn is_pruned(&self, number: u64) -> bool { - return self.db.read().is_pruned(number) - } -} - -#[cfg(test)] -mod tests { - use primitives::H256; - use {StateDb, PruningMode, Constraints}; - use test::{make_db, make_changeset, TestDb}; - - fn make_test_db(settings: PruningMode) -> (TestDb, StateDb) { - let mut db = make_db(&[91, 921, 922, 93, 94]); - let state_db = StateDb::new(settings, &db).unwrap(); - - db.commit(&state_db.insert_block(&H256::from(1), 1, &H256::from(0), make_changeset(&[1], &[91]))); - db.commit(&state_db.insert_block(&H256::from(21), 2, &H256::from(1), make_changeset(&[21], &[921, 1]))); - db.commit(&state_db.insert_block(&H256::from(22), 2, &H256::from(1), make_changeset(&[22], &[922]))); - db.commit(&state_db.insert_block(&H256::from(3), 3, &H256::from(21), make_changeset(&[3], &[93]))); - db.commit(&state_db.finalize_block(&H256::from(1))); - db.commit(&state_db.insert_block(&H256::from(4), 4, &H256::from(3), make_changeset(&[4], &[94]))); - db.commit(&state_db.finalize_block(&H256::from(21))); - db.commit(&state_db.finalize_block(&H256::from(3))); - - (db, state_db) - } - - #[test] - fn full_archive_keeps_everything() { - let (db, _) = make_test_db(PruningMode::ArchiveAll); - assert!(db.data_eq(&make_db(&[1, 21, 22, 3, 4, 91, 921, 922, 93, 94]))); - } - - #[test] - fn canonical_archive_keeps_canonical() { - let (db, _) = make_test_db(PruningMode::ArchiveCanonical); - assert!(db.data_eq(&make_db(&[1, 21, 3, 91, 921, 922, 93, 94]))); - } - - #[test] - fn prune_window_0() { - let (db, _) = make_test_db(PruningMode::Constrained(Constraints { - max_blocks: Some(0), - max_mem: None, - })); - assert!(db.data_eq(&make_db(&[21, 3, 922, 94]))); - } - - #[test] - fn prune_window_1() { - let (db, sdb) = make_test_db(PruningMode::Constrained(Constraints { - max_blocks: Some(1), - max_mem: None, - })); - assert!(sdb.is_pruned(0)); - assert!(sdb.is_pruned(1)); - assert!(!sdb.is_pruned(2)); - assert!(db.data_eq(&make_db(&[21, 3, 922, 93, 94]))); - } - - #[test] - fn prune_window_2() { - let (db, sdb) = make_test_db(PruningMode::Constrained(Constraints { - max_blocks: Some(2), - max_mem: None, - })); - assert!(sdb.is_pruned(0)); - assert!(!sdb.is_pruned(1)); - assert!(db.data_eq(&make_db(&[1, 21, 3, 921, 922, 93, 94]))); - } -} diff --git a/substrate/state-db/src/pruning.rs b/substrate/state-db/src/pruning.rs deleted file mode 100644 index aaacbd09b66ad..0000000000000 --- a/substrate/state-db/src/pruning.rs +++ /dev/null @@ -1,267 +0,0 @@ -// Copyright 2017 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 . - -//! Pruning window. -//! -//! For each block we maintain a list of nodes pending deletion. -//! There is also a global index of node key to block number. -//! If a node is re-inserted into the window it gets removed from -//! the death list. -//! The changes are journaled in the DB. - -use std::collections::{HashMap, HashSet, VecDeque}; -use codec::{Encode, Decode}; -use {CommitSet, Error, MetaDb, to_meta_key, Hash}; - -const LAST_PRUNED: &[u8] = b"last_pruned"; -const PRUNING_JOURNAL: &[u8] = b"pruning_journal"; - -/// See module documentation. -pub struct RefWindow { - death_rows: VecDeque>, - death_index: HashMap, - pending_number: u64, -} - -#[derive(Debug, PartialEq, Eq)] -struct DeathRow { - hash: BlockHash, - journal_key: Vec, - deleted: HashSet, -} - -#[derive(Encode, Decode)] -struct JournalRecord { - hash: BlockHash, - inserted: Vec, - deleted: Vec, -} - -fn to_journal_key(block: u64) -> Vec { - to_meta_key(PRUNING_JOURNAL, &block) -} - -impl RefWindow { - pub fn new(db: &D) -> Result, Error> { - let last_pruned = db.get_meta(&to_meta_key(LAST_PRUNED, &())) - .map_err(|e| Error::Db(e))?; - let pending_number: u64 = match last_pruned { - Some(buffer) => u64::decode(&mut buffer.as_slice()).ok_or(Error::Decoding)? + 1, - None => 0, - }; - let mut block = pending_number; - let mut pruning = RefWindow { - death_rows: Default::default(), - death_index: Default::default(), - pending_number: pending_number, - }; - // read the journal - trace!(target: "state-db", "Reading pruning journal. Pending #{}", pending_number); - loop { - let journal_key = to_journal_key(block); - match db.get_meta(&journal_key).map_err(|e| Error::Db(e))? { - Some(record) => { - let record: JournalRecord = Decode::decode(&mut record.as_slice()).ok_or(Error::Decoding)?; - trace!(target: "state-db", "Pruning journal entry {} ({} inserted, {} deleted)", block, record.inserted.len(), record.deleted.len()); - pruning.import(&record.hash, journal_key, record.inserted.into_iter(), record.deleted); - }, - None => break, - } - block += 1; - } - Ok(pruning) - } - - fn import>(&mut self, hash: &BlockHash, journal_key: Vec, inserted: I, deleted: Vec) { - // remove all re-inserted keys from death rows - for k in inserted { - if let Some(block) = self.death_index.remove(&k) { - self.death_rows[(block - self.pending_number) as usize].deleted.remove(&k); - } - } - - // add new keys - let imported_block = self.pending_number + self.death_rows.len() as u64; - for k in deleted.iter() { - self.death_index.insert(k.clone(), imported_block); - } - self.death_rows.push_back( - DeathRow { - hash: hash.clone(), - deleted: deleted.into_iter().collect(), - journal_key: journal_key, - } - ); - } - - pub fn window_size(&self) -> u64 { - self.death_rows.len() as u64 - } - - pub fn next_hash(&self) -> Option { - self.death_rows.front().map(|r| r.hash.clone()) - } - - pub fn mem_used(&self) -> usize { - 0 - } - - pub fn pending(&self) -> u64 { - self.pending_number - } - - /// Prune next block. Expects at least one block in the window. Adds changes to `commit`. - pub fn prune_one(&mut self, commit: &mut CommitSet) { - let pruned = self.death_rows.pop_front().expect("prune_one is only called with a non-empty window"); - trace!(target: "state-db", "Pruning {:?} ({} deleted)", pruned.hash, pruned.deleted.len()); - for k in pruned.deleted.iter() { - self.death_index.remove(&k); - } - commit.data.deleted.extend(pruned.deleted.into_iter()); - commit.meta.inserted.push((to_meta_key(LAST_PRUNED, &()), self.pending_number.encode())); - commit.meta.deleted.push(pruned.journal_key); - self.pending_number += 1; - } - - /// Add a change set to the window. Creates a journal record and pushes it to `commit` - pub fn note_finalized(&mut self, hash: &BlockHash, commit: &mut CommitSet) { - trace!(target: "state-db", "Adding to pruning window: {:?} ({} inserted, {} deleted)", hash, commit.data.inserted.len(), commit.data.deleted.len()); - let inserted = commit.data.inserted.iter().map(|(k, _)| k.clone()).collect(); - let deleted = ::std::mem::replace(&mut commit.data.deleted, Vec::new()); - let journal_record = JournalRecord { - hash: hash.clone(), - inserted, - deleted, - }; - let block = self.pending_number + self.window_size(); - let journal_key = to_journal_key(block); - commit.meta.inserted.push((journal_key.clone(), journal_record.encode())); - - self.import(hash, journal_key, journal_record.inserted.into_iter(), journal_record.deleted); - } -} - -#[cfg(test)] -mod tests { - use super::RefWindow; - use primitives::H256; - use {CommitSet}; - use test::{make_db, make_commit, TestDb}; - - fn check_journal(pruning: &RefWindow, db: &TestDb) { - let restored: RefWindow = RefWindow::new(db).unwrap(); - assert_eq!(pruning.pending_number, restored.pending_number); - assert_eq!(pruning.death_rows, restored.death_rows); - assert_eq!(pruning.death_index, restored.death_index); - } - - #[test] - fn created_from_empty_db() { - let db = make_db(&[]); - let pruning: RefWindow = RefWindow::new(&db).unwrap(); - assert_eq!(pruning.pending_number, 0); - assert!(pruning.death_rows.is_empty()); - assert!(pruning.death_index.is_empty()); - } - - #[test] - #[should_panic] - fn prune_empty_panics() { - let db = make_db(&[]); - let mut pruning: RefWindow = RefWindow::new(&db).unwrap(); - let mut commit = CommitSet::default(); - pruning.prune_one(&mut commit); - } - - #[test] - fn prune_one() { - let mut db = make_db(&[1, 2, 3]); - let mut pruning: RefWindow = RefWindow::new(&db).unwrap(); - let mut commit = make_commit(&[4, 5], &[1, 3]); - let h = H256::random(); - pruning.note_finalized(&h, &mut commit); - db.commit(&commit); - assert!(commit.data.deleted.is_empty()); - assert_eq!(pruning.death_rows.len(), 1); - assert_eq!(pruning.death_index.len(), 2); - assert!(db.data_eq(&make_db(&[1, 2, 3, 4, 5]))); - check_journal(&pruning, &db); - - let mut commit = CommitSet::default(); - pruning.prune_one(&mut commit); - db.commit(&commit); - assert!(db.data_eq(&make_db(&[2, 4, 5]))); - assert!(pruning.death_rows.is_empty()); - assert!(pruning.death_index.is_empty()); - assert_eq!(pruning.pending_number, 1); - } - - #[test] - fn prune_two() { - let mut db = make_db(&[1, 2, 3]); - let mut pruning: RefWindow = RefWindow::new(&db).unwrap(); - let mut commit = make_commit(&[4], &[1]); - pruning.note_finalized(&H256::random(), &mut commit); - db.commit(&commit); - let mut commit = make_commit(&[5], &[2]); - pruning.note_finalized(&H256::random(), &mut commit); - db.commit(&commit); - assert!(db.data_eq(&make_db(&[1, 2, 3, 4, 5]))); - - check_journal(&pruning, &db); - - let mut commit = CommitSet::default(); - pruning.prune_one(&mut commit); - db.commit(&commit); - assert!(db.data_eq(&make_db(&[2, 3, 4, 5]))); - let mut commit = CommitSet::default(); - pruning.prune_one(&mut commit); - db.commit(&commit); - assert!(db.data_eq(&make_db(&[3, 4, 5]))); - assert_eq!(pruning.pending_number, 2); - } - - #[test] - fn reinserted_survives() { - let mut db = make_db(&[1, 2, 3]); - let mut pruning: RefWindow = RefWindow::new(&db).unwrap(); - let mut commit = make_commit(&[], &[2]); - pruning.note_finalized(&H256::random(), &mut commit); - db.commit(&commit); - let mut commit = make_commit(&[2], &[]); - pruning.note_finalized(&H256::random(), &mut commit); - db.commit(&commit); - let mut commit = make_commit(&[], &[2]); - pruning.note_finalized(&H256::random(), &mut commit); - db.commit(&commit); - assert!(db.data_eq(&make_db(&[1, 2, 3]))); - - check_journal(&pruning, &db); - - let mut commit = CommitSet::default(); - pruning.prune_one(&mut commit); - db.commit(&commit); - assert!(db.data_eq(&make_db(&[1, 2, 3]))); - let mut commit = CommitSet::default(); - pruning.prune_one(&mut commit); - db.commit(&commit); - assert!(db.data_eq(&make_db(&[1, 2, 3]))); - pruning.prune_one(&mut commit); - db.commit(&commit); - assert!(db.data_eq(&make_db(&[1, 3]))); - assert_eq!(pruning.pending_number, 3); - } -} diff --git a/substrate/state-db/src/test.rs b/substrate/state-db/src/test.rs deleted file mode 100644 index 4931ed950698a..0000000000000 --- a/substrate/state-db/src/test.rs +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! Test utils - -use std::collections::HashMap; -use primitives::H256; -use {DBValue, ChangeSet, CommitSet, MetaDb, HashDb}; - -#[derive(Default, Debug, Clone, PartialEq, Eq)] -pub struct TestDb { - pub data: HashMap, - pub meta: HashMap, DBValue>, -} - -impl MetaDb for TestDb { - type Error = (); - - fn get_meta(&self, key: &[u8]) -> Result, ()> { - Ok(self.meta.get(key).cloned()) - } -} - -impl HashDb for TestDb { - type Error = (); - type Hash = H256; - - fn get(&self, key: &H256) -> Result, ()> { - Ok(self.data.get(key).cloned()) - } -} - -impl TestDb { - pub fn commit(&mut self, commit: &CommitSet) { - self.data.extend(commit.data.inserted.iter().cloned()); - for k in commit.data.deleted.iter() { - self.data.remove(k); - } - self.meta.extend(commit.meta.inserted.iter().cloned()); - for k in commit.meta.deleted.iter() { - self.meta.remove(k); - } - } - - pub fn data_eq(&self, other: &TestDb) -> bool { - self.data == other.data - } -} - -pub fn make_changeset(inserted: &[u64], deleted: &[u64]) -> ChangeSet { - ChangeSet { - inserted: inserted.iter().map(|v| (H256::from(*v), H256::from(*v).to_vec())).collect(), - deleted: deleted.iter().map(|v| H256::from(*v)).collect(), - } -} - -pub fn make_commit(inserted: &[u64], deleted: &[u64]) -> CommitSet { - CommitSet { - data: make_changeset(inserted, deleted), - meta: ChangeSet::default(), - } -} - -pub fn make_db(inserted: &[u64]) -> TestDb { - TestDb { - data: inserted.iter().map(|v| (H256::from(*v), H256::from(*v).to_vec())).collect(), - meta: Default::default(), - } -} - diff --git a/substrate/state-db/src/unfinalized.rs b/substrate/state-db/src/unfinalized.rs deleted file mode 100644 index 728c21ae4c172..0000000000000 --- a/substrate/state-db/src/unfinalized.rs +++ /dev/null @@ -1,533 +0,0 @@ -// Copyright 2017 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 . - -//! Finalization window. -//! Maintains trees of block overlays and allows discarding trees/roots -//! The overlays are added in `insert` and removed in `finalize`. -//! Last finalized overlay is kept in memory until next call to `finalize` or -//! `clear_overlay` - -use std::collections::{HashMap, VecDeque}; -use super::{Error, DBValue, ChangeSet, CommitSet, MetaDb, Hash, to_meta_key}; -use codec::{Decode, Encode}; - -const UNFINALIZED_JOURNAL: &[u8] = b"unfinalized_journal"; -const LAST_FINALIZED: &[u8] = b"last_finalized"; - -/// See module documentation. -pub struct UnfinalizedOverlay { - last_finalized: Option<(BlockHash, u64)>, - levels: VecDeque>>, - parents: HashMap, - last_finalized_overlay: HashMap, -} - -#[derive(Encode, Decode)] -struct JournalRecord { - hash: BlockHash, - parent_hash: BlockHash, - inserted: Vec<(Key, DBValue)>, - deleted: Vec, -} - -fn to_journal_key(block: u64, index: u64) -> Vec { - to_meta_key(UNFINALIZED_JOURNAL, &(block, index)) -} - -#[cfg_attr(test, derive(PartialEq, Debug))] -struct BlockOverlay { - hash: BlockHash, - journal_key: Vec, - values: HashMap, - deleted: Vec, -} - -impl UnfinalizedOverlay { - /// Creates a new instance. Does not expect any metadata to be present in the DB. - pub fn new(db: &D) -> Result, Error> { - let last_finalized = db.get_meta(&to_meta_key(LAST_FINALIZED, &())) - .map_err(|e| Error::Db(e))?; - let last_finalized = match last_finalized { - Some(buffer) => Some(<(BlockHash, u64)>::decode(&mut buffer.as_slice()).ok_or(Error::Decoding)?), - None => None, - }; - let mut levels = VecDeque::new(); - let mut parents = HashMap::new(); - if let Some((ref hash, mut block)) = last_finalized { - // read the journal - trace!(target: "state-db", "Reading unfinalized journal. Last finalized #{} ({:?})", block, hash); - let mut total: u64 = 0; - block += 1; - loop { - let mut index: u64 = 0; - let mut level = Vec::new(); - loop { - let journal_key = to_journal_key(block, index); - match db.get_meta(&journal_key).map_err(|e| Error::Db(e))? { - Some(record) => { - let record: JournalRecord = Decode::decode(&mut record.as_slice()).ok_or(Error::Decoding)?; - let overlay = BlockOverlay { - hash: record.hash.clone(), - journal_key, - values: record.inserted.into_iter().collect(), - deleted: record.deleted, - }; - trace!(target: "state-db", "Unfinalized journal entry {}.{} ({} inserted, {} deleted)", block, index, overlay.values.len(), overlay.deleted.len()); - level.push(overlay); - parents.insert(record.hash, record.parent_hash); - index += 1; - total += 1; - }, - None => break, - } - } - if level.is_empty() { - break; - } - levels.push_back(level); - block += 1; - } - trace!(target: "state-db", "Finished reading unfinalized journal, {} entries", total); - } - Ok(UnfinalizedOverlay { - last_finalized: last_finalized, - levels, - parents, - last_finalized_overlay: Default::default(), - }) - } - - /// Insert a new block into the overlay. If inserted on the second level or lover expects parent to be present in the window. - pub fn insert(&mut self, hash: &BlockHash, number: u64, parent_hash: &BlockHash, changeset: ChangeSet) -> CommitSet { - let mut commit = CommitSet::default(); - if self.levels.is_empty() && self.last_finalized.is_none() { - // assume that parent was finalized - let last_finalized = (parent_hash.clone(), number - 1); - commit.meta.inserted.push((to_meta_key(LAST_FINALIZED, &()), last_finalized.encode())); - self.last_finalized = Some(last_finalized); - } else if self.last_finalized.is_some() { - assert!(number >= self.front_block_number() && number < (self.front_block_number() + self.levels.len() as u64 + 1)); - // check for valid parent if inserting on second level or higher - if number == self.front_block_number() { - assert!(self.last_finalized.as_ref().map_or(false, |&(ref h, n)| h == parent_hash && n == number - 1)); - } else { - assert!(self.parents.contains_key(&parent_hash)); - } - } - let level = if self.levels.is_empty() || number == self.front_block_number() + self.levels.len() as u64 { - self.levels.push_back(Vec::new()); - self.levels.back_mut().expect("can't be empty after insertion; qed") - } else { - let front_block_number = self.front_block_number(); - self.levels.get_mut((number - front_block_number) as usize) - .expect("number is [front_block_number .. front_block_number + levels.len()) is asserted in precondition; qed") - }; - - let index = level.len() as u64; - let journal_key = to_journal_key(number, index); - - let overlay = BlockOverlay { - hash: hash.clone(), - journal_key: journal_key.clone(), - values: changeset.inserted.iter().cloned().collect(), - deleted: changeset.deleted.clone(), - }; - level.push(overlay); - self.parents.insert(hash.clone(), parent_hash.clone()); - let journal_record = JournalRecord { - hash: hash.clone(), - parent_hash: parent_hash.clone(), - inserted: changeset.inserted, - deleted: changeset.deleted, - }; - trace!(target: "state-db", "Inserted unfinalized changeset {}.{} ({} inserted, {} deleted)", number, index, journal_record.inserted.len(), journal_record.deleted.len()); - let journal_record = journal_record.encode(); - commit.meta.inserted.push((journal_key, journal_record)); - commit - } - - fn discard( - levels: &mut [Vec>], - parents: &mut HashMap, - discarded_journals: &mut Vec>, - number: u64, - hash: &BlockHash, - ) { - if let Some((level, sublevels)) = levels.split_first_mut() { - level.retain(|ref overlay| { - let parent = parents.get(&overlay.hash).expect("there is a parent entry for each entry in levels; qed").clone(); - if parent == *hash { - parents.remove(&overlay.hash); - discarded_journals.push(overlay.journal_key.clone()); - Self::discard(sublevels, parents, discarded_journals, number + 1, &overlay.hash); - false - } else { - true - } - }); - } - } - - fn front_block_number(&self) -> u64 { - self.last_finalized.as_ref().map(|&(_, n)| n + 1).unwrap_or(0) - } - - pub fn last_finalized_block_number(&self) -> u64 { - self.last_finalized.as_ref().map(|&(_, n)| n).unwrap_or(0) - } - - /// This may be called when the last finalization commit was applied to the database. - pub fn clear_overlay(&mut self) { - self.last_finalized_overlay.clear(); - } - - /// Select a top-level root and finalized it. Discards all sibling subtrees and the root. - /// Returns a set of changes that need to be added to the DB. - pub fn finalize(&mut self, hash: &BlockHash) -> CommitSet { - trace!(target: "state-db", "Finalizing {:?}", hash); - let level = self.levels.pop_front().expect("no blocks to finalize"); - let index = level.iter().position(|overlay| overlay.hash == *hash) - .expect("attempting to finalize unknown block"); - - let mut commit = CommitSet::default(); - let mut discarded_journals = Vec::new(); - for (i, overlay) in level.into_iter().enumerate() { - self.parents.remove(&overlay.hash); - if i == index { - self.last_finalized_overlay = overlay.values; - // that's the one we need to finalize - commit.data.inserted = self.last_finalized_overlay.iter().map(|(k, v)| (k.clone(), v.clone())).collect(); - commit.data.deleted = overlay.deleted; - } else { - // TODO: borrow checker won't allow us to split out mutable refernces - // required for recursive processing. A more efficient implementaion - // that does not require converting to vector is possible - let mut vec: Vec<_> = self.levels.drain(..).collect(); - Self::discard(&mut vec, &mut self.parents, &mut discarded_journals, 0, &overlay.hash); - self.levels.extend(vec.into_iter()); - } - // cleanup journal entry - discarded_journals.push(overlay.journal_key); - } - commit.meta.deleted.append(&mut discarded_journals); - let last_finalized = (hash.clone(), self.front_block_number()); - commit.meta.inserted.push((to_meta_key(LAST_FINALIZED, &()), last_finalized.encode())); - self.last_finalized = Some(last_finalized); - trace!(target: "state-db", "Discarded {} records", commit.meta.deleted.len()); - commit - } - - /// Get a value from the node overlay. This searches in every existing changeset. - pub fn get(&self, key: &Key) -> Option { - if let Some(value) = self.last_finalized_overlay.get(&key) { - return Some(value.clone()); - } - for level in self.levels.iter() { - for overlay in level.iter() { - if let Some(value) = overlay.values.get(&key) { - return Some(value.clone()); - } - } - } - None - } - - /// Revert a single level. Returns commit set that deletes the journal or `None` if not possible. - pub fn revert_one(&mut self) -> Option> { - self.levels.pop_back().map(|level| { - let mut commit = CommitSet::default(); - for overlay in level.into_iter() { - commit.meta.deleted.push(overlay.journal_key); - self.parents.remove(&overlay.hash); - } - commit - }) - } -} - -#[cfg(test)] -mod tests { - use super::UnfinalizedOverlay; - use {ChangeSet}; - use primitives::H256; - use test::{make_db, make_changeset}; - - fn contains(overlay: &UnfinalizedOverlay, key: u64) -> bool { - overlay.get(&H256::from(key)) == Some(H256::from(key).to_vec()) - } - - #[test] - fn created_from_empty_db() { - let db = make_db(&[]); - let overlay: UnfinalizedOverlay = UnfinalizedOverlay::new(&db).unwrap(); - assert_eq!(overlay.last_finalized, None); - assert!(overlay.levels.is_empty()); - assert!(overlay.parents.is_empty()); - } - - #[test] - #[should_panic] - fn finalize_empty_panics() { - let db = make_db(&[]); - let mut overlay = UnfinalizedOverlay::::new(&db).unwrap(); - overlay.finalize(&H256::default()); - } - - #[test] - #[should_panic] - fn insert_ahead_panics() { - let db = make_db(&[]); - let h1 = H256::random(); - let h2 = H256::random(); - let mut overlay = UnfinalizedOverlay::::new(&db).unwrap(); - overlay.insert(&h1, 2, &H256::default(), ChangeSet::default()); - overlay.insert(&h2, 1, &h1, ChangeSet::default()); - } - - #[test] - #[should_panic] - fn insert_behind_panics() { - let h1 = H256::random(); - let h2 = H256::random(); - let db = make_db(&[]); - let mut overlay = UnfinalizedOverlay::::new(&db).unwrap(); - overlay.insert(&h1, 1, &H256::default(), ChangeSet::default()); - overlay.insert(&h2, 3, &h1, ChangeSet::default()); - } - - #[test] - #[should_panic] - fn insert_unknown_parent_panics() { - let db = make_db(&[]); - let h1 = H256::random(); - let h2 = H256::random(); - let mut overlay = UnfinalizedOverlay::::new(&db).unwrap(); - overlay.insert(&h1, 1, &H256::default(), ChangeSet::default()); - overlay.insert(&h2, 2, &H256::default(), ChangeSet::default()); - } - - #[test] - #[should_panic] - fn finalize_unknown_panics() { - let h1 = H256::random(); - let h2 = H256::random(); - let db = make_db(&[]); - let mut overlay = UnfinalizedOverlay::::new(&db).unwrap(); - overlay.insert(&h1, 1, &H256::default(), ChangeSet::default()); - overlay.finalize(&h2); - } - - #[test] - fn insert_finalize_one() { - let h1 = H256::random(); - let mut db = make_db(&[1, 2]); - let mut overlay = UnfinalizedOverlay::::new(&db).unwrap(); - let changeset = make_changeset(&[3, 4], &[2]); - let insertion = overlay.insert(&h1, 1, &H256::default(), changeset.clone()); - assert_eq!(insertion.data.inserted.len(), 0); - assert_eq!(insertion.data.deleted.len(), 0); - assert_eq!(insertion.meta.inserted.len(), 2); - assert_eq!(insertion.meta.deleted.len(), 0); - db.commit(&insertion); - let finalization = overlay.finalize(&h1); - assert_eq!(finalization.data.inserted.len(), changeset.inserted.len()); - assert_eq!(finalization.data.deleted.len(), changeset.deleted.len()); - assert_eq!(finalization.meta.inserted.len(), 1); - assert_eq!(finalization.meta.deleted.len(), 1); - db.commit(&finalization); - assert!(db.data_eq(&make_db(&[1, 3, 4]))); - } - - #[test] - fn restore_from_journal() { - let h1 = H256::random(); - let h2 = H256::random(); - let mut db = make_db(&[1, 2]); - let mut overlay = UnfinalizedOverlay::::new(&db).unwrap(); - db.commit(&overlay.insert(&h1, 10, &H256::default(), make_changeset(&[3, 4], &[2]))); - db.commit(&overlay.insert(&h2, 11, &h1, make_changeset(&[5], &[3]))); - assert_eq!(db.meta.len(), 3); - - let overlay2 = UnfinalizedOverlay::::new(&db).unwrap(); - assert_eq!(overlay.levels, overlay2.levels); - assert_eq!(overlay.parents, overlay2.parents); - assert_eq!(overlay.last_finalized, overlay2.last_finalized); - } - - #[test] - fn restore_from_journal_after_finalize() { - let h1 = H256::random(); - let h2 = H256::random(); - let mut db = make_db(&[1, 2]); - let mut overlay = UnfinalizedOverlay::::new(&db).unwrap(); - db.commit(&overlay.insert(&h1, 10, &H256::default(), make_changeset(&[3, 4], &[2]))); - db.commit(&overlay.insert(&h2, 11, &h1, make_changeset(&[5], &[3]))); - db.commit(&overlay.finalize(&h1)); - assert_eq!(overlay.levels.len(), 1); - - let overlay2 = UnfinalizedOverlay::::new(&db).unwrap(); - assert_eq!(overlay.levels, overlay2.levels); - assert_eq!(overlay.parents, overlay2.parents); - assert_eq!(overlay.last_finalized, overlay2.last_finalized); - } - - #[test] - fn insert_finalize_two() { - let h1 = H256::random(); - let h2 = H256::random(); - let mut db = make_db(&[1, 2, 3, 4]); - let mut overlay = UnfinalizedOverlay::::new(&db).unwrap(); - let changeset1 = make_changeset(&[5, 6], &[2]); - let changeset2 = make_changeset(&[7, 8], &[5, 3]); - db.commit(&overlay.insert(&h1, 1, &H256::default(), changeset1)); - assert!(contains(&overlay, 5)); - db.commit(&overlay.insert(&h2, 2, &h1, changeset2)); - assert!(contains(&overlay, 7)); - assert!(contains(&overlay, 5)); - assert_eq!(overlay.levels.len(), 2); - assert_eq!(overlay.parents.len(), 2); - db.commit(&overlay.finalize(&h1)); - assert_eq!(overlay.levels.len(), 1); - assert_eq!(overlay.parents.len(), 1); - assert!(contains(&overlay, 5)); - overlay.clear_overlay(); - assert!(!contains(&overlay, 5)); - assert!(contains(&overlay, 7)); - db.commit(&overlay.finalize(&h2)); - overlay.clear_overlay(); - assert_eq!(overlay.levels.len(), 0); - assert_eq!(overlay.parents.len(), 0); - assert!(db.data_eq(&make_db(&[1, 4, 6, 7, 8]))); - } - - - #[test] - fn complex_tree() { - let mut db = make_db(&[]); - - // - 1 - 1_1 - 1_1_1 - // \ 1_2 - 1_2_1 - // \ 1_2_2 - // \ 1_2_3 - // - // - 2 - 2_1 - 2_1_1 - // \ 2_2 - // - // 1_2_2 is the winner - - let (h_1, c_1) = (H256::random(), make_changeset(&[1], &[])); - let (h_2, c_2) = (H256::random(), make_changeset(&[2], &[])); - - let (h_1_1, c_1_1) = (H256::random(), make_changeset(&[11], &[])); - let (h_1_2, c_1_2) = (H256::random(), make_changeset(&[12], &[])); - let (h_2_1, c_2_1) = (H256::random(), make_changeset(&[21], &[])); - let (h_2_2, c_2_2) = (H256::random(), make_changeset(&[22], &[])); - - let (h_1_1_1, c_1_1_1) = (H256::random(), make_changeset(&[111], &[])); - let (h_1_2_1, c_1_2_1) = (H256::random(), make_changeset(&[121], &[])); - let (h_1_2_2, c_1_2_2) = (H256::random(), make_changeset(&[122], &[])); - let (h_1_2_3, c_1_2_3) = (H256::random(), make_changeset(&[123], &[])); - let (h_2_1_1, c_2_1_1) = (H256::random(), make_changeset(&[211], &[])); - - let mut overlay = UnfinalizedOverlay::::new(&db).unwrap(); - db.commit(&overlay.insert(&h_1, 1, &H256::default(), c_1)); - - db.commit(&overlay.insert(&h_1_1, 2, &h_1, c_1_1)); - db.commit(&overlay.insert(&h_1_2, 2, &h_1, c_1_2)); - - db.commit(&overlay.insert(&h_2, 1, &H256::default(), c_2)); - - db.commit(&overlay.insert(&h_2_1, 2, &h_2, c_2_1)); - db.commit(&overlay.insert(&h_2_2, 2, &h_2, c_2_2)); - - db.commit(&overlay.insert(&h_1_1_1, 3, &h_1_1, c_1_1_1)); - db.commit(&overlay.insert(&h_1_2_1, 3, &h_1_2, c_1_2_1)); - db.commit(&overlay.insert(&h_1_2_2, 3, &h_1_2, c_1_2_2)); - db.commit(&overlay.insert(&h_1_2_3, 3, &h_1_2, c_1_2_3)); - db.commit(&overlay.insert(&h_2_1_1, 3, &h_2_1, c_2_1_1)); - - assert!(contains(&overlay, 2)); - assert!(contains(&overlay, 11)); - assert!(contains(&overlay, 21)); - assert!(contains(&overlay, 111)); - assert!(contains(&overlay, 122)); - assert!(contains(&overlay, 211)); - assert_eq!(overlay.levels.len(), 3); - assert_eq!(overlay.parents.len(), 11); - assert_eq!(overlay.last_finalized, Some((H256::default(), 0))); - - // check if restoration from journal results in the same tree - let overlay2 = UnfinalizedOverlay::::new(&db).unwrap(); - assert_eq!(overlay.levels, overlay2.levels); - assert_eq!(overlay.parents, overlay2.parents); - assert_eq!(overlay.last_finalized, overlay2.last_finalized); - - // finalize 1. 2 and all its children should be discarded - db.commit(&overlay.finalize(&h_1)); - overlay.clear_overlay(); - assert_eq!(overlay.levels.len(), 2); - assert_eq!(overlay.parents.len(), 6); - assert!(!contains(&overlay, 1)); - assert!(!contains(&overlay, 2)); - assert!(!contains(&overlay, 21)); - assert!(!contains(&overlay, 22)); - assert!(!contains(&overlay, 211)); - assert!(contains(&overlay, 111)); - - // finalize 1_2. 1_1 and all its children should be discarded - db.commit(&overlay.finalize(&h_1_2)); - overlay.clear_overlay(); - assert_eq!(overlay.levels.len(), 1); - assert_eq!(overlay.parents.len(), 3); - assert!(!contains(&overlay, 11)); - assert!(!contains(&overlay, 111)); - assert!(contains(&overlay, 121)); - assert!(contains(&overlay, 122)); - assert!(contains(&overlay, 123)); - - // finalize 1_2_2 - db.commit(&overlay.finalize(&h_1_2_2)); - overlay.clear_overlay(); - assert_eq!(overlay.levels.len(), 0); - assert_eq!(overlay.parents.len(), 0); - assert!(db.data_eq(&make_db(&[1, 12, 122]))); - assert_eq!(overlay.last_finalized, Some((h_1_2_2, 3))); - } - - #[test] - fn insert_revert() { - let h1 = H256::random(); - let h2 = H256::random(); - let mut db = make_db(&[1, 2, 3, 4]); - let mut overlay = UnfinalizedOverlay::::new(&db).unwrap(); - assert!(overlay.revert_one().is_none()); - let changeset1 = make_changeset(&[5, 6], &[2]); - let changeset2 = make_changeset(&[7, 8], &[5, 3]); - db.commit(&overlay.insert(&h1, 1, &H256::default(), changeset1)); - db.commit(&overlay.insert(&h2, 2, &h1, changeset2)); - assert!(contains(&overlay, 7)); - db.commit(&overlay.revert_one().unwrap()); - assert_eq!(overlay.parents.len(), 1); - assert!(contains(&overlay, 5)); - assert!(!contains(&overlay, 7)); - db.commit(&overlay.revert_one().unwrap()); - assert_eq!(overlay.levels.len(), 0); - assert_eq!(overlay.parents.len(), 0); - assert!(overlay.revert_one().is_none()); - } - -} - diff --git a/substrate/state-machine/Cargo.toml b/substrate/state-machine/Cargo.toml deleted file mode 100644 index 3635627999f35..0000000000000 --- a/substrate/state-machine/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "substrate-state-machine" -version = "0.1.0" -authors = ["Parity Technologies "] -description = "Substrate State Machine" - -[dependencies] -byteorder = "1.1" -hex-literal = "0.1.0" -log = "0.3" -parking_lot = "0.4" -heapsize = "0.4" -hashdb = "0.2.1" -memorydb = "0.2.1" -patricia-trie = "0.2.1" -triehash = "0.2" -rlp = "0.2.4" - -substrate-primitives = { path = "../primitives", version = "0.1.0" } -substrate-codec = { path = "../codec", default_features = false } - diff --git a/substrate/state-machine/README.adoc b/substrate/state-machine/README.adoc deleted file mode 100644 index aad08bed989db..0000000000000 --- a/substrate/state-machine/README.adoc +++ /dev/null @@ -1,13 +0,0 @@ - -= State Machine - -.Summary -[source, toml] ----- -include::Cargo.toml[lines=2..5] ----- - -.Description ----- -include::src/lib.rs[tag=description] ----- diff --git a/substrate/state-machine/src/backend.rs b/substrate/state-machine/src/backend.rs deleted file mode 100644 index 7db1e64d3f25f..0000000000000 --- a/substrate/state-machine/src/backend.rs +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! State machine backends. These manage the code and storage of contracts. - -use std::{error, fmt}; -use std::cmp::Ord; -use std::collections::HashMap; -use std::marker::PhantomData; -use std::sync::Arc; - -use hashdb::Hasher; -use rlp::Encodable; -use trie_backend::{TryIntoTrieBackend, TrieBackend}; -use patricia_trie::{TrieDBMut, TrieMut, NodeCodec}; -use heapsize::HeapSizeOf; - -/// A state backend is used to read state data and can have changes committed -/// to it. -/// -/// The clone operation (if implemented) should be cheap. -pub trait Backend>: TryIntoTrieBackend { - /// An error type when fetching data is not possible. - type Error: super::Error; - - /// Changes to be applied if committing - type Transaction; - - /// Get keyed storage associated with specific address, or None if there is nothing associated. - fn storage(&self, key: &[u8]) -> Result>, Self::Error>; - - /// true if a key exists in storage. - fn exists_storage(&self, key: &[u8]) -> Result { - Ok(self.storage(key)?.is_some()) - } - - /// Retrieve all entries keys of which start with the given prefix and - /// call `f` for each of those keys. - fn for_keys_with_prefix(&self, prefix: &[u8], f: F); - - /// Calculate the storage root, with given delta over what is already stored in - /// the backend, and produce a "transaction" that can be used to commit. - fn storage_root(&self, delta: I) -> (H::Out, Self::Transaction) - where - I: IntoIterator, Option>)>, - H::Out: Ord + Encodable; - - /// Get all key/value pairs into a Vec. - fn pairs(&self) -> Vec<(Vec, Vec)>; -} - -/// Error impossible. -// TODO: use `!` type when stabilized. -#[derive(Debug)] -pub enum Void {} - -impl fmt::Display for Void { - fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result { - match *self {} - } -} - -impl error::Error for Void { - fn description(&self) -> &str { "unreachable error" } -} - -/// In-memory backend. Fully recomputes tries on each commit but useful for -/// tests. -#[derive(Eq)] -pub struct InMemory { - inner: Arc, Vec>>, - _hasher: PhantomData, - _codec: PhantomData, -} - -impl Default for InMemory { - fn default() -> Self { - InMemory { - inner: Arc::new(Default::default()), - _hasher: PhantomData, - _codec: PhantomData, - } - } -} - -impl Clone for InMemory { - fn clone(&self) -> Self { - InMemory { - inner: self.inner.clone(), _hasher: PhantomData, _codec: PhantomData, - } - } -} - -impl PartialEq for InMemory { - fn eq(&self, other: &Self) -> bool { - self.inner.eq(&other.inner) - } -} - -impl> InMemory where H::Out: HeapSizeOf { - /// Copy the state, with applied updates - pub fn update(&self, changes: >::Transaction) -> Self { - let mut inner: HashMap<_, _> = (&*self.inner).clone(); - for (key, val) in changes { - match val { - Some(v) => { inner.insert(key, v); }, - None => { inner.remove(&key); }, - } - } - - inner.into() - } -} - -impl From, Vec>> for InMemory { - fn from(inner: HashMap, Vec>) -> Self { - InMemory { - inner: Arc::new(inner), _hasher: PhantomData, _codec: PhantomData - } - } -} - -impl super::Error for Void {} - -impl> Backend for InMemory where H::Out: HeapSizeOf { - type Error = Void; - type Transaction = Vec<(Vec, Option>)>; - - fn storage(&self, key: &[u8]) -> Result>, Self::Error> { - Ok(self.inner.get(key).map(Clone::clone)) - } - - fn exists_storage(&self, key: &[u8]) -> Result { - Ok(self.inner.get(key).is_some()) - } - - fn for_keys_with_prefix(&self, prefix: &[u8], f: F) { - self.inner.keys().filter(|key| key.starts_with(prefix)).map(|k| &**k).for_each(f); - } - - fn storage_root(&self, delta: I) -> (H::Out, Self::Transaction) - where - I: IntoIterator, Option>)>, - ::Out: Ord + Encodable, - { - let existing_pairs = self.inner.iter().map(|(k, v)| (k.clone(), Some(v.clone()))); - - let transaction: Vec<_> = delta.into_iter().collect(); - let root = ::triehash::trie_root::(existing_pairs.chain(transaction.iter().cloned()) - .collect::>() - .into_iter() - .filter_map(|(k, maybe_val)| maybe_val.map(|val| (k, val))) - ); - - (root, transaction) - } - - fn pairs(&self) -> Vec<(Vec, Vec)> { - self.inner.iter().map(|(k, v)| (k.clone(), v.clone())).collect() - } -} - -impl> TryIntoTrieBackend for InMemory where H::Out: HeapSizeOf { - fn try_into_trie_backend(self) -> Option> { - use memorydb::MemoryDB; - let mut root = ::Out::default(); - let mut mdb = MemoryDB::new(); - { - let mut trie = TrieDBMut::::new(&mut mdb, &mut root); - for (key, value) in self.inner.iter() { - if let Err(e) = trie.insert(&key, &value) { - warn!(target: "trie", "Failed to write to trie: {}", e); - return None; - } - } - } - - Some(TrieBackend::with_memorydb(mdb, root)) - } -} diff --git a/substrate/state-machine/src/ext.rs b/substrate/state-machine/src/ext.rs deleted file mode 100644 index 8504f42b59392..0000000000000 --- a/substrate/state-machine/src/ext.rs +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright 2017 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 . - -//! Conrete externalities implementation. - -use std::{error, fmt, cmp::Ord}; -use backend::Backend; -use {Externalities, OverlayedChanges}; -use hashdb::Hasher; -use rlp::Encodable; -use patricia_trie::NodeCodec; - -/// Errors that can occur when interacting with the externalities. -#[derive(Debug, Copy, Clone)] -pub enum Error { - /// Failure to load state data from the backend. - #[allow(unused)] - Backend(B), - /// Failure to execute a function. - #[allow(unused)] - Executor(E), -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::Backend(ref e) => write!(f, "Storage backend error: {}", e), - Error::Executor(ref e) => write!(f, "Sub-call execution error: {}", e), - } - } -} - -impl error::Error for Error { - fn description(&self) -> &str { - match *self { - Error::Backend(..) => "backend error", - Error::Executor(..) => "executor error", - } - } -} - -/// Wraps a read-only backend, call executor, and current overlayed changes. -pub struct Ext<'a, H, C, B> -where - H: Hasher, - C: NodeCodec, - B: 'a + Backend, -{ - // The overlayed changes to write to. - overlay: &'a mut OverlayedChanges, - // The storage backend to read from. - backend: &'a B, - // The transaction necessary to commit to the backend. - transaction: Option<(B::Transaction, H::Out)>, -} - -impl<'a, H, C, B> Ext<'a, H, C, B> -where - H: Hasher, - C: NodeCodec, - B: 'a + Backend, - H::Out: Ord + Encodable -{ - /// Create a new `Ext` from overlayed changes and read-only backend - pub fn new(overlay: &'a mut OverlayedChanges, backend: &'a B) -> Self { - Ext { - overlay, - backend, - transaction: None, - } - } - - /// Get the transaction necessary to update the backend. - pub fn transaction(mut self) -> B::Transaction { - let _ = self.storage_root(); - self.transaction.expect("transaction always set after calling storage root; qed").0 - } - - /// Invalidates the currently cached storage root and the db transaction. - /// - /// Called when there are changes that likely will invalidate the storage root. - fn mark_dirty(&mut self) { - self.transaction = None; - } -} - -#[cfg(test)] -impl<'a, H, C, B> Ext<'a, H, C, B> -where - H: Hasher, - C: NodeCodec, - B: 'a + Backend, -{ - pub fn storage_pairs(&self) -> Vec<(Vec, Vec)> { - use std::collections::HashMap; - - self.backend.pairs().iter() - .map(|&(ref k, ref v)| (k.to_vec(), Some(v.to_vec()))) - .chain(self.overlay.committed.clone().into_iter()) - .chain(self.overlay.prospective.clone().into_iter()) - .collect::>() - .into_iter() - .filter_map(|(k, maybe_val)| maybe_val.map(|val| (k, val))) - .collect() - } -} - -impl<'a, B: 'a, H, C> Externalities for Ext<'a, H, C, B> -where - H: Hasher, - C: NodeCodec, - B: 'a + Backend, - H::Out: Ord + Encodable -{ - fn storage(&self, key: &[u8]) -> Option> { - self.overlay.storage(key).map(|x| x.map(|x| x.to_vec())).unwrap_or_else(|| - self.backend.storage(key).expect("Externalities not allowed to fail within runtime")) - } - - fn exists_storage(&self, key: &[u8]) -> bool { - match self.overlay.storage(key) { - Some(x) => x.is_some(), - _ => self.backend.exists_storage(key).expect("Externalities not allowed to fail within runtime"), - } - } - - fn place_storage(&mut self, key: Vec, value: Option>) { - self.mark_dirty(); - self.overlay.set_storage(key, value); - } - - fn clear_prefix(&mut self, prefix: &[u8]) { - self.mark_dirty(); - self.overlay.clear_prefix(prefix); - self.backend.for_keys_with_prefix(prefix, |key| { - self.overlay.set_storage(key.to_vec(), None); - }); - } - - fn chain_id(&self) -> u64 { - 42 - } - - fn storage_root(&mut self) -> H::Out { - if let Some((_, ref root)) = self.transaction { - return root.clone(); - } - - // compute and memoize - let delta = self.overlay.committed.iter() - .chain(self.overlay.prospective.iter()) - .map(|(k, v)| (k.clone(), v.clone())); - - let (root, transaction) = self.backend.storage_root(delta); - self.transaction = Some((transaction, root)); - root - } -} diff --git a/substrate/state-machine/src/lib.rs b/substrate/state-machine/src/lib.rs deleted file mode 100644 index 7dab6e6af6375..0000000000000 --- a/substrate/state-machine/src/lib.rs +++ /dev/null @@ -1,691 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -// tag::description[] -//! Substrate state machine implementation. -// end::description[] - -#![warn(missing_docs)] - -#[cfg_attr(test, macro_use)] -extern crate hex_literal; - -#[macro_use] -extern crate log; - -extern crate hashdb; -extern crate memorydb; -extern crate triehash; -extern crate patricia_trie; -extern crate byteorder; -extern crate parking_lot; -extern crate rlp; -extern crate heapsize; -#[cfg(test)] -extern crate substrate_primitives as primitives; -extern crate substrate_codec as codec; - -use std::collections::HashMap; -use std::fmt; -use hashdb::Hasher; -use patricia_trie::NodeCodec; -use rlp::Encodable; -use heapsize::HeapSizeOf; -use codec::Decode; - -pub mod backend; -mod ext; -mod testing; -mod proving_backend; -mod trie_backend; - -pub use testing::TestExternalities; -pub use ext::Ext; -pub use backend::Backend; -pub use trie_backend::{TryIntoTrieBackend, TrieBackend, Storage, DBValue}; - -/// The overlayed changes to state to be queried on top of the backend. -/// -/// A transaction shares all prospective changes within an inner overlay -/// that can be cleared. -#[derive(Debug, Default, Clone)] -pub struct OverlayedChanges { - prospective: HashMap, Option>>, - committed: HashMap, Option>>, -} - -impl OverlayedChanges { - /// Returns a double-Option: None if the key is unknown (i.e. and the query should be refered - /// to the backend); Some(None) if the key has been deleted. Some(Some(...)) for a key whose - /// value has been set. - pub fn storage(&self, key: &[u8]) -> Option> { - self.prospective.get(key) - .or_else(|| self.committed.get(key)) - .map(|x| x.as_ref().map(AsRef::as_ref)) - } - - /// Inserts the given key-value pair into the prospective change set. - /// - /// `None` can be used to delete a value specified by the given key. - fn set_storage(&mut self, key: Vec, val: Option>) { - self.prospective.insert(key, val); - } - - /// Removes all key-value pairs which keys share the given prefix. - /// - /// NOTE that this doesn't take place immediately but written into the prospective - /// change set, and still can be reverted by [`discard_prospective`]. - /// - /// [`discard_prospective`]: #method.discard_prospective - fn clear_prefix(&mut self, prefix: &[u8]) { - // Iterate over all prospective and mark all keys that share - // the given prefix as removed (None). - for (key, value) in self.prospective.iter_mut() { - if key.starts_with(prefix) { - *value = None; - } - } - - // Then do the same with keys from commited changes. - // NOTE that we are making changes in the prospective change set. - for key in self.committed.keys() { - if key.starts_with(prefix) { - self.prospective.insert(key.to_owned(), None); - } - } - } - - /// Discard prospective changes to state. - pub fn discard_prospective(&mut self) { - self.prospective.clear(); - } - - /// Commit prospective changes to state. - pub fn commit_prospective(&mut self) { - if self.committed.is_empty() { - ::std::mem::swap(&mut self.prospective, &mut self.committed); - } else { - self.committed.extend(self.prospective.drain()); - } - } - - /// Drain committed changes to an iterator. - /// - /// Panics: - /// Will panic if there are any uncommitted prospective changes. - pub fn drain<'a>(&'a mut self) -> impl Iterator, Option>)> + 'a { - assert!(self.prospective.is_empty()); - self.committed.drain() - } - - /// Consume `OverlayedChanges` and take committed set. - /// - /// Panics: - /// Will panic if there are any uncommitted prospective changes. - pub fn into_committed(self) -> impl Iterator, Option>)> { - assert!(self.prospective.is_empty()); - self.committed.into_iter() - } -} - -/// State Machine Error bound. -/// -/// This should reflect WASM error type bound for future compatibility. -pub trait Error: 'static + fmt::Debug + fmt::Display + Send {} - -impl Error for ExecutionError {} - -/// Externalities Error. -/// -/// Externalities are not really allowed to have errors, since it's assumed that dependent code -/// would not be executed unless externalities were available. This is included for completeness, -/// and as a transition away from the pre-existing framework. -#[derive(Debug, Eq, PartialEq)] -pub enum ExecutionError { - /// The entry `:code` doesn't exist in storage so there's no way we can execute anything. - CodeEntryDoesNotExist, - /// Backend is incompatible with execution proof generation process. - UnableToGenerateProof, - /// Invalid execution proof. - InvalidProof, -} - -impl fmt::Display for ExecutionError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Externalities Error") } -} - -/// Externalities: pinned to specific active address. -pub trait Externalities { - /// Read storage of current contract being called. - fn storage(&self, key: &[u8]) -> Option>; - - /// Set storage entry `key` of current contract being called (effective immediately). - fn set_storage(&mut self, key: Vec, value: Vec) { - self.place_storage(key, Some(value)); - } - - /// Clear a storage entry (`key`) of current contract being called (effective immediately). - fn clear_storage(&mut self, key: &[u8]) { - self.place_storage(key.to_vec(), None); - } - - /// Clear a storage entry (`key`) of current contract being called (effective immediately). - fn exists_storage(&self, key: &[u8]) -> bool { - self.storage(key).is_some() - } - - /// Clear storage entries which keys are start with the given prefix. - fn clear_prefix(&mut self, prefix: &[u8]); - - /// Set or clear a storage entry (`key`) of current contract being called (effective immediately). - fn place_storage(&mut self, key: Vec, value: Option>); - - /// Get the identity of the chain. - fn chain_id(&self) -> u64; - - /// Get the trie root of the current storage map. - fn storage_root(&mut self) -> H::Out where H::Out: Ord + Encodable; -} - -/// Code execution engine. -pub trait CodeExecutor: Sized + Send + Sync { - /// Externalities error type. - type Error: Error; - - /// Call a given method in the runtime. Returns a tuple of the result (either the output data - /// or an execution error) together with a `bool`, which is true if native execution was used. - fn call>( - &self, - ext: &mut E, - heap_pages: usize, - code: &[u8], - method: &str, - data: &[u8], - use_native: bool - ) -> (Result, Self::Error>, bool); -} - -/// Strategy for executing a call into the runtime. -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub enum ExecutionStrategy { - /// Execute with the native equivalent if it is compatible with the given wasm module; otherwise fall back to the wasm. - NativeWhenPossible, - /// Use the given wasm module. - AlwaysWasm, - /// Run with both the wasm and the native variant (if compatible). Report any discrepency as an error. - Both, -} - -/// Like `ExecutionStrategy` only it also stores a handler in case of consensus failure. -pub enum ExecutionManager { - /// Execute with the native equivalent if it is compatible with the given wasm module; otherwise fall back to the wasm. - NativeWhenPossible, - /// Use the given wasm module. - AlwaysWasm, - /// Run with both the wasm and the native variant (if compatible). Call `F` in the case of any discrepency. - Both(F), -} - -impl<'a, F> From<&'a ExecutionManager> for ExecutionStrategy { - fn from(s: &'a ExecutionManager) -> Self { - match *s { - ExecutionManager::NativeWhenPossible => ExecutionStrategy::NativeWhenPossible, - ExecutionManager::AlwaysWasm => ExecutionStrategy::AlwaysWasm, - ExecutionManager::Both(_) => ExecutionStrategy::Both, - } - } -} - -/// Evaluate to ExecutionManager::NativeWhenPossible, without having to figure out the type. -pub fn native_when_possible() -> ExecutionManager, E>, Result, E>)->Result, E>> { - ExecutionManager::NativeWhenPossible -} - -/// Evaluate to ExecutionManager::NativeWhenPossible, without having to figure out the type. -pub fn always_wasm() -> ExecutionManager, E>, Result, E>)->Result, E>> { - ExecutionManager::AlwaysWasm -} - -/// Execute a call using the given state backend, overlayed changes, and call executor. -/// Produces a state-backend-specific "transaction" which can be used to apply the changes -/// to the backing store, such as the disk. -/// -/// On an error, no prospective changes are written to the overlay. -/// -/// Note: changes to code will be in place if this call is made again. For running partial -/// blocks (e.g. a transaction at a time), ensure a different method is used. -pub fn execute( - backend: &B, - overlay: &mut OverlayedChanges, - exec: &Exec, - method: &str, - call_data: &[u8], - strategy: ExecutionStrategy, -) -> Result<(Vec, B::Transaction), Box> -where - H: Hasher, - C: NodeCodec, - Exec: CodeExecutor, - B: Backend, - H::Out: Ord + Encodable -{ - execute_using_consensus_failure_handler( - backend, - overlay, - exec, - method, - call_data, - match strategy { - ExecutionStrategy::AlwaysWasm => ExecutionManager::AlwaysWasm, - ExecutionStrategy::NativeWhenPossible => ExecutionManager::NativeWhenPossible, - ExecutionStrategy::Both => ExecutionManager::Both(|wasm_result, native_result| { - warn!("Consensus error between wasm {:?} and native {:?}. Using wasm.", wasm_result, native_result); - wasm_result - }), - }, - ) -} - -/// Execute a call using the given state backend, overlayed changes, and call executor. -/// Produces a state-backend-specific "transaction" which can be used to apply the changes -/// to the backing store, such as the disk. -/// -/// On an error, no prospective changes are written to the overlay. -/// -/// Note: changes to code will be in place if this call is made again. For running partial -/// blocks (e.g. a transaction at a time), ensure a different method is used. -pub fn execute_using_consensus_failure_handler( - backend: &B, - overlay: &mut OverlayedChanges, - exec: &Exec, - method: &str, - call_data: &[u8], - manager: ExecutionManager, -) -> Result<(Vec, B::Transaction), Box> -where - H: Hasher, - C: NodeCodec, - Exec: CodeExecutor, - B: Backend, - H::Out: Ord + Encodable, - Handler: FnOnce(Result, Exec::Error>, Result, Exec::Error>) -> Result, Exec::Error> -{ - let strategy: ExecutionStrategy = (&manager).into(); - - // make a copy. - let code = ext::Ext::new(overlay, backend).storage(b":code") - .ok_or_else(|| Box::new(ExecutionError::CodeEntryDoesNotExist) as Box)? - .to_vec(); - - let heap_pages = ext::Ext::new(overlay, backend).storage(b":heappages") - .and_then(|v| u64::decode(&mut &v[..])).unwrap_or(8) as usize; - - let result = { - let mut orig_prospective = overlay.prospective.clone(); - - let (result, was_native, delta) = { - let ((result, was_native), delta) = { - let mut externalities = ext::Ext::new(overlay, backend); - ( - exec.call( - &mut externalities, - heap_pages, - &code, - method, - call_data, - // attempt to run native first, if we're not directed to run wasm only - strategy != ExecutionStrategy::AlwaysWasm, - ), - externalities.transaction() - ) - }; - (result, was_native, delta) - }; - - // run wasm separately if we did run native the first time and we're meant to run both - let (result, delta) = if let (true, ExecutionManager::Both(on_consensus_failure)) = - (was_native, manager) - { - overlay.prospective = orig_prospective.clone(); - - let (wasm_result, wasm_delta) = { - let ((result, _), delta) = { - let mut externalities = ext::Ext::new(overlay, backend); - ( - exec.call( - &mut externalities, - heap_pages, - &code, - method, - call_data, - false, - ), - externalities.transaction() - ) - }; - (result, delta) - }; - - if (result.is_ok() && wasm_result.is_ok() && result.as_ref().unwrap() == wasm_result.as_ref().unwrap()/* && delta == wasm_delta*/) - || (result.is_err() && wasm_result.is_err()) - { - (result, delta) - } else { - // Consensus error. - (on_consensus_failure(wasm_result, result), wasm_delta) - } - } else { - (result, delta) - }; - result.map(move |out| (out, delta)) - }; - - result.map_err(|e| Box::new(e) as _) -} - -/// Prove execution using the given state backend, overlayed changes, and call executor. -/// Produces a state-backend-specific "transaction" which can be used to apply the changes -/// to the backing store, such as the disk. -/// Execution proof is the set of all 'touched' storage DBValues from the backend. -/// -/// On an error, no prospective changes are written to the overlay. -/// -/// Note: changes to code will be in place if this call is made again. For running partial -/// blocks (e.g. a transaction at a time), ensure a different method is used. -pub fn prove_execution( - backend: B, - overlay: &mut OverlayedChanges, - exec: &Exec, - method: &str, - call_data: &[u8], -) -> Result<(Vec, Vec>, as Backend>::Transaction), Box> -where - H: Hasher, - Exec: CodeExecutor, - C: NodeCodec, - B: TryIntoTrieBackend, - H::Out: Ord + Encodable + HeapSizeOf, -{ - let trie_backend = backend.try_into_trie_backend() - .ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box)?; - let proving_backend = proving_backend::ProvingBackend::new(trie_backend); - let (result, transaction) = execute::(&proving_backend, overlay, exec, method, call_data, ExecutionStrategy::NativeWhenPossible)?; - let proof = proving_backend.extract_proof(); - Ok((result, proof, transaction)) -} - -/// Check execution proof, generated by `prove_execution` call. -pub fn execution_proof_check( - root: H::Out, - proof: Vec>, - overlay: &mut OverlayedChanges, - exec: &Exec, - method: &str, - call_data: &[u8], -) -> Result<(Vec, memorydb::MemoryDB), Box> -where -H: Hasher, -C: NodeCodec, -Exec: CodeExecutor, -H::Out: Ord + Encodable + HeapSizeOf, -{ - let backend = proving_backend::create_proof_check_backend::(root.into(), proof)?; - execute::(&backend, overlay, exec, method, call_data, ExecutionStrategy::NativeWhenPossible) -} - -/// Generate storage read proof. -pub fn prove_read( - backend: B, - key: &[u8] -) -> Result<(Option>, Vec>), Box> -where - B: TryIntoTrieBackend, - H: Hasher, - C: NodeCodec, - H::Out: Ord + Encodable + HeapSizeOf -{ - let trie_backend = backend.try_into_trie_backend() - .ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box)?; - let proving_backend = proving_backend::ProvingBackend::::new(trie_backend); - let result = proving_backend.storage(key).map_err(|e| Box::new(e) as Box)?; - Ok((result, proving_backend.extract_proof())) -} - -/// Check storage read proof, generated by `prove_read` call. -pub fn read_proof_check( - root: H::Out, - proof: Vec>, - key: &[u8], -) -> Result>, Box> -where - H: Hasher, - C: NodeCodec, - H::Out: Ord + Encodable + HeapSizeOf -{ - let backend = proving_backend::create_proof_check_backend::(root, proof)?; - backend.storage(key).map_err(|e| Box::new(e) as Box) -} - -#[cfg(test)] -mod tests { - use super::*; - use super::backend::InMemory; - use super::ext::Ext; - use primitives::{Blake2Hasher, RlpCodec, H256}; - - struct DummyCodeExecutor { - native_available: bool, - native_succeeds: bool, - fallback_succeeds: bool, - } - - impl CodeExecutor for DummyCodeExecutor { - type Error = u8; - - fn call>( - &self, - ext: &mut E, - _heap_pages: usize, - _code: &[u8], - _method: &str, - _data: &[u8], - use_native: bool - ) -> (Result, Self::Error>, bool) { - let using_native = use_native && self.native_available; - match (using_native, self.native_succeeds, self.fallback_succeeds) { - (true, true, _) | (false, _, true) => - (Ok(vec![ext.storage(b"value1").unwrap()[0] + ext.storage(b"value2").unwrap()[0]]), using_native), - _ => (Err(0), using_native), - } - } - } - - impl Error for u8 {} - - #[test] - fn overlayed_storage_works() { - let mut overlayed = OverlayedChanges::default(); - - let key = vec![42, 69, 169, 142]; - - assert!(overlayed.storage(&key).is_none()); - - overlayed.set_storage(key.clone(), Some(vec![1, 2, 3])); - assert_eq!(overlayed.storage(&key).unwrap(), Some(&[1, 2, 3][..])); - - overlayed.commit_prospective(); - assert_eq!(overlayed.storage(&key).unwrap(), Some(&[1, 2, 3][..])); - - overlayed.set_storage(key.clone(), Some(vec![])); - assert_eq!(overlayed.storage(&key).unwrap(), Some(&[][..])); - - overlayed.set_storage(key.clone(), None); - assert!(overlayed.storage(&key).unwrap().is_none()); - - overlayed.discard_prospective(); - assert_eq!(overlayed.storage(&key).unwrap(), Some(&[1, 2, 3][..])); - - overlayed.set_storage(key.clone(), None); - overlayed.commit_prospective(); - assert!(overlayed.storage(&key).unwrap().is_none()); - } - - macro_rules! map { - ($( $name:expr => $value:expr ),*) => ( - vec![ $( ( $name, $value ) ),* ].into_iter().collect() - ) - } - - #[test] - fn overlayed_storage_root_works() { - let initial: HashMap<_, _> = map![ - b"doe".to_vec() => b"reindeer".to_vec(), - b"dog".to_vec() => b"puppyXXX".to_vec(), - b"dogglesworth".to_vec() => b"catXXX".to_vec(), - b"doug".to_vec() => b"notadog".to_vec() - ]; - let backend = InMemory::::from(initial); - let mut overlay = OverlayedChanges { - committed: map![ - b"dog".to_vec() => Some(b"puppy".to_vec()), - b"dogglesworth".to_vec() => Some(b"catYYY".to_vec()), - b"doug".to_vec() => Some(vec![]) - ], - prospective: map![ - b"dogglesworth".to_vec() => Some(b"cat".to_vec()), - b"doug".to_vec() => None - ], - }; - let mut ext = Ext::new(&mut overlay, &backend); - const ROOT: [u8; 32] = hex!("6ca394ff9b13d6690a51dea30b1b5c43108e52944d30b9095227c49bae03ff8b"); - assert_eq!(ext.storage_root(), H256(ROOT)); - } - - #[test] - fn execute_works() { - assert_eq!(execute( - &trie_backend::tests::test_trie(), - &mut Default::default(), - &DummyCodeExecutor { - native_available: true, - native_succeeds: true, - fallback_succeeds: true, - }, - "test", - &[], - ExecutionStrategy::NativeWhenPossible - ).unwrap().0, vec![66]); - } - - #[test] - fn dual_execution_strategy_detects_consensus_failure() { - let mut consensus_failed = false; - assert!(execute_using_consensus_failure_handler( - &trie_backend::tests::test_trie(), - &mut Default::default(), - &DummyCodeExecutor { - native_available: true, - native_succeeds: true, - fallback_succeeds: false, - }, - "test", - &[], - ExecutionManager::Both(|we, _ne| { - consensus_failed = true; - println!("HELLO!"); - we - }), - ).is_err()); - assert!(consensus_failed); - } - - #[test] - fn prove_execution_and_proof_check_works() { - let executor = DummyCodeExecutor { - native_available: true, - native_succeeds: true, - fallback_succeeds: true, - }; - - // fetch execution proof from 'remote' full node - let remote_backend = trie_backend::tests::test_trie(); - let remote_root = remote_backend.storage_root(::std::iter::empty()).0; - let (remote_result, remote_proof, _) = prove_execution(remote_backend, - &mut Default::default(), &executor, "test", &[]).unwrap(); - - // check proof locally - let (local_result, _) = execution_proof_check::(remote_root, remote_proof, - &mut Default::default(), &executor, "test", &[]).unwrap(); - - // check that both results are correct - assert_eq!(remote_result, vec![66]); - assert_eq!(remote_result, local_result); - } - - #[test] - fn clear_prefix_in_ext_works() { - let initial: HashMap<_, _> = map![ - b"aaa".to_vec() => b"0".to_vec(), - b"abb".to_vec() => b"1".to_vec(), - b"abc".to_vec() => b"2".to_vec(), - b"bbb".to_vec() => b"3".to_vec() - ]; - let backend = InMemory::::from(initial).try_into_trie_backend().unwrap(); - let mut overlay = OverlayedChanges { - committed: map![ - b"aba".to_vec() => Some(b"1312".to_vec()), - b"bab".to_vec() => Some(b"228".to_vec()) - ], - prospective: map![ - b"abd".to_vec() => Some(b"69".to_vec()), - b"bbd".to_vec() => Some(b"42".to_vec()) - ], - }; - - { - let mut ext = Ext::new(&mut overlay, &backend); - ext.clear_prefix(b"ab"); - } - overlay.commit_prospective(); - - assert_eq!( - overlay.committed, - map![ - b"abb".to_vec() => None, - b"abc".to_vec() => None, - b"aba".to_vec() => None, - b"abd".to_vec() => None, - - b"bab".to_vec() => Some(b"228".to_vec()), - b"bbd".to_vec() => Some(b"42".to_vec()) - ], - ); - } - - #[test] - fn prove_read_and_proof_check_works() { - // fetch read proof from 'remote' full node - let remote_backend = trie_backend::tests::test_trie(); - let remote_root = remote_backend.storage_root(::std::iter::empty()).0; - let remote_proof = prove_read(remote_backend, b"value2").unwrap().1; - // check proof locally - let local_result1 = read_proof_check::(remote_root, remote_proof.clone(), b"value2").unwrap(); - let local_result2 = read_proof_check::(remote_root, remote_proof.clone(), &[0xff]).is_ok(); - // check that results are correct - assert_eq!(local_result1, Some(vec![24])); - assert_eq!(local_result2, false); - } -} diff --git a/substrate/state-machine/src/proving_backend.rs b/substrate/state-machine/src/proving_backend.rs deleted file mode 100644 index 25a2f49c75a34..0000000000000 --- a/substrate/state-machine/src/proving_backend.rs +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright 2017 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 . - -//! Proving state machine backend. - -use std::cell::RefCell; -use hashdb::{Hasher, HashDB}; -use memorydb::MemoryDB; -use patricia_trie::{TrieDB, Trie, Recorder, NodeCodec}; -use trie_backend::{TrieBackend, Ephemeral}; -use {Error, ExecutionError, Backend, TryIntoTrieBackend}; -use rlp::Encodable; -use heapsize::HeapSizeOf; - -/// Patricia trie-based backend which also tracks all touched storage trie values. -/// These can be sent to remote node and used as a proof of execution. -pub struct ProvingBackend> { - backend: TrieBackend, - proof_recorder: RefCell>, -} - -impl> ProvingBackend { - /// Create new proving backend. - pub fn new(backend: TrieBackend) -> Self { - ProvingBackend { - backend, - proof_recorder: RefCell::new(Recorder::new()), - } - } - - /// Consume the backend, extracting the gathered proof in lexicographical order - /// by value. - pub fn extract_proof(self) -> Vec> { - self.proof_recorder.into_inner().drain() - .into_iter() - .map(|n| n.data.to_vec()) - .collect() - } -} - -impl Backend for ProvingBackend -where - H: Hasher, - C: NodeCodec, - H::Out: Ord + Encodable + HeapSizeOf -{ - type Error = String; - type Transaction = MemoryDB; - - fn storage(&self, key: &[u8]) -> Result>, Self::Error> { - let mut read_overlay = MemoryDB::new(); - let eph = Ephemeral::new( - self.backend.backend_storage(), - &mut read_overlay, - ); - let map_e = |e| format!("Trie lookup error: {}", e); - - let mut proof_recorder = self.proof_recorder.try_borrow_mut() - .expect("only fails when already borrowed; storage() is non-reentrant; qed"); - TrieDB::::new(&eph, &self.backend.root()).map_err(map_e)? - .get_with(key, &mut *proof_recorder).map(|x| x.map(|val| val.to_vec())).map_err(map_e) - } - - fn for_keys_with_prefix(&self, prefix: &[u8], f: F) { - self.backend.for_keys_with_prefix(prefix, f) - } - - fn pairs(&self) -> Vec<(Vec, Vec)> { - self.backend.pairs() - } - - fn storage_root(&self, delta: I) -> (H::Out, MemoryDB) - where I: IntoIterator, Option>)> - { - self.backend.storage_root(delta) - } -} - -impl> TryIntoTrieBackend for ProvingBackend { - fn try_into_trie_backend(self) -> Option> { - None - } -} - -/// Create proof check backend. -pub fn create_proof_check_backend( - root: H::Out, - proof: Vec> -) -> Result, Box> -where - H: Hasher, - C: NodeCodec, - H::Out: HeapSizeOf, -{ - let mut db = MemoryDB::new(); - for item in proof { - db.insert(&item); - } - - if !db.contains(&root) { - return Err(Box::new(ExecutionError::InvalidProof) as Box); - } - - - Ok(TrieBackend::with_memorydb(db, root)) -} - -#[cfg(test)] -mod tests { - use backend::{InMemory}; - use trie_backend::tests::test_trie; - use super::*; - use primitives::{Blake2Hasher, RlpCodec}; - - fn test_proving() -> ProvingBackend { - ProvingBackend::new(test_trie()) - } - - #[test] - fn proof_is_empty_until_value_is_read() { - assert!(test_proving().extract_proof().is_empty()); - } - - #[test] - fn proof_is_non_empty_after_value_is_read() { - let backend = test_proving(); - assert_eq!(backend.storage(b"key").unwrap(), Some(b"value".to_vec())); - assert!(!backend.extract_proof().is_empty()); - } - - #[test] - fn proof_is_invalid_when_does_not_contains_root() { - assert!(create_proof_check_backend::(1.into(), vec![]).is_err()); - } - - #[test] - fn passes_throgh_backend_calls() { - let trie_backend = test_trie(); - let proving_backend = test_proving(); - assert_eq!(trie_backend.storage(b"key").unwrap(), proving_backend.storage(b"key").unwrap()); - assert_eq!(trie_backend.pairs(), proving_backend.pairs()); - - let (trie_root, mut trie_mdb) = trie_backend.storage_root(::std::iter::empty()); - let (proving_root, mut proving_mdb) = proving_backend.storage_root(::std::iter::empty()); - assert_eq!(trie_root, proving_root); - assert_eq!(trie_mdb.drain(), proving_mdb.drain()); - } - - #[test] - fn proof_recorded_and_checked() { - let contents = (0..64).map(|i| (vec![i], Some(vec![i]))).collect::>(); - let in_memory = InMemory::::default(); - let in_memory = in_memory.update(contents); - let in_memory_root = in_memory.storage_root(::std::iter::empty()).0; - (0..64).for_each(|i| assert_eq!(in_memory.storage(&[i]).unwrap().unwrap(), vec![i])); - - let trie = in_memory.try_into_trie_backend().unwrap(); - let trie_root = trie.storage_root(::std::iter::empty()).0; - assert_eq!(in_memory_root, trie_root); - (0..64).for_each(|i| assert_eq!(trie.storage(&[i]).unwrap().unwrap(), vec![i])); - - let proving = ProvingBackend::new(trie); - assert_eq!(proving.storage(&[42]).unwrap().unwrap(), vec![42]); - - let proof = proving.extract_proof(); - - let proof_check = create_proof_check_backend::(in_memory_root.into(), proof).unwrap(); - assert_eq!(proof_check.storage(&[42]).unwrap().unwrap(), vec![42]); - } -} diff --git a/substrate/state-machine/src/testing.rs b/substrate/state-machine/src/testing.rs deleted file mode 100644 index bcb02f6bb06e0..0000000000000 --- a/substrate/state-machine/src/testing.rs +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! Test implementation for Externalities. - -use std::collections::HashMap; -use std::cmp::Ord; -use super::Externalities; -use triehash::trie_root; -use hashdb::Hasher; -use rlp::Encodable; -use std::marker::PhantomData; -use std::iter::FromIterator; - -/// Simple HashMap-based Externalities impl. -#[derive(Debug)] -pub struct TestExternalities { - inner: HashMap, Vec>, - _hasher: PhantomData, -} - -impl TestExternalities { - /// Create a new instance of `TestExternalities` - pub fn new() -> Self { - TestExternalities {inner: HashMap::new(), _hasher: PhantomData} - } - /// Insert key/value - pub fn insert(&mut self, k: Vec, v: Vec) -> Option> { - self.inner.insert(k, v) - } -} - -impl PartialEq for TestExternalities { - fn eq(&self, other: &TestExternalities) -> bool { - self.inner.eq(&other.inner) - } -} - -impl FromIterator<(Vec, Vec)> for TestExternalities { - fn from_iter, Vec)>>(iter: I) -> Self { - let mut t = Self::new(); - for i in iter { - t.inner.insert(i.0, i.1); - } - t - } -} - -impl Default for TestExternalities { - fn default() -> Self { Self::new() } -} - -impl From> for HashMap, Vec> { - fn from(tex: TestExternalities) -> Self { - tex.inner.into() - } -} - -impl From< HashMap, Vec> > for TestExternalities { - fn from(hashmap: HashMap, Vec>) -> Self { - TestExternalities { inner: hashmap, _hasher: PhantomData } - } -} - - -impl Externalities for TestExternalities where H::Out: Ord + Encodable { - fn storage(&self, key: &[u8]) -> Option> { - self.inner.get(key).map(|x| x.to_vec()) - } - - fn place_storage(&mut self, key: Vec, maybe_value: Option>) { - match maybe_value { - Some(value) => { self.inner.insert(key, value); } - None => { self.inner.remove(&key); } - } - } - - fn clear_prefix(&mut self, prefix: &[u8]) { - self.inner.retain(|key, _| - !key.starts_with(prefix) - ) - } - - fn chain_id(&self) -> u64 { 42 } - - fn storage_root(&mut self) -> H::Out { - trie_root::(self.inner.clone()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use primitives::{Blake2Hasher, H256}; - - #[test] - fn commit_should_work() { - let mut ext = TestExternalities::::new(); - ext.set_storage(b"doe".to_vec(), b"reindeer".to_vec()); - ext.set_storage(b"dog".to_vec(), b"puppy".to_vec()); - ext.set_storage(b"dogglesworth".to_vec(), b"cat".to_vec()); - const ROOT: [u8; 32] = hex!("6ca394ff9b13d6690a51dea30b1b5c43108e52944d30b9095227c49bae03ff8b"); - assert_eq!(ext.storage_root(), H256(ROOT)); - } -} diff --git a/substrate/state-machine/src/trie_backend.rs b/substrate/state-machine/src/trie_backend.rs deleted file mode 100644 index 565cc5387f486..0000000000000 --- a/substrate/state-machine/src/trie_backend.rs +++ /dev/null @@ -1,362 +0,0 @@ -// Copyright 2017 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 . - -//! Trie-based state machine backend. -use Backend; -use hashdb::{Hasher, HashDB, AsHashDB}; -use memorydb::MemoryDB; -use patricia_trie::{TrieDB, TrieDBMut, TrieError, Trie, TrieMut, NodeCodec}; -use std::collections::HashMap; -use std::sync::Arc; -use std::marker::PhantomData; -use heapsize::HeapSizeOf; - -pub use hashdb::DBValue; - -/// Backend trie storage trait. -pub trait Storage: Send + Sync { - /// Get a trie node. - fn get(&self, key: &H::Out) -> Result, String>; -} - -/// Try convert into trie-based backend. -pub trait TryIntoTrieBackend> { - /// Try to convert self into trie backend. - fn try_into_trie_backend(self) -> Option>; -} - -/// Patricia trie-based backend. Transaction type is an overlay of changes to commit. -#[derive(Clone)] -pub struct TrieBackend> { - storage: TrieBackendStorage, - root: H::Out, - _codec: PhantomData -} - -impl> TrieBackend where H::Out: HeapSizeOf { - /// Create new trie-based backend. - pub fn with_storage(db: Arc>, root: H::Out) -> Self { - TrieBackend { - storage: TrieBackendStorage::Storage(db), - root, - _codec: PhantomData, - } - } - - /// Create new trie-based backend for genesis block. - pub fn with_storage_for_genesis(db: Arc>) -> Self { - let mut root = ::Out::default(); - let mut mdb = MemoryDB::::new(); - TrieDBMut::::new(&mut mdb, &mut root); - - Self::with_storage(db, root) - } - - /// Create new trie-based backend backed by MemoryDb storage. - pub fn with_memorydb(db: MemoryDB, root: H::Out) -> Self { - // TODO: check that root is a part of db??? - TrieBackend { - storage: TrieBackendStorage::MemoryDb(db), - root, - _codec: PhantomData, - } - } - - /// Get backend storage reference. - pub fn backend_storage(&self) -> &TrieBackendStorage { - &self.storage - } - - /// Get trie root. - pub fn root(&self) -> &H::Out { - &self.root - } -} - -impl super::Error for String {} - -impl> Backend for TrieBackend where H::Out: HeapSizeOf { - type Error = String; - type Transaction = MemoryDB; - - fn storage(&self, key: &[u8]) -> Result>, Self::Error> { - let mut read_overlay = MemoryDB::new(); - let eph = Ephemeral { - storage: &self.storage, - overlay: &mut read_overlay, - }; - - let map_e = |e| format!("Trie lookup error: {}", e); - TrieDB::::new(&eph, &self.root).map_err(map_e)? - .get(key).map(|x| x.map(|val| val.to_vec())).map_err(map_e) - } - - fn for_keys_with_prefix(&self, prefix: &[u8], mut f: F) { - let mut read_overlay = MemoryDB::new(); - let eph = Ephemeral { - storage: &self.storage, - overlay: &mut read_overlay, - }; - - let mut iter = move || -> Result<(), Box>> { - let trie = TrieDB::::new(&eph, &self.root)?; - let mut iter = trie.iter()?; - - iter.seek(prefix)?; - - for x in iter { - let (key, _) = x?; - - if !key.starts_with(prefix) { - break; - } - - f(&key); - } - - Ok(()) - }; - - if let Err(e) = iter() { - debug!(target: "trie", "Error while iterating by prefix: {}", e); - } - } - - fn pairs(&self) -> Vec<(Vec, Vec)> { - let mut read_overlay = MemoryDB::new(); - let eph = Ephemeral { - storage: &self.storage, - overlay: &mut read_overlay, - }; - - let collect_all = || -> Result<_, Box>> { - let trie = TrieDB::::new(&eph, &self.root)?; - let mut v = Vec::new(); - for x in trie.iter()? { - let (key, value) = x?; - v.push((key.to_vec(), value.to_vec())); - } - - Ok(v) - }; - - match collect_all() { - Ok(v) => v, - Err(e) => { - debug!(target: "trie", "Error extracting trie values: {}", e); - Vec::new() - } - } - } - - fn storage_root(&self, delta: I) -> (H::Out, MemoryDB) - where I: IntoIterator, Option>)> - { - let mut write_overlay = MemoryDB::new(); - let mut root = self.root; - { - let mut eph = Ephemeral { - storage: &self.storage, - overlay: &mut write_overlay, - }; - - let mut trie = TrieDBMut::::from_existing(&mut eph, &mut root).expect("prior state root to exist"); // TODO: handle gracefully - for (key, change) in delta { - let result = match change { - Some(val) => trie.insert(&key, &val), - None => trie.remove(&key), // TODO: archive mode - }; - - if let Err(e) = result { - warn!(target: "trie", "Failed to write to trie: {}", e); - } - } - } - - (root, write_overlay) - } -} - -impl> TryIntoTrieBackend for TrieBackend { - fn try_into_trie_backend(self) -> Option> { - Some(self) - } -} - -pub struct Ephemeral<'a, H: 'a + Hasher> { - storage: &'a TrieBackendStorage, - overlay: &'a mut MemoryDB, -} - -impl<'a, H: Hasher> AsHashDB for Ephemeral<'a, H> where H::Out: HeapSizeOf { - fn as_hashdb(&self) -> &HashDB { self } - fn as_hashdb_mut(&mut self) -> &mut HashDB { self } -} - -impl<'a, H: Hasher> Ephemeral<'a, H> { - pub fn new(storage: &'a TrieBackendStorage, overlay: &'a mut MemoryDB) -> Self { - Ephemeral { - storage, - overlay, - } - } -} - -impl<'a, H: Hasher> HashDB for Ephemeral<'a, H> where H::Out: HeapSizeOf { - fn keys(&self) -> HashMap { - self.overlay.keys() // TODO: iterate backing - } - - fn get(&self, key: &H::Out) -> Option { - match self.overlay.raw(key) { - Some((val, i)) => { - if i <= 0 { - None - } else { - Some(val) - } - } - None => match self.storage.get(key) { - Ok(x) => x, - Err(e) => { - warn!(target: "trie", "Failed to read from DB: {}", e); - None - }, - }, - } - } - - fn contains(&self, key: &H::Out) -> bool { - self.get(key).is_some() - } - - fn insert(&mut self, value: &[u8]) -> H::Out { - self.overlay.insert(value) - } - - fn emplace(&mut self, key: H::Out, value: DBValue) { - self.overlay.emplace(key, value) - } - - fn remove(&mut self, key: &H::Out) { - self.overlay.remove(key) - } -} - -#[derive(Clone)] -pub enum TrieBackendStorage { - /// Key value db + storage column. - Storage(Arc>), - /// Hash db. - MemoryDb(MemoryDB), -} - -impl TrieBackendStorage { - pub fn get(&self, key: &H::Out) -> Result, String> { - match *self { - TrieBackendStorage::Storage(ref db) => - db.get(key) - .map_err(|e| format!("Trie lookup error: {}", e)), - TrieBackendStorage::MemoryDb(ref db) => - Ok(db.get(key)), - } - } -} - -#[cfg(test)] -pub mod tests { - use super::*; - use std::collections::HashSet; - use primitives::{Blake2Hasher, RlpCodec, H256}; - - fn test_db() -> (MemoryDB, H256) { - let mut root = H256::default(); - let mut mdb = MemoryDB::::new(); - { - let mut trie = TrieDBMut::<_, RlpCodec>::new(&mut mdb, &mut root); - trie.insert(b"key", b"value").expect("insert failed"); - trie.insert(b"value1", &[42]).expect("insert failed"); - trie.insert(b"value2", &[24]).expect("insert failed"); - trie.insert(b":code", b"return 42").expect("insert failed"); - for i in 128u8..255u8 { - trie.insert(&[i], &[i]).unwrap(); - } - } - (mdb, root) - } - - pub(crate) fn test_trie() -> TrieBackend { - let (mdb, root) = test_db(); - TrieBackend::with_memorydb(mdb, root) - } - - #[test] - fn read_from_storage_returns_some() { - assert_eq!(test_trie().storage(b"key").unwrap(), Some(b"value".to_vec())); - } - - #[test] - fn read_from_storage_returns_none() { - assert_eq!(test_trie().storage(b"non-existing-key").unwrap(), None); - } - - #[test] - fn pairs_are_not_empty_on_non_empty_storage() { - assert!(!test_trie().pairs().is_empty()); - } - - #[test] - fn pairs_are_empty_on_empty_storage() { - let db = TrieBackend::::with_memorydb( - MemoryDB::new(), - Default::default() - ); - assert!(db.pairs().is_empty()); - } - - #[test] - fn storage_root_is_non_default() { - assert!(test_trie().storage_root(::std::iter::empty()).0 != H256([0; 32])); - } - - #[test] - fn storage_root_transaction_is_empty() { - assert!(test_trie().storage_root(::std::iter::empty()).1.drain().is_empty()); - } - - #[test] - fn storage_root_transaction_is_non_empty() { - let (new_root, mut tx) = test_trie().storage_root(vec![(b"new-key".to_vec(), Some(b"new-value".to_vec()))]); - assert!(!tx.drain().is_empty()); - assert!(new_root != test_trie().storage_root(::std::iter::empty()).0); - } - - #[test] - fn prefix_walking_works() { - let trie = test_trie(); - - let mut seen = HashSet::new(); - trie.for_keys_with_prefix(b"value", |key| { - let for_first_time = seen.insert(key.to_vec()); - assert!(for_first_time, "Seen key '{:?}' more than once", key); - }); - - let mut expected = HashSet::new(); - expected.insert(b"value1".to_vec()); - expected.insert(b"value2".to_vec()); - assert_eq!(seen, expected); - } -} diff --git a/substrate/telemetry/Cargo.toml b/substrate/telemetry/Cargo.toml deleted file mode 100644 index 2e4e8db6d6bd3..0000000000000 --- a/substrate/telemetry/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "substrate-telemetry" -version = "0.3.0" -authors = ["Parity Technologies "] -description = "Telemetry utils" - -[dependencies] -parking_lot = "0.4" -lazy_static = "1.0" -log = "0.3" -slog = "^2" -slog-json = "^2" -slog-async = "^2" -slog-scope = "^4" -websocket = "^0.20" diff --git a/substrate/telemetry/README.adoc b/substrate/telemetry/README.adoc deleted file mode 100644 index 9759c5bc50bdd..0000000000000 --- a/substrate/telemetry/README.adoc +++ /dev/null @@ -1,13 +0,0 @@ - -= Telemetry - -.Summary -[source, toml] ----- -include::Cargo.toml[lines=2..5] ----- - -.Description ----- -include::src/lib.rs[tag=description] ----- diff --git a/substrate/telemetry/src/lib.rs b/substrate/telemetry/src/lib.rs deleted file mode 100644 index 21e8685b36031..0000000000000 --- a/substrate/telemetry/src/lib.rs +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -// tag::description[] -//! Telemetry utils. -//! -//! `telemetry` macro be used from whereever in the Substrate codebase -//! in order to send real-time logging information to the telemetry -//! server (if there is one). We use the async drain adapter of `slog` -//! so that the logging thread doesn't get held up at all. -// end::description[] - -extern crate parking_lot; -extern crate websocket as ws; -extern crate slog_async; -extern crate slog_json; -#[macro_use] -extern crate log; -#[macro_use(o, kv)] -extern crate slog; -extern crate slog_scope; - -use std::{io, time}; -use parking_lot::Mutex; -use slog::Drain; -pub use slog_scope::with_logger; - -/// Configuration for telemetry. -pub struct TelemetryConfig { - /// URL of the telemetry WebSocket server. - pub url: String, - /// What do do when we connect to the server. - pub on_connect: Box, -} - -/// Telemetry service guard. -pub type Telemetry = slog_scope::GlobalLoggerGuard; - -/// Size of the channel for passing messages to telemetry thread. -const CHANNEL_SIZE: usize = 262144; - -/// Initialise telemetry. -pub fn init_telemetry(config: TelemetryConfig) -> slog_scope::GlobalLoggerGuard { - let client = ws::ClientBuilder::new(&config.url).ok().and_then(|mut x| x.connect(None).ok()); - let log = slog::Logger::root( - slog_async::Async::new( - slog_json::Json::default( - TelemetryWriter { - buffer: vec![], - out: Mutex::new(client), - config, - last_time: None, // ensures that on_connect will be called. - } - ).fuse() - ).chan_size(CHANNEL_SIZE) - .overflow_strategy(slog_async::OverflowStrategy::DropAndReport) - .build().fuse(), o!() - ); - slog_scope::set_global_logger(log) -} - -/// Exactly equivalent to `slog_scope::info`, provided as a convenience. -#[macro_export] -macro_rules! telemetry { - ( $($t:tt)* ) => { $crate::with_logger(|l| slog_info!(l, $($t)* )) } -} - -struct TelemetryWriter { - buffer: Vec, - out: Mutex>>>, - config: TelemetryConfig, - last_time: Option, -} - -/// Every two minutes we reconnect to the telemetry server otherwise we don't get notified -/// of a flakey connection that has been dropped and needs to be reconnected. We can remove -/// this once we introduce a keepalive ping/pong. -const RECONNECT_PERIOD: u64 = 120; - -impl TelemetryWriter { - fn ensure_connected(&mut self) { - let mut client = self.out.lock(); - - let controlled_disconnect = if let Some(t) = self.last_time { - if t.elapsed().as_secs() > RECONNECT_PERIOD && client.is_some() { - trace!(target: "telemetry", "Performing controlled drop of the telemetry connection."); - let _ = client.as_mut().and_then(|socket| - socket.send_message(&ws::Message::text("{\"msg\":\"system.reconnect\"}")).ok() - ); - *client = None; - true - } else { - false - } - } else { - false - }; - - let just_connected = if client.is_none() { - if !controlled_disconnect { - info!(target: "telemetry", "Connection dropped unexpectedly. Reconnecting to telemetry server..."); - } - *client = ws::ClientBuilder::new(&self.config.url).ok().and_then(|mut x| x.connect(None).ok()); - client.is_some() - } else { - self.last_time.is_none() - }; - - drop(client); - if just_connected { - if !controlled_disconnect { - info!("Reconnected to telemetry server: {}", self.config.url); - } - self.last_time = Some(time::Instant::now()); - (self.config.on_connect)(); - } - } -} - -impl io::Write for TelemetryWriter { - fn write(&mut self, msg: &[u8]) -> io::Result { - if msg.iter().any(|x| *x == b'\n') { - let _ = self.flush(); - } else { - self.buffer.extend_from_slice(msg); - } - Ok(msg.len()) - } - - fn flush(&mut self) -> io::Result<()> { - self.ensure_connected(); - - let mut l = self.out.lock(); - let socket_closed = if let Some(ref mut socket) = *l { - if let Ok(s) = ::std::str::from_utf8(&self.buffer[..]) { - let r = socket.send_message(&ws::Message::text(s)); - trace!(target: "telemetry", "Sent to telemetry: {} -> {:?}", s, r); - r.is_err() - } else { false } - } else { false }; - if socket_closed { - *l = None; - } - self.buffer.clear(); - Ok(()) - } -} diff --git a/substrate/test-client/Cargo.toml b/substrate/test-client/Cargo.toml deleted file mode 100644 index f36ec382f6028..0000000000000 --- a/substrate/test-client/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "substrate-test-client" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -rhododendron = "0.3" -substrate-bft = { path = "../bft" } -substrate-client = { path = "../client" } -substrate-codec = { path = "../codec" } -substrate-executor = { path = "../executor" } -substrate-keyring = { path = "../../substrate/keyring" } -substrate-primitives = { path = "../primitives" } -substrate-runtime-support = { path = "../runtime-support" } -substrate-test-runtime = { path = "../test-runtime" } -substrate-runtime-primitives = { path = "../runtime/primitives" } -hashdb = "0.2.1" - diff --git a/substrate/test-client/README.adoc b/substrate/test-client/README.adoc deleted file mode 100644 index e56c4c7f66e7c..0000000000000 --- a/substrate/test-client/README.adoc +++ /dev/null @@ -1,13 +0,0 @@ - -= Test client - -.Summary -[source, toml] ----- -include::Cargo.toml[lines=2..5] ----- - -.Description ----- -include::src/lib.rs[tag=description] ----- diff --git a/substrate/test-client/src/block_builder_ext.rs b/substrate/test-client/src/block_builder_ext.rs deleted file mode 100644 index 4cc0854771a2c..0000000000000 --- a/substrate/test-client/src/block_builder_ext.rs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! Block Builder extensions for tests. - -use codec; -use client; -use keyring; -use runtime; - -use {Backend, Executor}; -use primitives::{Blake2Hasher, RlpCodec}; - -/// Extension trait for test block builder. -pub trait BlockBuilderExt { - /// Add transfer extrinsic to the block. - fn push_transfer(&mut self, transfer: runtime::Transfer) -> Result<(), client::error::Error>; -} - -impl BlockBuilderExt for client::block_builder::BlockBuilder { - fn push_transfer(&mut self, transfer: runtime::Transfer) -> Result<(), client::error::Error> { - self.push(sign_tx(transfer)) - } -} - -fn sign_tx(transfer: runtime::Transfer) -> runtime::Extrinsic { - let signature = keyring::Keyring::from_raw_public(transfer.from.0.clone()).unwrap().sign(&codec::Encode::encode(&transfer)).into(); - runtime::Extrinsic { transfer, signature } -} diff --git a/substrate/test-client/src/client_ext.rs b/substrate/test-client/src/client_ext.rs deleted file mode 100644 index 386df6915830e..0000000000000 --- a/substrate/test-client/src/client_ext.rs +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! Client extension for tests. - -use client::{self, Client}; -use keyring::Keyring; -use runtime_primitives::StorageMap; -use runtime::genesismap::{GenesisConfig, additional_storage_with_genesis}; -use executor::NativeExecutor; -use runtime; -use bft; -use {Backend, Executor}; - -/// Extension trait for a test client. -pub trait TestClient { - /// Crates new client instance for tests. - fn new_for_tests() -> Self; - - /// Justify and import block to the chain. - fn justify_and_import(&self, origin: client::BlockOrigin, block: runtime::Block) -> client::error::Result<()>; - - /// Returns hash of the genesis block. - fn genesis_hash(&self) -> runtime::Hash; -} - -impl TestClient for Client { - fn new_for_tests() -> Self { - client::new_in_mem(NativeExecutor::new(), genesis_storage()).unwrap() - } - - fn justify_and_import(&self, origin: client::BlockOrigin, block: runtime::Block) -> client::error::Result<()> { - let justification = fake_justify(&block.header); - let justified = self.check_justification(block.header, justification)?; - self.import_block(origin, justified, Some(block.extrinsics))?; - - Ok(()) - } - - fn genesis_hash(&self) -> runtime::Hash { - self.block_hash(0).unwrap().unwrap() - } -} - -/// Prepare fake justification for the header. -/// -/// since we are in the client module we can create falsely justified -/// headers. -/// TODO: remove this in favor of custom verification pipelines for the -/// client -fn fake_justify(header: &runtime::Header) -> bft::UncheckedJustification { - let hash = header.hash(); - let authorities = vec![ - Keyring::Alice.into(), - Keyring::Bob.into(), - Keyring::Charlie.into(), - ]; - - bft::UncheckedJustification::new( - hash, - authorities.iter().map(|key| { - let msg = bft::sign_message::( - ::rhododendron::Vote::Commit(1, hash).into(), - key, - header.parent_hash - ); - - match msg { - ::rhododendron::LocalizedMessage::Vote(vote) => vote.signature, - _ => panic!("signing vote leads to signed vote"), - } - }).collect(), - 1, - ) -} - -fn genesis_config() -> GenesisConfig { - GenesisConfig::new_simple(vec![ - Keyring::Alice.to_raw_public().into(), - Keyring::Bob.to_raw_public().into(), - Keyring::Charlie.to_raw_public().into(), - ], 1000) -} - -fn genesis_storage() -> StorageMap { - let mut storage = genesis_config().genesis_map(); - let block: runtime::Block = client::genesis::construct_genesis_block(&storage); - storage.extend(additional_storage_with_genesis(&block)); - storage -} diff --git a/substrate/test-client/src/lib.rs b/substrate/test-client/src/lib.rs deleted file mode 100644 index 3aeefed0fafb2..0000000000000 --- a/substrate/test-client/src/lib.rs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -// tag::description[] -//! Client testing utilities. -// end::description[] - -#![warn(missing_docs)] - -extern crate rhododendron; -extern crate substrate_bft as bft; -extern crate substrate_codec as codec; -extern crate substrate_primitives as primitives; -extern crate substrate_runtime_support as runtime_support; -extern crate substrate_runtime_primitives as runtime_primitives; -#[macro_use] extern crate substrate_executor as executor; -extern crate hashdb; - -pub extern crate substrate_client as client; -pub extern crate substrate_keyring as keyring; -pub extern crate substrate_test_runtime as runtime; - -mod client_ext; -mod block_builder_ext; - -pub use client_ext::TestClient; -pub use block_builder_ext::BlockBuilderExt; - -use primitives::{Blake2Hasher, RlpCodec}; - -mod local_executor { - #![allow(missing_docs)] - use super::runtime; - // TODO: change the macro and pass in the `BlakeHasher` that dispatch needs from here instead - native_executor_instance!(pub LocalExecutor, runtime::api::dispatch, runtime::VERSION, include_bytes!("../../test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm")); -} - -/// Native executor used for tests. -pub use local_executor::LocalExecutor; - -/// Test client database backend. -pub type Backend = client::in_mem::Backend; - -/// Test client executor. -pub type Executor = client::LocalCallExecutor< - Backend, - executor::NativeExecutor, ->; - -/// Creates new client instance used for tests. -pub fn new() -> client::Client { - TestClient::new_for_tests() -} diff --git a/substrate/test-runtime/Cargo.toml b/substrate/test-runtime/Cargo.toml deleted file mode 100644 index 5482936f05733..0000000000000 --- a/substrate/test-runtime/Cargo.toml +++ /dev/null @@ -1,38 +0,0 @@ -[package] -name = "substrate-test-runtime" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -log = { version = "0.3", optional = true } -hex-literal = { version = "0.1.0", optional = true } -ed25519 = { path = "../ed25519", optional = true } -serde = { version = "1.0", optional = true } -serde_derive = { version = "1.0", optional = true } -substrate-keyring = { path = "../keyring", optional = true } -substrate-codec = { path = "../codec", default-features = false } -substrate-codec-derive = { path = "../codec/derive", default-features = false } -substrate-runtime-std = { path = "../runtime-std", default-features = false } -substrate-runtime-io = { path = "../runtime-io", default-features = false } -substrate-runtime-support = { path = "../runtime-support", default-features = false } -substrate-primitives = { path = "../primitives", default-features = false } -substrate-runtime-primitives = { path = "../runtime/primitives", default-features = false } -substrate-runtime-version = { path = "../runtime/version", default-features = false } - -[features] -default = ["std"] -std = [ - "log", - "hex-literal", - "ed25519", - "serde", - "serde_derive", - "substrate-keyring", - "substrate-codec/std", - "substrate-runtime-std/std", - "substrate-runtime-io/std", - "substrate-runtime-support/std", - "substrate-primitives/std", - "substrate-runtime-primitives/std", - "substrate-runtime-version/std" -] diff --git a/substrate/test-runtime/README.adoc b/substrate/test-runtime/README.adoc deleted file mode 100644 index 15b3c4c4ac327..0000000000000 --- a/substrate/test-runtime/README.adoc +++ /dev/null @@ -1,13 +0,0 @@ - -= Test runtime - -.Summary -[source, toml] ----- -include::Cargo.toml[lines=2..5] ----- - -.Description ----- -include::src/lib.rs[tag=description] ----- diff --git a/substrate/test-runtime/src/genesismap.rs b/substrate/test-runtime/src/genesismap.rs deleted file mode 100644 index fe5fd84acd014..0000000000000 --- a/substrate/test-runtime/src/genesismap.rs +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2017 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 . - -//! Tool for creating the genesis block. - -use std::collections::HashMap; -use runtime_io::twox_128; -use codec::{KeyedVec, Joiner}; -use primitives::AuthorityId; -use runtime_primitives::traits::Block; - -/// Configuration of a general Substrate test genesis block. -pub struct GenesisConfig { - pub authorities: Vec, - pub balances: Vec<(AuthorityId, u64)>, -} - -impl GenesisConfig { - pub fn new_simple(authorities: Vec, balance: u64) -> Self { - GenesisConfig { - authorities: authorities.clone(), - balances: authorities.into_iter().map(|a| (a, balance)).collect(), - } - } - - pub fn genesis_map(&self) -> HashMap, Vec> { - let wasm_runtime = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm").to_vec(); - self.balances.iter() - .map(|&(account, balance)| (account.to_keyed_vec(b"balance:"), vec![].and(&balance))) - .map(|(k, v)| (twox_128(&k[..])[..].to_vec(), v.to_vec())) - .chain(vec![ - (b":code"[..].into(), wasm_runtime), - (b":heappages"[..].into(), vec![].and(&(16 as u64))), - (b":auth:len"[..].into(), vec![].and(&(self.authorities.len() as u32))), - ].into_iter()) - .chain(self.authorities.iter() - .enumerate() - .map(|(i, account)| ((i as u32).to_keyed_vec(b":auth:"), vec![].and(account))) - ) - .collect() - } -} - -macro_rules! map { - ($( $name:expr => $value:expr ),*) => ( - vec![ $( ( $name, $value ) ),* ].into_iter().collect() - ) -} - -pub fn additional_storage_with_genesis(genesis_block: &::Block) -> HashMap, Vec> { - map![ - twox_128(&b"latest"[..]).to_vec() => genesis_block.hash().0.to_vec() - ] -} diff --git a/substrate/test-runtime/src/lib.rs b/substrate/test-runtime/src/lib.rs deleted file mode 100644 index 5f9712aa7167b..0000000000000 --- a/substrate/test-runtime/src/lib.rs +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -// tag::description[] -//! The Substrate runtime. This can be compiled with #[no_std], ready for Wasm. -// end::description[] - -#![cfg_attr(not(feature = "std"), no_std)] - -extern crate substrate_runtime_std as rstd; -extern crate substrate_codec as codec; -extern crate substrate_runtime_primitives as runtime_primitives; - -#[cfg(feature = "std")] -extern crate serde; - -#[cfg(feature = "std")] -#[macro_use] -extern crate serde_derive; - -#[macro_use] -extern crate substrate_runtime_support as runtime_support; -#[macro_use] -extern crate substrate_codec_derive; -#[macro_use] -extern crate substrate_runtime_io as runtime_io; -#[macro_use] -extern crate substrate_runtime_version as runtime_version; - - -#[cfg(test)] -#[macro_use] -extern crate hex_literal; -#[cfg(test)] -extern crate ed25519; -#[cfg(test)] -extern crate substrate_keyring as keyring; -#[cfg_attr(test, macro_use)] -extern crate substrate_primitives as primitives; - -#[cfg(feature = "std")] pub mod genesismap; -pub mod system; - -use rstd::prelude::*; -use codec::{Encode, Decode}; - -use runtime_primitives::traits::{BlindCheckable, BlakeTwo256}; -use runtime_primitives::Ed25519Signature; -use runtime_version::RuntimeVersion; -pub use primitives::hash::H256; - -/// Test runtime version. -pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: ver_str!("test"), - impl_name: ver_str!("parity-test"), - authoring_version: 1, - spec_version: 1, - impl_version: 1, -}; - -fn version() -> RuntimeVersion { - VERSION -} - -/// Calls in transactions. -#[derive(Clone, PartialEq, Eq, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] -pub struct Transfer { - pub from: AccountId, - pub to: AccountId, - pub amount: u64, - pub nonce: u64, -} - -/// Extrinsic for test-runtime. -#[derive(Clone, PartialEq, Eq, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] -pub struct Extrinsic { - pub transfer: Transfer, - pub signature: Ed25519Signature, -} - -impl BlindCheckable for Extrinsic { - type Checked = Self; - - fn check(self) -> Result { - if ::runtime_primitives::verify_encoded_lazy(&self.signature, &self.transfer, &self.transfer.from) { - Ok(self) - } else { - Err("bad signature") - } - } -} - -/// An identifier for an account on this system. -pub type AccountId = H256; -/// A simple hash type for all our hashing. -pub type Hash = H256; -/// The block number type used in this runtime. -pub type BlockNumber = u64; -/// Index of a transaction. -pub type Index = u64; -/// The item of a block digest. -pub type DigestItem = runtime_primitives::generic::DigestItem; -/// The digest of a block. -pub type Digest = runtime_primitives::generic::Digest; -/// A test block. -pub type Block = runtime_primitives::generic::Block; -/// A test block's header. -pub type Header = runtime_primitives::generic::Header; - -/// Run whatever tests we have. -pub fn run_tests(mut input: &[u8]) -> Vec { - use runtime_io::print; - - print("run_tests..."); - let block = Block::decode(&mut input).unwrap(); - print("deserialised block."); - let stxs = block.extrinsics.iter().map(Encode::encode).collect::>(); - print("reserialised transactions."); - [stxs.len() as u8].encode() -} - -fn test_event_json() -> &'static str { - "hallo" -} - -pub mod api { - use system; - impl_stubs!( - version => |()| super::version(), - json_metadata => |()| { - let mut vec = ::runtime_support::metadata::Vec::new(); - vec.push(::runtime_support::metadata::JSONMetadata::Events { - name: "Test", events: &[ ("event", super::test_event_json) ] - }); - vec - }, - authorities => |()| system::authorities(), - initialise_block => |header| system::initialise_block(header), - execute_block => |block| system::execute_block(block), - apply_extrinsic => |utx| system::execute_transaction(utx), - finalise_block => |()| system::finalise_block() - ); -} diff --git a/substrate/test-runtime/src/system.rs b/substrate/test-runtime/src/system.rs deleted file mode 100644 index b60cfa7872c89..0000000000000 --- a/substrate/test-runtime/src/system.rs +++ /dev/null @@ -1,281 +0,0 @@ -// Copyright 2017 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 . - -//! System manager: Handles all of the top-level stuff; executing block/transaction, setting code -//! and depositing logs. - -use rstd::prelude::*; -use runtime_io::{storage_root, enumerated_trie_root}; -use runtime_support::storage::{self, StorageValue, StorageMap}; -use runtime_primitives::traits::{Hash as HashT, BlakeTwo256}; -use runtime_primitives::{ApplyError, ApplyOutcome, ApplyResult}; -use codec::{KeyedVec, Encode}; -use super::{AccountId, BlockNumber, Extrinsic, H256 as Hash, Block, Header}; -use primitives::Blake2Hasher; - -const NONCE_OF: &[u8] = b"nonce:"; -const BALANCE_OF: &[u8] = b"balance:"; -const AUTHORITY_AT: &'static[u8] = b":auth:"; -const AUTHORITY_COUNT: &'static[u8] = b":auth:len"; - -storage_items! { - ExtrinsicIndex: b"sys:xti" => required u32; - ExtrinsicData: b"sys:xtd" => required map [ u32 => Vec ]; - // The current block number being processed. Set by `execute_block`. - Number: b"sys:num" => required BlockNumber; - ParentHash: b"sys:pha" => required Hash; -} - -pub fn balance_of(who: AccountId) -> u64 { - storage::get_or(&who.to_keyed_vec(BALANCE_OF), 0) -} - -pub fn nonce_of(who: AccountId) -> u64 { - storage::get_or(&who.to_keyed_vec(NONCE_OF), 0) -} - -/// Get authorities ar given block. -pub fn authorities() -> Vec<::primitives::AuthorityId> { - let len: u32 = storage::unhashed::get(AUTHORITY_COUNT).expect("There are always authorities in test-runtime"); - (0..len) - .map(|i| storage::unhashed::get(&i.to_keyed_vec(AUTHORITY_AT)).expect("Authority is properly encoded in test-runtime")) - .collect() -} - -pub fn initialise_block(header: Header) { - // populate environment. - ::put(&header.number); - ::put(&header.parent_hash); - ::put(0); -} - -/// Actually execute all transitioning for `block`. -pub fn execute_block(block: Block) { - let ref header = block.header; - - // check transaction trie root represents the transactions. - let txs = block.extrinsics.iter().map(Encode::encode).collect::>(); - let txs = txs.iter().map(Vec::as_slice).collect::>(); - let txs_root = enumerated_trie_root::(&txs).into(); - info_expect_equal_hash(&txs_root, &header.extrinsics_root); - assert!(txs_root == header.extrinsics_root, "Transaction trie root must be valid."); - - // execute transactions - block.extrinsics.iter().for_each(|e| { execute_transaction_backend(e).map_err(|_| ()).expect("Extrinsic error"); }); - - // check storage root. - let storage_root = storage_root().into(); - info_expect_equal_hash(&storage_root, &header.state_root); - assert!(storage_root == header.state_root, "Storage root must match that calculated."); -} - -/// Execute a transaction outside of the block execution function. -/// This doesn't attempt to validate anything regarding the block. -pub fn execute_transaction(utx: Extrinsic) -> ApplyResult { - let extrinsic_index = ExtrinsicIndex::get(); - ExtrinsicData::insert(extrinsic_index, utx.encode()); - ExtrinsicIndex::put(extrinsic_index + 1); - execute_transaction_backend(&utx) -} - -/// Finalise the block. -pub fn finalise_block() -> Header { - let extrinsic_index = ExtrinsicIndex::take(); - let txs: Vec<_> = (0..extrinsic_index).map(ExtrinsicData::take).collect(); - let txs = txs.iter().map(Vec::as_slice).collect::>(); - let extrinsics_root = enumerated_trie_root::(&txs).into(); - - let number = ::take(); - let parent_hash = ::take(); - let storage_root = BlakeTwo256::storage_root(); - - Header { - number, - extrinsics_root, - state_root: storage_root, - parent_hash, - digest: Default::default(), - } -} - -fn execute_transaction_backend(utx: &Extrinsic) -> ApplyResult { - use runtime_primitives::traits::BlindCheckable; - - // check signature - let utx = match utx.clone().check() { - Ok(tx) => tx, - Err(_) => return Err(ApplyError::BadSignature), - }; - - let tx: ::Transfer = utx.transfer; - - // check nonce - let nonce_key = tx.from.to_keyed_vec(NONCE_OF); - let expected_nonce: u64 = storage::get_or(&nonce_key, 0); - if !(tx.nonce == expected_nonce) { - return Err(ApplyError::Stale) - } - - // increment nonce in storage - storage::put(&nonce_key, &(expected_nonce + 1)); - - // check sender balance - let from_balance_key = tx.from.to_keyed_vec(BALANCE_OF); - let from_balance: u64 = storage::get_or(&from_balance_key, 0); - - // enact transfer - if !(tx.amount <= from_balance) { - return Err(ApplyError::CantPay) - } - let to_balance_key = tx.to.to_keyed_vec(BALANCE_OF); - let to_balance: u64 = storage::get_or(&to_balance_key, 0); - storage::put(&from_balance_key, &(from_balance - tx.amount)); - storage::put(&to_balance_key, &(to_balance + tx.amount)); - Ok(ApplyOutcome::Success) -} - -#[cfg(feature = "std")] -fn info_expect_equal_hash(given: &Hash, expected: &Hash) { - use primitives::hexdisplay::HexDisplay; - if given != expected { - println!("Hash: given={}, expected={}", HexDisplay::from(&given.0), HexDisplay::from(&expected.0)); - } -} - -#[cfg(not(feature = "std"))] -fn info_expect_equal_hash(given: &Hash, expected: &Hash) { - if given != expected { - ::runtime_io::print("Hash not equal"); - ::runtime_io::print(&given.0[..]); - ::runtime_io::print(&expected.0[..]); - } -} - -#[cfg(test)] -mod tests { - use super::*; - - use runtime_io::{with_externalities, twox_128, TestExternalities}; - use codec::{Joiner, KeyedVec}; - use keyring::Keyring; - use ::{Header, Digest, Extrinsic, Transfer}; - use primitives::Blake2Hasher; - - fn new_test_ext() -> TestExternalities { - map![ - twox_128(b"latest").to_vec() => vec![69u8; 32], - twox_128(b":auth:len").to_vec() => vec![].and(&3u32), - twox_128(&0u32.to_keyed_vec(b":auth:")).to_vec() => Keyring::Alice.to_raw_public().to_vec(), - twox_128(&1u32.to_keyed_vec(b":auth:")).to_vec() => Keyring::Bob.to_raw_public().to_vec(), - twox_128(&2u32.to_keyed_vec(b":auth:")).to_vec() => Keyring::Charlie.to_raw_public().to_vec(), - twox_128(&Keyring::Alice.to_raw_public().to_keyed_vec(b"balance:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0] - ] - } - - fn construct_signed_tx(tx: Transfer) -> Extrinsic { - let signature = Keyring::from_raw_public(tx.from.0).unwrap().sign(&tx.encode()).into(); - Extrinsic { transfer: tx, signature } - } - - #[test] - fn block_import_works() { - let mut t = new_test_ext(); - - let h = Header { - parent_hash: [69u8; 32].into(), - number: 1, - state_root: hex!("0c22599e15fb5e052c84f79a2aab179ba6bb238218fd86bdd4a74ebcc87adfcd").into(), - extrinsics_root: hex!("45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0").into(), - digest: Digest { logs: vec![], }, - }; - - let b = Block { - header: h, - extrinsics: vec![], - }; - - with_externalities(&mut t, || { - execute_block(b); - }); - } - - #[test] - fn block_import_with_transaction_works() { - let mut t = new_test_ext(); - - with_externalities(&mut t, || { - assert_eq!(balance_of(Keyring::Alice.to_raw_public().into()), 111); - assert_eq!(balance_of(Keyring::Bob.to_raw_public().into()), 0); - }); - - let b = Block { - header: Header { - parent_hash: [69u8; 32].into(), - number: 1, - state_root: hex!("0425393fd07e2a806cfd7e990ee91dc92fe6bba34eab2bf45d5be7d67e24d467").into(), - extrinsics_root: hex!("83fd59e8fe7cee53d7421713a09fe0abae1aec5f4db94fe5193737b12195f013").into(), - digest: Digest { logs: vec![], }, - }, - extrinsics: vec![ - construct_signed_tx(Transfer { - from: Keyring::Alice.to_raw_public().into(), - to: Keyring::Bob.to_raw_public().into(), - amount: 69, - nonce: 0, - }) - ], - }; - - with_externalities(&mut t, || { - execute_block(b.clone()); - - assert_eq!(balance_of(Keyring::Alice.to_raw_public().into()), 42); - assert_eq!(balance_of(Keyring::Bob.to_raw_public().into()), 69); - }); - - let b = Block { - header: Header { - parent_hash: b.header.hash(), - number: 2, - state_root: hex!("e32dd1d84d9133ca48078d2d83f2b0db19f9d47229ba98bf5ced0e9f86fac2c7").into(), - extrinsics_root: hex!("5d2d0a93201744f0df878c33b07da40cd38e24ac2358cc2811ea640835c31b68").into(), - digest: Digest { logs: vec![], }, - }, - extrinsics: vec![ - construct_signed_tx(Transfer { - from: Keyring::Bob.to_raw_public().into(), - to: Keyring::Alice.to_raw_public().into(), - amount: 27, - nonce: 0, - }), - construct_signed_tx(Transfer { - from: Keyring::Alice.to_raw_public().into(), - to: Keyring::Charlie.to_raw_public().into(), - amount: 69, - nonce: 1, - }), - ], - }; - - with_externalities(&mut t, || { - execute_block(b); - - assert_eq!(balance_of(Keyring::Alice.to_raw_public().into()), 0); - assert_eq!(balance_of(Keyring::Bob.to_raw_public().into()), 42); - assert_eq!(balance_of(Keyring::Charlie.to_raw_public().into()), 69); - }); - } -} diff --git a/substrate/test-runtime/wasm/Cargo.lock b/substrate/test-runtime/wasm/Cargo.lock deleted file mode 100644 index f42241061f26e..0000000000000 --- a/substrate/test-runtime/wasm/Cargo.lock +++ /dev/null @@ -1,828 +0,0 @@ -[[package]] -name = "arrayvec" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "nodrop 0.1.12 (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 = "bitflags" -version = "1.0.3" -source = "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.7 (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 = "byteorder" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "cfg-if" -version = "0.1.3" -source = "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 = "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-epoch" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.3 (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.0.1 (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.12 (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-utils" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.3 (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 = "ed25519" -version = "0.1.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)", - "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-primitives 0.1.0", - "untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "elastic-array" -version = "0.10.0" -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 = "environmental" -version = "0.1.0" - -[[package]] -name = "ethbloom" -version = "0.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)", - "ethereum-types-serialize 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fixed-hash 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ethereum-types" -version = "0.4.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)", - "ethbloom 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types-serialize 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fixed-hash 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "uint 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ethereum-types-serialize" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "fixed-hash" -version = "0.2.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)", - "libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 1.0.0 (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.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)", -] - -[[package]] -name = "fuchsia-zircon-sys" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "gcc" -version = "0.3.54" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "hashdb" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "elastic-array 0.10.0 (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.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hex-literal" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "hex-literal-impl 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hex-literal-impl" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "integer-sqrt" -version = "0.1.0" -source = "git+https://github.com/paritytech/integer-sqrt-rs.git#886e9cb983c46498003878afe965d55caa762025" - -[[package]] -name = "lazy_static" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "lazy_static" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "libc" -version = "0.2.41" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "log" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "log" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.3 (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_units" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "memorydb" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "hashdb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "plain_hasher 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "nan-preserving-float" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "nodrop" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "num-traits" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "num_cpus" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.41 (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.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parity-bytes" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "parity-wasm" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parking_lot" -version = "0.4.8" -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_core" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "patricia-trie" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "hashdb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "plain_hasher" -version = "0.2.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)", -] - -[[package]] -name = "proc-macro-hack" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro-hack-impl 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "proc-macro-hack-impl" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "proc-macro2" -version = "0.4.4" -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 = "pwasm-alloc" -version = "0.1.0" -dependencies = [ - "pwasm-libc 0.1.0", - "rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "pwasm-libc" -version = "0.1.0" - -[[package]] -name = "quote" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand" -version = "0.3.22" -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)", - "libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand" -version = "0.4.2" -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)", - "libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rayon" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rayon-core 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rayon-core" -version = "1.4.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)", - "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ring" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rlp" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rustc-hex" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "rustc-hex" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "rustc_version" -version = "0.2.2" -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 = "scopeguard" -version = "0.3.3" -source = "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.64" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "serde_derive" -version = "1.0.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "smallvec" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "stable_deref_trait" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "substrate-codec" -version = "0.1.0" -dependencies = [ - "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-codec-derive" -version = "0.1.0" -dependencies = [ - "proc-macro2 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-primitives" -version = "0.1.0" -dependencies = [ - "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "fixed-hash 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hashdb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "patricia-trie 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "plain_hasher 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-codec-derive 0.1.0", - "substrate-runtime-std 0.1.0", - "twox-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "uint 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-runtime-io" -version = "0.1.0" -dependencies = [ - "ed25519 0.1.0", - "environmental 0.1.0", - "hashdb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-std 0.1.0", - "substrate-state-machine 0.1.0", - "triehash 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-runtime-primitives" -version = "0.1.0" -dependencies = [ - "integer-sqrt 0.1.0 (git+https://github.com/paritytech/integer-sqrt-rs.git)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-codec-derive 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-io 0.1.0", - "substrate-runtime-std 0.1.0", - "substrate-runtime-support 0.1.0", -] - -[[package]] -name = "substrate-runtime-std" -version = "0.1.0" -dependencies = [ - "pwasm-alloc 0.1.0", - "pwasm-libc 0.1.0", - "rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-runtime-support" -version = "0.1.0" -dependencies = [ - "ed25519 0.1.0", - "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-io 0.1.0", - "substrate-runtime-std 0.1.0", -] - -[[package]] -name = "substrate-runtime-version" -version = "0.1.0" -dependencies = [ - "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-codec-derive 0.1.0", - "substrate-runtime-std 0.1.0", - "substrate-runtime-support 0.1.0", -] - -[[package]] -name = "substrate-state-machine" -version = "0.1.0" -dependencies = [ - "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "hashdb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "memorydb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "patricia-trie 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-primitives 0.1.0", - "triehash 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-test-runtime" -version = "0.1.0" -dependencies = [ - "ed25519 0.1.0", - "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-codec-derive 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-io 0.1.0", - "substrate-runtime-primitives 0.1.0", - "substrate-runtime-std 0.1.0", - "substrate-runtime-support 0.1.0", - "substrate-runtime-version 0.1.0", -] - -[[package]] -name = "syn" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.3 (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 = "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 = "triehash" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "hashdb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "twox-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "uint" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "crunchy 0.1.6 (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.0 (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 = "unreachable" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "untrusted" -version = "0.5.1" -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 = "wasmi" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "nan-preserving-float 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-wasm 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "winapi" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[metadata] -"checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" -"checksum base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83" -"checksum bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c54bb8f454c567f21197eefcdbf5679d0bd99f2ddbe52e84c77061952e6789" -"checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" -"checksum byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "74c0b906e9446b0a2e4f760cdb3fa4b2c48cdc6db8766a845c54b6ff063fd2e9" -"checksum cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "405216fd8fe65f718daa7102ea808a946b6ce40c742998fbfd3463645552de18" -"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e" -"checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" -"checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150" -"checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" -"checksum crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" -"checksum elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "88d4851b005ef16de812ea9acdb7bece2f0a40dd86c07b85631d7dafa54537bb" -"checksum ethbloom 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a93a43ce2e9f09071449da36bfa7a1b20b950ee344b6904ff23de493b03b386" -"checksum ethereum-types 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "35b3c5a18bc5e73a32a110ac743ec04b02bbbcd3b71d3118d40a6113d509378a" -"checksum ethereum-types-serialize 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ac59a21a9ce98e188f3dace9eb67a6c4a3c67ec7fbc7218cb827852679dc002" -"checksum fixed-hash 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0d5ec8112f00ea8a483e04748a85522184418fd1cf02890b626d8fc28683f7de" -"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 gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb" -"checksum hashdb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f1c71fc577cde89b3345d5f2880fecaf462a32e96c619f431279bdaf1ba5ddb1" -"checksum heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1679e6ea370dee694f91f1dc469bf94cf8f52051d147aec3e1f9497c6fc22461" -"checksum hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4da5f0e01bd8a71a224a4eedecaacfcabda388dbb7a80faf04d3514287572d95" -"checksum hex-literal-impl 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1d340b6514f232f6db1bd16db65302a5278a04fef9ce867cb932e7e5fa21130a" -"checksum integer-sqrt 0.1.0 (git+https://github.com/paritytech/integer-sqrt-rs.git)" = "" -"checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" -"checksum lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e6412c5e2ad9584b0b8e979393122026cdd6d2a80b933f890dcd694ddbe73739" -"checksum libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)" = "ac8ebf8343a981e2fa97042b14768f02ed3e1d602eac06cae6166df3c8ced206" -"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" -"checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2" -"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" -"checksum memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" -"checksum memorydb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f72c93304ad51e21230ecbd0d2b58a3f94703bf9339d14aed88c3aaf5e8b7a56" -"checksum nan-preserving-float 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34d4f00fcc2f4c9efa8cc971db0da9e28290e28e97af47585e48691ef10ff31f" -"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" -"checksum num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "775393e285254d2f5004596d69bb8bc1149754570dcc08cf30cabeba67955e28" -"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" -"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" -"checksum parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa5168b4cf41f3835e4bc6ffb32f51bc9365dc50cb351904595b3931d917fd0c" -"checksum parity-wasm 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1c91199d14bd5b78ecade323d4a891d094799749c1b9e82d9c590c2e2849a40" -"checksum parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "149d8f5b97f3c1133e3cfcd8886449959e856b557ff281e292b733d7c69e005e" -"checksum parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4db1a8ccf734a7bce794cc19b3df06ed87ab2f3907036b693c68f56b4d4537fa" -"checksum patricia-trie 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fa27fc4a972a03d64e5170d7facd2c84c6ed425b38ce62ad98dcfee2f7845b3b" -"checksum plain_hasher 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "95fa6386b1d34aaf0adb9b7dd2885dbe7c34190e6263785e5a7ec2b19044a90f" -"checksum proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ba8d4f9257b85eb6cdf13f055cea3190520aab1409ca2ab43493ea4820c25f0" -"checksum proc-macro-hack-impl 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d5cb6f960ad471404618e9817c0e5d10b1ae74cfdf01fab89ea0641fe7fb2892" -"checksum proc-macro2 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1fa93823f53cfd0f5ac117b189aed6cfdfb2cfc0a9d82e956dd7927595ed7d46" -"checksum quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e44651a0dc4cdd99f71c83b561e221f714912d11af1a4dff0631f923d53af035" -"checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1" -"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" -"checksum rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b614fe08b6665cb9a231d07ac1364b0ef3cb3698f1239ee0c4c3a88a524f54c8" -"checksum rayon-core 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d24ad214285a7729b174ed6d3bcfcb80177807f959d95fafd5bfc5c4f201ac8" -"checksum ring 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6f7d28b30a72c01b458428e0ae988d4149c20d902346902be881e3edc4bb325c" -"checksum rlp 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "524c5ad554859785dfc8469df3ed5e0b5784d4d335877ed47c8d90fc0eb238fe" -"checksum rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0ceb8ce7a5e520de349e1fa172baeba4a9e8d5ef06c47471863530bc4972ee1e" -"checksum rustc-hex 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d2b03280c2813907a030785570c577fb27d3deec8da4c18566751ade94de0ace" -"checksum rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a54aa04a10c68c1c4eacb4337fd883b435997ede17a9385784b990777686b09a" -"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" -"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.64 (registry+https://github.com/rust-lang/crates.io-index)" = "fba5be06346c5200249c8c8ca4ccba4a09e8747c71c16e420bd359a0db4d8f91" -"checksum serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)" = "79e4620ba6fbe051fc7506fab6f84205823564d55da18d55b695160fb3479cd8" -"checksum smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "153ffa32fd170e9944f7e0838edf824a754ec4c1fc64746fcc9fe1f8fa602e5d" -"checksum stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15132e0e364248108c5e2c02e3ab539be8d6f5d52a01ca9bbf27ed657316f02b" -"checksum syn 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6dfd71b2be5a58ee30a6f8ea355ba8290d397131c00dfa55c3d34e6e13db5101" -"checksum tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e9175261fbdb60781fcd388a4d6cc7e14764a2b629a7ad94abb439aed223a44f" -"checksum triehash 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3da77dc2c88bac48769c53f2c7675d99d522a7fc8130da3fadf29d7c6f94c9ac" -"checksum twox-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "475352206e7a290c5fccc27624a163e8d0d115f7bb60ca18a64fc9ce056d7435" -"checksum uint 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "754ba11732b9161b94c41798e5197e5e75388d012f760c42adb5000353e98646" -"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" -"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" -"checksum untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f392d7819dbe58833e26872f5f6f0d68b7bbbe90fc3667e98731c4a15ad9a7ae" -"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum wasmi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "522fe3fdd44a56f25cd5ddcd8ccdb1cf2e982ceb28fcb00f41d8a018ae5245a8" -"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/substrate/test-runtime/wasm/Cargo.toml b/substrate/test-runtime/wasm/Cargo.toml deleted file mode 100644 index 599733e271c17..0000000000000 --- a/substrate/test-runtime/wasm/Cargo.toml +++ /dev/null @@ -1,42 +0,0 @@ -[package] -name = "substrate-test-runtime" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -log = { version = "0.3", optional = true } -hex-literal = { version = "0.1.0", optional = true } -ed25519 = { path = "../../ed25519", optional = true } -substrate-codec = { path = "../../codec", default-features = false } -substrate-codec-derive = { path = "../../codec/derive", default-features = false } -substrate-runtime-std = { path = "../../runtime-std", default-features = false } -substrate-runtime-io = { path = "../../runtime-io", default-features = false } -substrate-runtime-support = { path = "../../runtime-support", default-features = false } -substrate-runtime-version = { path = "../../runtime/version", default-features = false } -substrate-primitives = { path = "../../primitives", default-features = false } -substrate-runtime-primitives = { path = "../../runtime/primitives", default-features = false } - -[features] -default = [] -std = [ - "log", - "hex-literal", - "ed25519", - "substrate-codec/std", - "substrate-runtime-std/std", - "substrate-runtime-io/std", - "substrate-runtime-support/std", - "substrate-runtime-version/std", - "substrate-primitives/std", - "substrate-runtime-primitives/std" -] - -[lib] -crate-type = ["cdylib"] - -[profile.release] -panic = "abort" -lto = true - -[workspace] -members = [] diff --git a/substrate/test-runtime/wasm/build.sh b/substrate/test-runtime/wasm/build.sh deleted file mode 100755 index 63d9347bf461c..0000000000000 --- a/substrate/test-runtime/wasm/build.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -set -e - -cargo +nightly 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/substrate/test-runtime/wasm/src b/substrate/test-runtime/wasm/src deleted file mode 120000 index 5cd551cf2693e..0000000000000 --- a/substrate/test-runtime/wasm/src +++ /dev/null @@ -1 +0,0 @@ -../src \ No newline at end of file diff --git a/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm b/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm deleted file mode 100644 index c917584c7f3d85c1150e8a92da021ac389386132..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52573 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 GwErKnJo0(~ diff --git a/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.wasm b/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.wasm deleted file mode 100755 index 89a0e6127e8be974092254411ea0cd2a83bfa478..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52705 zcmeIb34C1Fb>DyAyqUoOgJnQcwpQu^q9{@(Aq;lFGOeLRS+upV5@pMFTxb9cNN@ol zER<;3k|N2nnmQ?+y8k$vqW2E-uZ@uS~A4%#OKGi5-o^`PJht+W=HMKD~I-xu%5Ui$0-fP1TMc zUpP@~F0x!(@d;a0ayU!zBvXU{(rGEnuI7b zb4#^phG}(vdSz~5esZNcb*vV+;^gGvT6J-9s=8bYgSN@Znd(XvBxk~m>T75=S%4wA zu!R&Gii?z_A-FgnGr}cVV}MIr==(D8-S&Mh%w(NG4 z=H>}ikIgODpw7bdQMZ#(s7WK0otsZr8EkC}TqP)G zFCXsdZTq)B9c<6I;ANzH|kWfqZKfSf@|{I=J4vD3p(gL?QoLJcSCLLIT&5^AyU9LU9eF zkm%wm)Y71kku4u(={&vKbaC9>7T?`p41(N)j6-m=H>|YJ_hc*Co?ONCFnT>Lo&`X= zeZP8A30Ek@s0LB!M=}#-ds>Q_>je5nXhgjz*As>$XvEcHwdvRw5vG}v*mBE}VL~GRg+9bE)rT_I z2<98XmSP@2CFdVO@o*-1Fnr(&SLr|68*C$MXQtEx?46kcxbjek0$Qp#btQyOeY)@M;YOQ=bxTkU^5WBWT`5QYjQCnQB7`0JH#oEPfJ2G@0&T*AX z)hq+j?fj6Jj?k-YR}lmLGBYc%iz4LBOR{GOins2CM0`IV)ff_m>;b=DU2 zP8x4*4+NyumyOyhn^us>7u-EdQR@<+Pr4hSM?$`+WuGVoSNF8SjIDGL3FvLT5y@&b z;?Pj5NYf_LIBOUJi7?r?yB(B-Dn%h7qV%>KSu!4x4tgVkG%|eabjF|*VmoSzP zQ~}b_;(k2DP@;84T-1{lu{{M(!$uDUHwuBQ??6F#6imi5Le3%fFivj^%|em9hBpSM z;7bHa`J+*bv3=xryZ+u*#xK0_l1g|F{Ev2`&3zr{pPrU>`g9)bdtPPe+X-eiby1dh* zJ*|}ok&ajaAWH2=dQ6lu*TDxO38sNY7oK1aIRYb5&7mgX7{mZ=14E%;Vn*;ea97Bz zz0FkuR0s{Eq2-&Qc~I#JNnKYgNkywY-Lj>v#n+LCO4h6pa?WCfAa}CU%nHdsuGmgPSL7Hl*=e#un(Z{K1X&?g zyby)3eQc{yi@6~a6?}i0{JpIveEfu7FiT{_#f$>hm<&fFG^6Y|SSG}J)MO=F3ADtJ z4Og2dPsda}ZdqKG{DtB#eoj|{6&iI}WMB3PnfAI0V% z+SAO!|DW9dOUeB^*-XoW83`*!lF5`*DME;~6}rmlcbsNTf-GKH`J{pjZs|(RndyIG z&n)c3qtN@mOAugx@HBY<$tEd4xgTC|;3AxK<(A)`eKvI|;R6GE#1 zS}etc7WM(KV#JWcrd6J453ugBC7u?9vC0riTCR9XF?6<|Ve#aVib8CB83uZ^nL0$< zwneRbqsJa?Ud1sDnAl=j+Ve~KW`0WZN##-&GhnUN>>rx?)w%_CQv!-iw9PPANc)GH zEHsQ3mr75wWfnlP%jt$!HG3a@NZti45SR{A!<}xw5spIHLzFZ-w8DZ2P%yEzXj!k3 z1*3`8R+QZeuUk=)x~~IKkuI;VC^s`y(&pl%)QS>&dTj$})_Mb_4I3a8Hd@m__r?uq zXxH=r;?x`H+OPpq_jNGNO+DDOaRaPb1TLA4@-pt46EMV@6EI)mRdc%-&z75bBI=Ci zCn_zOE5U>l7nq^Svm}F6x`wd1M0nantF(3+is6FPx#4DB0$kd#g&ofellHX&i`$|% z=WfgSw$|ZGJ&yXe!-dU zZt!>ICm%ssx0+>DaTQl$Ng!4?DLRn3F3u$jj7bEFHCt zWDZ1`z4_71!MMs7PzjW{fN1{_2-YjjBm4*!VF&36thJrtfzhzv3US*7)~d22GsFUy zM}Kce)JEyv&NaP9N$z#bX;HyHivE$&<3QBGlF+vni5k>~U}4l*x%?I$wvb3-Zn7K-U`g5gc`fo#kLCs(GIiNw8Tx;4LV! z@Ze&on8f4)Vf}>d+JmZQAyUulXrWN7{5|DpDyES9eby%pAC1OjYD%VgOZI+3l?H3q zPhToOC{tswbOory)?wuImlIy%an&F;&_n`ki;4Ijde{6;|Lsf_V=kP^F7 znaY3E2uf*?U6O=7k&ooWisBkd|u7)++8BsH;M$4X>c*G(7jDytpbSXAxO8 zg}_nrs#7a`4elhl0#&4*i_@r{jX&3#eOI7~%UY~FZeh<0PiEkSy{z*=4Zmu&dt z{DY4z{-1<}a8}DX6JaMr`+IZjUEt&8Uliq!vJkx->-AzWlFDL~imR}a$n3?;%MvE> zNUsctM2OcvafCu|8vqohR_H*J);75_t>g!%S8S&|Xk3tt)$^Ee#EQz(G?e5?KRpRM zKZMyGg|KIDQFXBP6mC}-m7;KmhZU)Q)QBM8Rw{!OOZcri
MRs?N%Jc+o7pY=3S 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 From 49c8942f8a7541e1d596e6ca9ed27ae16dd76de3 Mon Sep 17 00:00:00 2001 From: Gav Date: Tue, 11 Sep 2018 20:31:19 +0200 Subject: [PATCH 2/2] Other bits of big refactor --- core/bft/Cargo.toml | 22 + core/bft/README.adoc | 13 + core/bft/src/error.rs | 95 + core/bft/src/lib.rs | 1124 +++++++++++ core/cli/Cargo.toml | 35 + core/cli/README.adoc | 13 + core/cli/build.rs | 59 + core/cli/doc/shell-completion.adoc | 41 + core/cli/src/cli.yml | 229 +++ core/cli/src/error.rs | 37 + core/cli/src/informant.rs | 124 ++ core/cli/src/lib.rs | 571 ++++++ core/cli/src/panic_hook.rs | 69 + core/client/Cargo.toml | 32 + core/client/README.adoc | 13 + core/client/db/Cargo.toml | 24 + core/client/db/src/cache.rs | 432 +++++ core/client/db/src/lib.rs | 715 +++++++ core/client/db/src/light.rs | 392 ++++ core/client/db/src/utils.rs | 174 ++ core/client/src/backend.rs | 107 ++ core/client/src/block_builder.rs | 140 ++ core/client/src/blockchain.rs | 92 + core/client/src/call_executor.rs | 199 ++ core/client/src/cht.rs | 274 +++ core/client/src/client.rs | 810 ++++++++ core/client/src/error.rs | 154 ++ core/client/src/genesis.rs | 202 ++ core/client/src/in_mem.rs | 439 +++++ core/client/src/lib.rs | 69 + core/client/src/light/backend.rs | 229 +++ core/client/src/light/blockchain.rs | 142 ++ core/client/src/light/call_executor.rs | 191 ++ core/client/src/light/fetcher.rs | 308 +++ core/client/src/light/mod.rs | 77 + core/client/src/notifications.rs | 289 +++ core/codec/Cargo.toml | 12 + core/codec/README.adoc | 13 + core/codec/derive/Cargo.toml | 20 + core/codec/derive/src/decode.rs | 111 ++ core/codec/derive/src/encode.rs | 153 ++ core/codec/derive/src/lib.rs | 126 ++ core/codec/derive/tests/mod.rs | 151 ++ core/codec/src/codec.rs | 540 ++++++ core/codec/src/joiner.rs | 33 + core/codec/src/keyedvec.rs | 36 + core/codec/src/lib.rs | 45 + core/environmental/Cargo.toml | 8 + core/environmental/README.adoc | 13 + core/environmental/src/lib.rs | 383 ++++ core/environmental/with_std.rs | 32 + core/environmental/without_std.rs | 70 + core/executor/Cargo.toml | 33 + core/executor/README.adoc | 13 + core/executor/src/error.rs | 81 + core/executor/src/lib.rs | 93 + core/executor/src/native_executor.rs | 224 +++ core/executor/src/sandbox.rs | 676 +++++++ core/executor/src/wasm_executor.rs | 718 +++++++ core/executor/src/wasm_utils.rs | 199 ++ core/executor/wasm/Cargo.lock | 216 +++ core/executor/wasm/Cargo.toml | 19 + core/executor/wasm/build.sh | 8 + core/executor/wasm/src/lib.rs | 122 ++ core/extrinsic-pool/Cargo.toml | 19 + core/extrinsic-pool/README.adoc | 13 + core/extrinsic-pool/src/error.rs | 33 + core/extrinsic-pool/src/lib.rs | 49 + core/extrinsic-pool/src/listener.rs | 95 + core/extrinsic-pool/src/pool.rs | 606 ++++++ core/extrinsic-pool/src/rotator.rs | 209 +++ core/extrinsic-pool/src/watcher.rs | 103 + core/keyring/Cargo.toml | 9 + core/keyring/README.adoc | 13 + core/keyring/src/lib.rs | 176 ++ core/keystore/Cargo.toml | 19 + core/keystore/README.adoc | 13 + core/keystore/src/lib.rs | 293 +++ core/misbehavior-check/Cargo.toml | 19 + core/misbehavior-check/README.adoc | 13 + core/misbehavior-check/src/lib.rs | 206 ++ core/network-libp2p/Cargo.toml | 34 + core/network-libp2p/README.adoc | 13 + core/network-libp2p/src/connection_filter.rs | 31 + core/network-libp2p/src/custom_proto.rs | 290 +++ core/network-libp2p/src/error.rs | 221 +++ core/network-libp2p/src/lib.rs | 75 + core/network-libp2p/src/network_state.rs | 953 ++++++++++ core/network-libp2p/src/service.rs | 1437 ++++++++++++++ core/network-libp2p/src/timeouts.rs | 115 ++ core/network-libp2p/src/topology.rs | 655 +++++++ core/network-libp2p/src/traits.rs | 299 +++ core/network-libp2p/src/transport.rs | 50 + core/network-libp2p/tests/tests.rs | 130 ++ core/network/Cargo.toml | 30 + core/network/README.adoc | 13 + core/network/src/blocks.rs | 282 +++ core/network/src/chain.rs | 105 ++ core/network/src/config.rs | 32 + core/network/src/consensus_gossip.rs | 382 ++++ core/network/src/error.rs | 35 + core/network/src/import_queue.rs | 593 ++++++ core/network/src/io.rs | 72 + core/network/src/lib.rs | 69 + core/network/src/message.rs | 375 ++++ core/network/src/on_demand.rs | 736 ++++++++ core/network/src/protocol.rs | 679 +++++++ core/network/src/service.rs | 340 ++++ core/network/src/specialization.rs | 48 + core/network/src/sync.rs | 428 +++++ core/network/src/test/mod.rs | 330 ++++ core/network/src/test/sync.rs | 121 ++ core/primitives/Cargo.toml | 58 + core/primitives/README.adoc | 13 + core/primitives/src/authority_id.rs | 106 ++ core/primitives/src/bytes.rs | 158 ++ core/primitives/src/ed25519.rs | 357 ++++ core/primitives/src/hash.rs | 166 ++ core/primitives/src/hasher.rs | 53 + core/primitives/src/hashing.rs | 106 ++ core/primitives/src/hexdisplay.rs | 94 + core/primitives/src/lib.rs | 142 ++ core/primitives/src/rlp_codec.rs | 123 ++ core/primitives/src/sandbox.rs | 221 +++ core/primitives/src/storage.rs | 44 + core/primitives/src/tests.rs | 17 + core/primitives/src/u32_trait.rs | 89 + core/primitives/src/uint.rs | 99 + core/pwasm-alloc/Cargo.lock | 37 + core/pwasm-alloc/Cargo.toml | 23 + core/pwasm-alloc/README.adoc | 25 + core/pwasm-alloc/build.rs | 14 + core/pwasm-alloc/src/lib.rs | 34 + core/pwasm-libc/Cargo.lock | 4 + core/pwasm-libc/Cargo.toml | 15 + core/pwasm-libc/README.adoc | 24 + core/pwasm-libc/src/lib.rs | 55 + core/rpc-servers/Cargo.toml | 14 + core/rpc-servers/README.adoc | 14 + core/rpc-servers/src/lib.rs | 93 + core/rpc/Cargo.toml | 27 + core/rpc/README.adoc | 13 + core/rpc/src/author/error.rs | 68 + core/rpc/src/author/mod.rs | 162 ++ core/rpc/src/author/tests.rs | 178 ++ core/rpc/src/chain/error.rs | 42 + core/rpc/src/chain/mod.rs | 165 ++ core/rpc/src/chain/tests.rs | 207 ++ core/rpc/src/errors.rs | 34 + core/rpc/src/helpers.rs | 25 + core/rpc/src/lib.rs | 59 + core/rpc/src/metadata.rs | 54 + core/rpc/src/state/error.rs | 54 + core/rpc/src/state/mod.rs | 277 +++ core/rpc/src/state/tests.rs | 186 ++ core/rpc/src/subscriptions.rs | 85 + core/rpc/src/system/error.rs | 40 + core/rpc/src/system/mod.rs | 41 + core/rpc/src/system/tests.rs | 54 + core/runtime-io/Cargo.toml | 32 + core/runtime-io/README.adoc | 13 + core/runtime-io/build.rs | 14 + core/runtime-io/src/lib.rs | 35 + core/runtime-io/with_std.rs | 266 +++ core/runtime-io/without_std.rs | 329 ++++ core/runtime-sandbox/Cargo.toml | 31 + core/runtime-sandbox/README.adoc | 13 + core/runtime-sandbox/build.rs | 14 + core/runtime-sandbox/src/lib.rs | 221 +++ core/runtime-sandbox/with_std.rs | 482 +++++ core/runtime-sandbox/without_std.rs | 303 +++ core/runtime-std/Cargo.toml | 18 + core/runtime-std/README.adoc | 14 + core/runtime-std/build.rs | 14 + core/runtime-std/src/lib.rs | 51 + core/runtime-std/with_std.rs | 38 + core/runtime-std/without_std.rs | 46 + core/serializer/Cargo.toml | 8 + core/serializer/README.adoc | 14 + core/serializer/src/lib.rs | 46 + core/service/Cargo.toml | 31 + core/service/README.adoc | 14 + core/service/src/chain_ops.rs | 139 ++ core/service/src/chain_spec.rs | 170 ++ core/service/src/components.rs | 256 +++ core/service/src/config.rs | 121 ++ core/service/src/error.rs | 35 + core/service/src/lib.rs | 424 +++++ core/state-db/Cargo.toml | 14 + core/state-db/README.adoc | 13 + core/state-db/src/lib.rs | 407 ++++ core/state-db/src/pruning.rs | 267 +++ core/state-db/src/test.rs | 83 + core/state-db/src/unfinalized.rs | 533 ++++++ core/state-machine/Cargo.toml | 21 + core/state-machine/README.adoc | 13 + core/state-machine/src/backend.rs | 193 ++ core/state-machine/src/ext.rs | 171 ++ core/state-machine/src/lib.rs | 691 +++++++ core/state-machine/src/proving_backend.rs | 183 ++ core/state-machine/src/testing.rs | 118 ++ core/state-machine/src/trie_backend.rs | 362 ++++ core/telemetry/Cargo.toml | 15 + core/telemetry/README.adoc | 13 + core/telemetry/src/lib.rs | 160 ++ core/test-client/Cargo.toml | 18 + core/test-client/README.adoc | 13 + core/test-client/src/block_builder_ext.rs | 42 + core/test-client/src/client_ext.rs | 103 + core/test-client/src/lib.rs | 66 + core/test-runtime/Cargo.toml | 36 + core/test-runtime/README.adoc | 13 + core/test-runtime/src/genesismap.rs | 67 + core/test-runtime/src/lib.rs | 156 ++ core/test-runtime/src/system.rs | 281 +++ core/test-runtime/wasm/Cargo.lock | 709 +++++++ core/test-runtime/wasm/Cargo.toml | 26 + core/test-runtime/wasm/build.sh | 8 + core/test-runtime/wasm/src | 1 + framework/client/Cargo.toml | 32 + framework/executor/Cargo.toml | 33 + framework/network/Cargo.toml | 30 + framework/runtime-support/Cargo.toml | 32 + framework/test-runtime/Cargo.toml | 36 + node/Cargo.toml | 18 + node/api/Cargo.toml | 13 + node/api/src/lib.rs | 155 ++ node/build.rs | 24 + node/cli/Cargo.toml | 12 + node/cli/src/cli.yml | 12 + node/cli/src/error.rs | 29 + node/cli/src/lib.rs | 122 ++ node/consensus/Cargo.toml | 26 + node/consensus/README.adoc | 5 + node/consensus/src/error.rs | 51 + node/consensus/src/evaluation.rs | 96 + node/consensus/src/lib.rs | 445 +++++ node/consensus/src/offline_tracker.rs | 137 ++ node/consensus/src/service.rs | 172 ++ node/executor/Cargo.toml | 28 + node/executor/src/lib.rs | 523 ++++++ node/network/Cargo.toml | 17 + node/network/src/consensus.rs | 297 +++ node/network/src/lib.rs | 117 ++ node/primitives/Cargo.toml | 29 + node/primitives/src/lib.rs | 94 + node/runtime/Cargo.toml | 61 + node/runtime/src/checked_block.rs | 94 + node/runtime/src/lib.rs | 386 ++++ node/runtime/wasm/Cargo.lock | 568 ++++++ node/runtime/wasm/Cargo.toml | 38 + node/runtime/wasm/build.sh | 8 + node/runtime/wasm/src | 1 + node/service/Cargo.toml | 26 + node/service/src/chain_spec.rs | 209 +++ node/service/src/lib.rs | 213 +++ node/src/main.rs | 69 + node/transaction-pool/Cargo.toml | 18 + node/transaction-pool/src/error.rs | 73 + node/transaction-pool/src/lib.rs | 236 +++ runtime/README.adoc | 6 + runtime/balances/Cargo.toml | 36 + runtime/balances/src/address.rs | 111 ++ runtime/balances/src/genesis_config.rs | 84 + runtime/balances/src/lib.rs | 660 +++++++ runtime/balances/src/mock.rs | 77 + runtime/balances/src/tests.rs | 324 ++++ runtime/consensus/Cargo.toml | 32 + runtime/consensus/src/lib.rs | 267 +++ runtime/contract/Cargo.toml | 41 + runtime/contract/src/account_db.rs | 180 ++ runtime/contract/src/double_map.rs | 90 + runtime/contract/src/exec.rs | 266 +++ runtime/contract/src/gas.rs | 178 ++ runtime/contract/src/genesis_config.rs | 54 + runtime/contract/src/lib.rs | 278 +++ runtime/contract/src/tests.rs | 670 +++++++ runtime/contract/src/vm/env_def/macros.rs | 285 +++ runtime/contract/src/vm/env_def/mod.rs | 315 ++++ runtime/contract/src/vm/mod.rs | 553 ++++++ runtime/contract/src/vm/prepare.rs | 286 +++ runtime/council/Cargo.toml | 43 + runtime/council/src/lib.rs | 238 +++ runtime/council/src/motions.rs | 372 ++++ runtime/council/src/seats.rs | 1355 ++++++++++++++ runtime/council/src/voting.rs | 505 +++++ runtime/democracy/Cargo.toml | 37 + runtime/democracy/src/lib.rs | 681 +++++++ runtime/democracy/src/vote_threshold.rs | 97 + runtime/example/Cargo.toml | 34 + runtime/example/src/lib.rs | 415 +++++ runtime/executive/Cargo.toml | 33 + runtime/executive/src/lib.rs | 359 ++++ runtime/primitives/Cargo.toml | 34 + runtime/primitives/src/bft.rs | 195 ++ runtime/primitives/src/generic/block.rs | 105 ++ .../src/generic/checked_extrinsic.rs | 59 + runtime/primitives/src/generic/digest.rs | 150 ++ runtime/primitives/src/generic/header.rs | 167 ++ runtime/primitives/src/generic/mod.rs | 33 + runtime/primitives/src/generic/tests.rs | 105 ++ .../src/generic/unchecked_extrinsic.rs | 159 ++ runtime/primitives/src/lib.rs | 434 +++++ runtime/primitives/src/testing.rs | 138 ++ runtime/primitives/src/traits.rs | 462 +++++ runtime/session/Cargo.toml | 40 + runtime/session/src/lib.rs | 467 +++++ runtime/staking/Cargo.toml | 45 + runtime/staking/src/genesis_config.rs | 77 + runtime/staking/src/lib.rs | 576 ++++++ runtime/staking/src/mock.rs | 126 ++ runtime/staking/src/tests.rs | 502 +++++ runtime/support/Cargo.toml | 32 + runtime/support/README.adoc | 14 + runtime/support/src/dispatch.rs | 584 ++++++ runtime/support/src/event.rs | 298 +++ runtime/support/src/hashable.rs | 38 + runtime/support/src/lib.rs | 191 ++ runtime/support/src/metadata.rs | 459 +++++ runtime/support/src/storage/generator.rs | 1659 +++++++++++++++++ runtime/support/src/storage/mod.rs | 609 ++++++ runtime/system/Cargo.toml | 32 + runtime/system/src/lib.rs | 426 +++++ runtime/timestamp/Cargo.toml | 32 + runtime/timestamp/src/lib.rs | 210 +++ runtime/treasury/Cargo.toml | 34 + runtime/treasury/src/lib.rs | 546 ++++++ runtime/version/Cargo.toml | 22 + runtime/version/src/lib.rs | 130 ++ 329 files changed, 57639 insertions(+) create mode 100644 core/bft/Cargo.toml create mode 100644 core/bft/README.adoc create mode 100644 core/bft/src/error.rs create mode 100644 core/bft/src/lib.rs create mode 100644 core/cli/Cargo.toml create mode 100644 core/cli/README.adoc create mode 100644 core/cli/build.rs create mode 100644 core/cli/doc/shell-completion.adoc create mode 100644 core/cli/src/cli.yml create mode 100644 core/cli/src/error.rs create mode 100644 core/cli/src/informant.rs create mode 100644 core/cli/src/lib.rs create mode 100644 core/cli/src/panic_hook.rs create mode 100644 core/client/Cargo.toml create mode 100644 core/client/README.adoc create mode 100644 core/client/db/Cargo.toml create mode 100644 core/client/db/src/cache.rs create mode 100644 core/client/db/src/lib.rs create mode 100644 core/client/db/src/light.rs create mode 100644 core/client/db/src/utils.rs create mode 100644 core/client/src/backend.rs create mode 100644 core/client/src/block_builder.rs create mode 100644 core/client/src/blockchain.rs create mode 100644 core/client/src/call_executor.rs create mode 100644 core/client/src/cht.rs create mode 100644 core/client/src/client.rs create mode 100644 core/client/src/error.rs create mode 100644 core/client/src/genesis.rs create mode 100644 core/client/src/in_mem.rs create mode 100644 core/client/src/lib.rs create mode 100644 core/client/src/light/backend.rs create mode 100644 core/client/src/light/blockchain.rs create mode 100644 core/client/src/light/call_executor.rs create mode 100644 core/client/src/light/fetcher.rs create mode 100644 core/client/src/light/mod.rs create mode 100644 core/client/src/notifications.rs create mode 100644 core/codec/Cargo.toml create mode 100644 core/codec/README.adoc create mode 100644 core/codec/derive/Cargo.toml create mode 100644 core/codec/derive/src/decode.rs create mode 100644 core/codec/derive/src/encode.rs create mode 100644 core/codec/derive/src/lib.rs create mode 100644 core/codec/derive/tests/mod.rs create mode 100644 core/codec/src/codec.rs create mode 100644 core/codec/src/joiner.rs create mode 100644 core/codec/src/keyedvec.rs create mode 100644 core/codec/src/lib.rs create mode 100644 core/environmental/Cargo.toml create mode 100644 core/environmental/README.adoc create mode 100644 core/environmental/src/lib.rs create mode 100644 core/environmental/with_std.rs create mode 100644 core/environmental/without_std.rs create mode 100644 core/executor/Cargo.toml create mode 100644 core/executor/README.adoc create mode 100644 core/executor/src/error.rs create mode 100644 core/executor/src/lib.rs create mode 100644 core/executor/src/native_executor.rs create mode 100644 core/executor/src/sandbox.rs create mode 100644 core/executor/src/wasm_executor.rs create mode 100644 core/executor/src/wasm_utils.rs create mode 100644 core/executor/wasm/Cargo.lock create mode 100644 core/executor/wasm/Cargo.toml create mode 100755 core/executor/wasm/build.sh create mode 100644 core/executor/wasm/src/lib.rs create mode 100644 core/extrinsic-pool/Cargo.toml create mode 100644 core/extrinsic-pool/README.adoc create mode 100644 core/extrinsic-pool/src/error.rs create mode 100644 core/extrinsic-pool/src/lib.rs create mode 100644 core/extrinsic-pool/src/listener.rs create mode 100644 core/extrinsic-pool/src/pool.rs create mode 100644 core/extrinsic-pool/src/rotator.rs create mode 100644 core/extrinsic-pool/src/watcher.rs create mode 100644 core/keyring/Cargo.toml create mode 100644 core/keyring/README.adoc create mode 100644 core/keyring/src/lib.rs create mode 100644 core/keystore/Cargo.toml create mode 100644 core/keystore/README.adoc create mode 100644 core/keystore/src/lib.rs create mode 100644 core/misbehavior-check/Cargo.toml create mode 100644 core/misbehavior-check/README.adoc create mode 100644 core/misbehavior-check/src/lib.rs create mode 100644 core/network-libp2p/Cargo.toml create mode 100644 core/network-libp2p/README.adoc create mode 100644 core/network-libp2p/src/connection_filter.rs create mode 100644 core/network-libp2p/src/custom_proto.rs create mode 100644 core/network-libp2p/src/error.rs create mode 100644 core/network-libp2p/src/lib.rs create mode 100644 core/network-libp2p/src/network_state.rs create mode 100644 core/network-libp2p/src/service.rs create mode 100644 core/network-libp2p/src/timeouts.rs create mode 100644 core/network-libp2p/src/topology.rs create mode 100644 core/network-libp2p/src/traits.rs create mode 100644 core/network-libp2p/src/transport.rs create mode 100644 core/network-libp2p/tests/tests.rs create mode 100644 core/network/Cargo.toml create mode 100644 core/network/README.adoc create mode 100644 core/network/src/blocks.rs create mode 100644 core/network/src/chain.rs create mode 100644 core/network/src/config.rs create mode 100644 core/network/src/consensus_gossip.rs create mode 100644 core/network/src/error.rs create mode 100644 core/network/src/import_queue.rs create mode 100644 core/network/src/io.rs create mode 100644 core/network/src/lib.rs create mode 100644 core/network/src/message.rs create mode 100644 core/network/src/on_demand.rs create mode 100644 core/network/src/protocol.rs create mode 100644 core/network/src/service.rs create mode 100644 core/network/src/specialization.rs create mode 100644 core/network/src/sync.rs create mode 100644 core/network/src/test/mod.rs create mode 100644 core/network/src/test/sync.rs create mode 100644 core/primitives/Cargo.toml create mode 100644 core/primitives/README.adoc create mode 100644 core/primitives/src/authority_id.rs create mode 100644 core/primitives/src/bytes.rs create mode 100644 core/primitives/src/ed25519.rs create mode 100644 core/primitives/src/hash.rs create mode 100644 core/primitives/src/hasher.rs create mode 100644 core/primitives/src/hashing.rs create mode 100644 core/primitives/src/hexdisplay.rs create mode 100644 core/primitives/src/lib.rs create mode 100644 core/primitives/src/rlp_codec.rs create mode 100644 core/primitives/src/sandbox.rs create mode 100644 core/primitives/src/storage.rs create mode 100644 core/primitives/src/tests.rs create mode 100644 core/primitives/src/u32_trait.rs create mode 100644 core/primitives/src/uint.rs create mode 100644 core/pwasm-alloc/Cargo.lock create mode 100644 core/pwasm-alloc/Cargo.toml create mode 100644 core/pwasm-alloc/README.adoc create mode 100644 core/pwasm-alloc/build.rs create mode 100644 core/pwasm-alloc/src/lib.rs create mode 100644 core/pwasm-libc/Cargo.lock create mode 100644 core/pwasm-libc/Cargo.toml create mode 100644 core/pwasm-libc/README.adoc create mode 100644 core/pwasm-libc/src/lib.rs create mode 100644 core/rpc-servers/Cargo.toml create mode 100644 core/rpc-servers/README.adoc create mode 100644 core/rpc-servers/src/lib.rs create mode 100644 core/rpc/Cargo.toml create mode 100644 core/rpc/README.adoc create mode 100644 core/rpc/src/author/error.rs create mode 100644 core/rpc/src/author/mod.rs create mode 100644 core/rpc/src/author/tests.rs create mode 100644 core/rpc/src/chain/error.rs create mode 100644 core/rpc/src/chain/mod.rs create mode 100644 core/rpc/src/chain/tests.rs create mode 100644 core/rpc/src/errors.rs create mode 100644 core/rpc/src/helpers.rs create mode 100644 core/rpc/src/lib.rs create mode 100644 core/rpc/src/metadata.rs create mode 100644 core/rpc/src/state/error.rs create mode 100644 core/rpc/src/state/mod.rs create mode 100644 core/rpc/src/state/tests.rs create mode 100644 core/rpc/src/subscriptions.rs create mode 100644 core/rpc/src/system/error.rs create mode 100644 core/rpc/src/system/mod.rs create mode 100644 core/rpc/src/system/tests.rs create mode 100644 core/runtime-io/Cargo.toml create mode 100644 core/runtime-io/README.adoc create mode 100644 core/runtime-io/build.rs create mode 100644 core/runtime-io/src/lib.rs create mode 100644 core/runtime-io/with_std.rs create mode 100644 core/runtime-io/without_std.rs create mode 100755 core/runtime-sandbox/Cargo.toml create mode 100644 core/runtime-sandbox/README.adoc create mode 100755 core/runtime-sandbox/build.rs create mode 100755 core/runtime-sandbox/src/lib.rs create mode 100755 core/runtime-sandbox/with_std.rs create mode 100755 core/runtime-sandbox/without_std.rs create mode 100644 core/runtime-std/Cargo.toml create mode 100644 core/runtime-std/README.adoc create mode 100644 core/runtime-std/build.rs create mode 100644 core/runtime-std/src/lib.rs create mode 100644 core/runtime-std/with_std.rs create mode 100644 core/runtime-std/without_std.rs create mode 100644 core/serializer/Cargo.toml create mode 100644 core/serializer/README.adoc create mode 100644 core/serializer/src/lib.rs create mode 100644 core/service/Cargo.toml create mode 100644 core/service/README.adoc create mode 100644 core/service/src/chain_ops.rs create mode 100644 core/service/src/chain_spec.rs create mode 100644 core/service/src/components.rs create mode 100644 core/service/src/config.rs create mode 100644 core/service/src/error.rs create mode 100644 core/service/src/lib.rs create mode 100644 core/state-db/Cargo.toml create mode 100644 core/state-db/README.adoc create mode 100644 core/state-db/src/lib.rs create mode 100644 core/state-db/src/pruning.rs create mode 100644 core/state-db/src/test.rs create mode 100644 core/state-db/src/unfinalized.rs create mode 100644 core/state-machine/Cargo.toml create mode 100644 core/state-machine/README.adoc create mode 100644 core/state-machine/src/backend.rs create mode 100644 core/state-machine/src/ext.rs create mode 100644 core/state-machine/src/lib.rs create mode 100644 core/state-machine/src/proving_backend.rs create mode 100644 core/state-machine/src/testing.rs create mode 100644 core/state-machine/src/trie_backend.rs create mode 100644 core/telemetry/Cargo.toml create mode 100644 core/telemetry/README.adoc create mode 100644 core/telemetry/src/lib.rs create mode 100644 core/test-client/Cargo.toml create mode 100644 core/test-client/README.adoc create mode 100644 core/test-client/src/block_builder_ext.rs create mode 100644 core/test-client/src/client_ext.rs create mode 100644 core/test-client/src/lib.rs create mode 100644 core/test-runtime/Cargo.toml create mode 100644 core/test-runtime/README.adoc create mode 100644 core/test-runtime/src/genesismap.rs create mode 100644 core/test-runtime/src/lib.rs create mode 100644 core/test-runtime/src/system.rs create mode 100644 core/test-runtime/wasm/Cargo.lock create mode 100644 core/test-runtime/wasm/Cargo.toml create mode 100755 core/test-runtime/wasm/build.sh create mode 120000 core/test-runtime/wasm/src create mode 100644 framework/client/Cargo.toml create mode 100644 framework/executor/Cargo.toml create mode 100644 framework/network/Cargo.toml create mode 100644 framework/runtime-support/Cargo.toml create mode 100644 framework/test-runtime/Cargo.toml create mode 100644 node/Cargo.toml create mode 100644 node/api/Cargo.toml create mode 100644 node/api/src/lib.rs create mode 100644 node/build.rs create mode 100644 node/cli/Cargo.toml create mode 100644 node/cli/src/cli.yml create mode 100644 node/cli/src/error.rs create mode 100644 node/cli/src/lib.rs create mode 100644 node/consensus/Cargo.toml create mode 100644 node/consensus/README.adoc create mode 100644 node/consensus/src/error.rs create mode 100644 node/consensus/src/evaluation.rs create mode 100644 node/consensus/src/lib.rs create mode 100644 node/consensus/src/offline_tracker.rs create mode 100644 node/consensus/src/service.rs create mode 100644 node/executor/Cargo.toml create mode 100644 node/executor/src/lib.rs create mode 100644 node/network/Cargo.toml create mode 100644 node/network/src/consensus.rs create mode 100644 node/network/src/lib.rs create mode 100644 node/primitives/Cargo.toml create mode 100644 node/primitives/src/lib.rs create mode 100644 node/runtime/Cargo.toml create mode 100644 node/runtime/src/checked_block.rs create mode 100644 node/runtime/src/lib.rs create mode 100644 node/runtime/wasm/Cargo.lock create mode 100644 node/runtime/wasm/Cargo.toml create mode 100755 node/runtime/wasm/build.sh create mode 120000 node/runtime/wasm/src create mode 100644 node/service/Cargo.toml create mode 100644 node/service/src/chain_spec.rs create mode 100644 node/service/src/lib.rs create mode 100644 node/src/main.rs create mode 100644 node/transaction-pool/Cargo.toml create mode 100644 node/transaction-pool/src/error.rs create mode 100644 node/transaction-pool/src/lib.rs create mode 100644 runtime/README.adoc create mode 100644 runtime/balances/Cargo.toml create mode 100644 runtime/balances/src/address.rs create mode 100644 runtime/balances/src/genesis_config.rs create mode 100644 runtime/balances/src/lib.rs create mode 100644 runtime/balances/src/mock.rs create mode 100644 runtime/balances/src/tests.rs create mode 100644 runtime/consensus/Cargo.toml create mode 100644 runtime/consensus/src/lib.rs create mode 100644 runtime/contract/Cargo.toml create mode 100644 runtime/contract/src/account_db.rs create mode 100644 runtime/contract/src/double_map.rs create mode 100644 runtime/contract/src/exec.rs create mode 100644 runtime/contract/src/gas.rs create mode 100644 runtime/contract/src/genesis_config.rs create mode 100644 runtime/contract/src/lib.rs create mode 100644 runtime/contract/src/tests.rs create mode 100644 runtime/contract/src/vm/env_def/macros.rs create mode 100644 runtime/contract/src/vm/env_def/mod.rs create mode 100644 runtime/contract/src/vm/mod.rs create mode 100644 runtime/contract/src/vm/prepare.rs create mode 100644 runtime/council/Cargo.toml create mode 100644 runtime/council/src/lib.rs create mode 100644 runtime/council/src/motions.rs create mode 100644 runtime/council/src/seats.rs create mode 100644 runtime/council/src/voting.rs create mode 100644 runtime/democracy/Cargo.toml create mode 100644 runtime/democracy/src/lib.rs create mode 100644 runtime/democracy/src/vote_threshold.rs create mode 100644 runtime/example/Cargo.toml create mode 100644 runtime/example/src/lib.rs create mode 100644 runtime/executive/Cargo.toml create mode 100644 runtime/executive/src/lib.rs create mode 100644 runtime/primitives/Cargo.toml create mode 100644 runtime/primitives/src/bft.rs create mode 100644 runtime/primitives/src/generic/block.rs create mode 100644 runtime/primitives/src/generic/checked_extrinsic.rs create mode 100644 runtime/primitives/src/generic/digest.rs create mode 100644 runtime/primitives/src/generic/header.rs create mode 100644 runtime/primitives/src/generic/mod.rs create mode 100644 runtime/primitives/src/generic/tests.rs create mode 100644 runtime/primitives/src/generic/unchecked_extrinsic.rs create mode 100644 runtime/primitives/src/lib.rs create mode 100644 runtime/primitives/src/testing.rs create mode 100644 runtime/primitives/src/traits.rs create mode 100644 runtime/session/Cargo.toml create mode 100644 runtime/session/src/lib.rs create mode 100644 runtime/staking/Cargo.toml create mode 100644 runtime/staking/src/genesis_config.rs create mode 100644 runtime/staking/src/lib.rs create mode 100644 runtime/staking/src/mock.rs create mode 100644 runtime/staking/src/tests.rs create mode 100644 runtime/support/Cargo.toml create mode 100644 runtime/support/README.adoc create mode 100644 runtime/support/src/dispatch.rs create mode 100644 runtime/support/src/event.rs create mode 100644 runtime/support/src/hashable.rs create mode 100644 runtime/support/src/lib.rs create mode 100644 runtime/support/src/metadata.rs create mode 100644 runtime/support/src/storage/generator.rs create mode 100644 runtime/support/src/storage/mod.rs create mode 100644 runtime/system/Cargo.toml create mode 100644 runtime/system/src/lib.rs create mode 100644 runtime/timestamp/Cargo.toml create mode 100644 runtime/timestamp/src/lib.rs create mode 100644 runtime/treasury/Cargo.toml create mode 100644 runtime/treasury/src/lib.rs create mode 100644 runtime/version/Cargo.toml create mode 100644 runtime/version/src/lib.rs diff --git a/core/bft/Cargo.toml b/core/bft/Cargo.toml new file mode 100644 index 0000000000000..609a27088b4de --- /dev/null +++ b/core/bft/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "substrate-bft" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +futures = "0.1.17" +substrate-codec = { path = "../codec" } +substrate-primitives = { path = "../primitives" } +substrate-runtime-support = { path = "../../runtime/support" } +substrate-runtime-primitives = { path = "../../runtime/primitives" } +substrate-runtime-version = { path = "../../runtime/version" } + +tokio = "0.1.7" +parking_lot = "0.4" +error-chain = "0.12" +log = "0.3" +rhododendron = "0.3" + +[dev-dependencies] +substrate-keyring = { path = "../keyring" } +substrate-executor = { path = "../executor" } diff --git a/core/bft/README.adoc b/core/bft/README.adoc new file mode 100644 index 0000000000000..8f4939087eed7 --- /dev/null +++ b/core/bft/README.adoc @@ -0,0 +1,13 @@ + += Substrate BFT + +.Summary +[source, toml] +---- +include::Cargo.toml[lines=2..5] +---- + +.Description +---- +include::src/lib.rs[tag=description] +---- diff --git a/core/bft/src/error.rs b/core/bft/src/error.rs new file mode 100644 index 0000000000000..fb41e7efa9055 --- /dev/null +++ b/core/bft/src/error.rs @@ -0,0 +1,95 @@ +// Copyright 2017 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 . + +//! Error types in the BFT service. +use runtime_version::RuntimeVersion; +use primitives::ed25519; + +error_chain! { + errors { + /// Missing state at block with given descriptor. + StateUnavailable(b: String) { + description("State missing at given block."), + display("State unavailable at block {}", b), + } + + /// I/O terminated unexpectedly + IoTerminated { + description("I/O terminated unexpectedly."), + display("I/O terminated unexpectedly."), + } + + /// Unable to schedule wakeup. + FaultyTimer(e: ::tokio::timer::Error) { + description("Timer error"), + display("Timer error: {}", e), + } + + /// Unable to propose a block. + CannotPropose { + description("Unable to create block proposal."), + display("Unable to create block proposal."), + } + + /// Error checking signature + InvalidSignature(s: ed25519::Signature, a: ::primitives::AuthorityId) { + description("Message signature is invalid"), + display("Message signature {:?} by {:?} is invalid.", s, a), + } + + /// Account is not an authority. + InvalidAuthority(a: ::primitives::AuthorityId) { + description("Message sender is not a valid authority"), + display("Message sender {:?} is not a valid authority.", a), + } + + /// Authoring interface does not match the runtime. + IncompatibleAuthoringRuntime(native: RuntimeVersion, on_chain: RuntimeVersion) { + description("Authoring for current runtime is not supported"), + display("Authoring for current runtime is not supported. Native ({}) cannot author for on-chain ({}).", native, on_chain), + } + + /// Authoring interface does not match the runtime. + RuntimeVersionMissing { + description("Current runtime has no version"), + display("Authoring for current runtime is not supported since it has no version."), + } + + /// Authoring interface does not match the runtime. + NativeRuntimeMissing { + description("This build has no native runtime"), + display("Authoring in current build is not supported since it has no runtime."), + } + + /// Justification requirements not met. + InvalidJustification { + description("Invalid justification"), + display("Invalid justification."), + } + + /// Some other error. + Other(e: Box<::std::error::Error + Send>) { + description("Other error") + display("Other error: {}", e.description()) + } + } +} + +impl From<::rhododendron::InputStreamConcluded> for Error { + fn from(_: ::rhododendron::InputStreamConcluded) -> Error { + ErrorKind::IoTerminated.into() + } +} diff --git a/core/bft/src/lib.rs b/core/bft/src/lib.rs new file mode 100644 index 0000000000000..e6f2615f0fd53 --- /dev/null +++ b/core/bft/src/lib.rs @@ -0,0 +1,1124 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +// tag::description[] +//! BFT Agreement based on a rotating proposer in different rounds. +//! +//! Where this crate refers to input stream, should never logically conclude. +//! The logic in this crate assumes that messages flushed to the output stream +//! will eventually reach other nodes and that our own messages are not included +//! in the input stream. +//! +//! Note that it is possible to witness agreement being reached without ever +//! seeing the candidate. Any candidates seen will be checked for validity. +//! +//! Although technically the agreement will always complete (given the eventual +//! delivery of messages), in practice it is possible for this future to +//! conclude without having witnessed the conclusion. +//! In general, this future should be pre-empted by the import of a justification +//! set for this block height. +// end::description[] + +#![recursion_limit="128"] + +pub mod error; + +extern crate substrate_codec as codec; +extern crate substrate_primitives as primitives; +extern crate substrate_runtime_support as runtime_support; +extern crate substrate_runtime_primitives as runtime_primitives; +extern crate substrate_runtime_version as runtime_version; +extern crate tokio; +extern crate parking_lot; +extern crate rhododendron; + +#[macro_use] +extern crate log; + +extern crate futures; + +#[macro_use] +extern crate error_chain; + +use std::sync::Arc; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::time::{Instant, Duration}; + +use codec::Encode; +use runtime_primitives::generic::BlockId; +use runtime_primitives::traits::{Block, Header}; +use runtime_primitives::bft::{Message as PrimitiveMessage, Action as PrimitiveAction, Justification as PrimitiveJustification}; +use primitives::{AuthorityId, ed25519, ed25519::LocalizedSignature}; + +use futures::{Async, Stream, Sink, Future, IntoFuture}; +use futures::sync::oneshot; +use tokio::timer::Delay; +use parking_lot::Mutex; + +pub use rhododendron::{InputStreamConcluded, AdvanceRoundReason}; +pub use error::{Error, ErrorKind}; + +// statuses for an agreement +mod status { + pub const LIVE: usize = 0; + pub const BAD: usize = 1; + pub const GOOD: usize = 2; +} + +/// Messages over the proposal. +/// Each message carries an associated round number. +pub type Message = rhododendron::Message::Hash>; + +/// Localized message type. +pub type LocalizedMessage = rhododendron::LocalizedMessage< + B, + ::Hash, + AuthorityId, + LocalizedSignature +>; + +/// Justification of some hash. +pub type Justification = rhododendron::Justification; + +/// Justification of a prepare message. +pub type PrepareJustification = rhododendron::PrepareJustification; + +/// Unchecked justification. +pub struct UncheckedJustification(rhododendron::UncheckedJustification); + +impl UncheckedJustification { + /// Create a new, unchecked justification. + pub fn new(digest: H, signatures: Vec, round_number: usize) -> Self { + UncheckedJustification(rhododendron::UncheckedJustification { + digest, + signatures, + round_number, + }) + } +} + +impl From> for UncheckedJustification { + fn from(inner: rhododendron::UncheckedJustification) -> Self { + UncheckedJustification(inner) + } +} + +impl From> for UncheckedJustification { + fn from(just: PrimitiveJustification) -> Self { + UncheckedJustification(rhododendron::UncheckedJustification { + round_number: just.round_number as usize, + digest: just.hash, + signatures: just.signatures.into_iter().map(|(from, sig)| LocalizedSignature { + signer: from.into(), + signature: sig, + }).collect(), + }) + } +} + +impl Into> for UncheckedJustification { + fn into(self) -> PrimitiveJustification { + PrimitiveJustification { + round_number: self.0.round_number as u32, + hash: self.0.digest, + signatures: self.0.signatures.into_iter().map(|s| (s.signer.into(), s.signature)).collect(), + } + } +} + +/// Result of a committed round of BFT +pub type Committed = rhododendron::Committed::Hash, LocalizedSignature>; + +/// Communication between BFT participants. +pub type Communication = rhododendron::Communication::Hash, AuthorityId, LocalizedSignature>; + +/// Misbehavior observed from BFT participants. +pub type Misbehavior = rhododendron::Misbehavior; + +/// Environment producer for a BFT instance. Creates proposer instance and communication streams. +pub trait Environment { + /// The proposer type this creates. + type Proposer: Proposer; + /// The input stream type. + type Input: Stream, Error=>::Error>; + /// The output stream type. + type Output: Sink, SinkError=>::Error>; + /// Error which can occur upon creation. + type Error: From; + + /// Initialize the proposal logic on top of a specific header. + /// Produces the proposer and message streams for this instance of BFT agreement. + // TODO: provide state context explicitly? + fn init(&self, parent_header: &B::Header, authorities: &[AuthorityId], sign_with: Arc) + -> Result<(Self::Proposer, Self::Input, Self::Output), Self::Error>; +} + +/// Logic for a proposer. +/// +/// This will encapsulate creation and evaluation of proposals at a specific +/// block. +pub trait Proposer { + /// Error type which can occur when proposing or evaluating. + type Error: From + From + ::std::fmt::Debug + 'static; + /// Future that resolves to a committed proposal. + type Create: IntoFuture; + /// Future that resolves when a proposal is evaluated. + type Evaluate: IntoFuture; + + /// Create a proposal. + fn propose(&self) -> Self::Create; + + /// Evaluate proposal. True means valid. + fn evaluate(&self, proposal: &B) -> Self::Evaluate; + + /// Import witnessed misbehavior. + fn import_misbehavior(&self, misbehavior: Vec<(AuthorityId, Misbehavior)>); + + /// Determine the proposer for a given round. This should be a deterministic function + /// with consistent results across all authorities. + fn round_proposer(&self, round_number: usize, authorities: &[AuthorityId]) -> AuthorityId; + + /// Hook called when a BFT round advances without a proposal. + fn on_round_end(&self, _round_number: usize, _proposed: bool) { } +} + +/// Block import trait. +pub trait BlockImport { + /// Import a block alongside its corresponding justification. + fn import_block(&self, block: B, justification: Justification, authorities: &[AuthorityId]) -> bool; +} + +/// Trait for getting the authorities at a given block. +pub trait Authorities { + /// Get the authorities at the given block. + fn authorities(&self, at: &BlockId) -> Result, Error>; +} + +// caches the round number to start at if we end up with BFT consensus on the same +// parent hash more than once (happens if block is bad). +// +// this will force a committed but locally-bad block to be considered analogous to +// a round advancement vote. +#[derive(Debug)] +struct RoundCache { + hash: Option, + start_round: usize, +} + +/// Instance of BFT agreement. +struct BftInstance { + key: Arc, + authorities: Vec, + parent_hash: B::Hash, + round_timeout_multiplier: u64, + cache: Arc>>, + proposer: P, +} + +impl> BftInstance + where + B: Clone + Eq, + B::Hash: ::std::hash::Hash + +{ + fn round_timeout_duration(&self, round: usize) -> Duration { + // 2^(min(6, x/8)) * 10 + // Grows exponentially starting from 10 seconds, capped at 640 seconds. + const ROUND_INCREMENT_STEP: usize = 8; + + let round = round / ROUND_INCREMENT_STEP; + let round = ::std::cmp::min(6, round) as u32; + + let timeout = 1u64.checked_shl(round) + .unwrap_or_else(u64::max_value) + .saturating_mul(self.round_timeout_multiplier); + + Duration::from_secs(timeout) + } + + fn update_round_cache(&self, current_round: usize) { + let mut cache = self.cache.lock(); + if cache.hash.as_ref() == Some(&self.parent_hash) { + cache.start_round = current_round + 1; + } + } +} + +impl> rhododendron::Context for BftInstance + where + B: Clone + Eq, + B::Hash: ::std::hash::Hash, +{ + type Error = P::Error; + type AuthorityId = AuthorityId; + type Digest = B::Hash; + type Signature = LocalizedSignature; + type Candidate = B; + type RoundTimeout = Box>; + type CreateProposal = ::Future; + type EvaluateProposal = ::Future; + + fn local_id(&self) -> AuthorityId { + self.key.public().into() + } + + fn proposal(&self) -> Self::CreateProposal { + self.proposer.propose().into_future() + } + + fn candidate_digest(&self, proposal: &B) -> B::Hash { + proposal.hash() + } + + fn sign_local(&self, message: Message) -> LocalizedMessage { + sign_message(message, &*self.key, self.parent_hash.clone()) + } + + fn round_proposer(&self, round: usize) -> AuthorityId { + self.proposer.round_proposer(round, &self.authorities[..]) + } + + fn proposal_valid(&self, proposal: &B) -> Self::EvaluateProposal { + self.proposer.evaluate(proposal).into_future() + } + + fn begin_round_timeout(&self, round: usize) -> Self::RoundTimeout { + let timeout = self.round_timeout_duration(round); + let fut = Delay::new(Instant::now() + timeout) + .map_err(|e| Error::from(ErrorKind::FaultyTimer(e))) + .map_err(Into::into); + + Box::new(fut) + } + + fn on_advance_round( + &self, + accumulator: &::rhododendron::Accumulator, + round: usize, + next_round: usize, + reason: AdvanceRoundReason, + ) { + use std::collections::HashSet; + + let collect_pubkeys = |participants: HashSet<&Self::AuthorityId>| participants.into_iter() + .map(|p| ed25519::Public::from_raw(p.0)) + .collect::>(); + + let round_timeout = self.round_timeout_duration(next_round); + debug!(target: "bft", "Advancing to round {} from {}", next_round, round); + debug!(target: "bft", "Participating authorities: {:?}", + collect_pubkeys(accumulator.participants())); + debug!(target: "bft", "Voting authorities: {:?}", + collect_pubkeys(accumulator.voters())); + debug!(target: "bft", "Round {} should end in at most {} seconds from now", next_round, round_timeout.as_secs()); + + self.update_round_cache(next_round); + + if let AdvanceRoundReason::Timeout = reason { + self.proposer.on_round_end(round, accumulator.proposal().is_some()); + } + } +} + +/// A future that resolves either when canceled (witnessing a block from the network at same height) +/// or when agreement completes. +pub struct BftFuture where + B: Block + Clone + Eq, + B::Hash: ::std::hash::Hash, + P: Proposer, + InStream: Stream, Error=P::Error>, + OutSink: Sink, SinkError=P::Error>, +{ + inner: rhododendron::Agreement, InStream, OutSink>, + status: Arc, + cancel: oneshot::Receiver<()>, + import: Arc, +} + +impl Future for BftFuture where + B: Block + Clone + Eq, + B::Hash: ::std::hash::Hash, + P: Proposer, + P::Error: ::std::fmt::Display, + I: BlockImport, + InStream: Stream, Error=P::Error>, + OutSink: Sink, SinkError=P::Error>, +{ + type Item = (); + type Error = (); + + fn poll(&mut self) -> ::futures::Poll<(), ()> { + // service has canceled the future. bail + let cancel = match self.cancel.poll() { + Ok(Async::Ready(())) | Err(_) => true, + Ok(Async::NotReady) => false, + }; + + // TODO: handle and log this error in a way which isn't noisy on exit. + let committed = match self.inner.poll().map_err(|_| ()) { + Ok(Async::Ready(x)) => x, + Ok(Async::NotReady) => + return Ok(if cancel { Async::Ready(()) } else { Async::NotReady }), + Err(()) => return Err(()), + }; + + // if something was committed, the round leader must have proposed. + self.inner.context().proposer.on_round_end(committed.round_number, true); + + // If we didn't see the proposal (very unlikely), + // we will get the block from the network later. + if let Some(justified_block) = committed.candidate { + let hash = justified_block.hash(); + info!(target: "bft", "Importing block #{} ({}) directly from BFT consensus", + justified_block.header().number(), hash); + + let import_ok = self.import.import_block( + justified_block, + committed.justification, + &self.inner.context().authorities + ); + + if !import_ok { + warn!(target: "bft", "{:?} was bad block agreed on in round #{}", + hash, committed.round_number); + self.status.store(status::BAD, Ordering::Release); + } else { + self.status.store(status::GOOD, Ordering::Release); + } + } else { + // assume good unless we received the proposal. + self.status.store(status::GOOD, Ordering::Release); + } + + self.inner.context().update_round_cache(committed.round_number); + + Ok(Async::Ready(())) + } +} + +impl Drop for BftFuture where + B: Block + Clone + Eq, + B::Hash: ::std::hash::Hash, + P: Proposer, + InStream: Stream, Error=P::Error>, + OutSink: Sink, SinkError=P::Error>, +{ + fn drop(&mut self) { + // TODO: have a trait member to pass misbehavior reports into. + let misbehavior = self.inner.drain_misbehavior().collect::>(); + self.inner.context().proposer.import_misbehavior(misbehavior); + } +} + +struct AgreementHandle { + status: Arc, + send_cancel: Option>, +} + +impl AgreementHandle { + fn status(&self) -> usize { + self.status.load(Ordering::Acquire) + } +} + +impl Drop for AgreementHandle { + fn drop(&mut self) { + if let Some(sender) = self.send_cancel.take() { + let _ = sender.send(()); + } + } +} + +/// The BftService kicks off the agreement process on top of any blocks it +/// is notified of. +/// +/// This assumes that it is being run in the context of a tokio runtime. +pub struct BftService { + client: Arc, + live_agreement: Mutex>, + round_cache: Arc>>, + round_timeout_multiplier: u64, + key: Arc, // TODO: key changing over time. + factory: P, +} + +impl BftService + where + B: Block + Clone + Eq, + P: Environment, + >::Error: ::std::fmt::Display, + I: BlockImport + Authorities, +{ + + /// Create a new service instance. + pub fn new(client: Arc, key: Arc, factory: P) -> BftService { + BftService { + client: client, + live_agreement: Mutex::new(None), + round_cache: Arc::new(Mutex::new(RoundCache { + hash: None, + start_round: 0, + })), + round_timeout_multiplier: 10, + key: key, // TODO: key changing over time. + factory, + } + } + + /// Get the local Authority ID. + pub fn local_id(&self) -> AuthorityId { + // TODO: based on a header and some keystore. + self.key.public().into() + } + + /// Signal that a valid block with the given header has been imported. + /// + /// If the local signing key is an authority, this will begin the consensus process to build a + /// block on top of it. If the executor fails to run the future, an error will be returned. + /// Returns `None` if the agreement on the block with given parent is already in progress. + pub fn build_upon(&self, header: &B::Header) + -> Result>::Proposer, + I, +

>::Input, +

>::Output, + >>, P::Error> + where + { + let hash = header.hash(); + + let mut live_agreement = self.live_agreement.lock(); + let can_build = live_agreement.as_ref() + .map_or(true, |x| self.can_build_on_inner(header, x)); + + if !can_build { + return Ok(None) + } + + let authorities = self.client.authorities(&BlockId::Hash(hash.clone()))?; + + let n = authorities.len(); + let max_faulty = max_faulty_of(n); + trace!(target: "bft", "Initiating agreement on top of #{}, {:?}", header.number(), hash); + trace!(target: "bft", "max_faulty_of({})={}", n, max_faulty); + + let local_id = self.local_id(); + + if !authorities.contains(&local_id) { + // cancel current agreement + live_agreement.take(); + Err(ErrorKind::InvalidAuthority(local_id).into())?; + } + + let (proposer, input, output) = self.factory.init(header, &authorities, self.key.clone())?; + + let bft_instance = BftInstance { + proposer, + parent_hash: hash.clone(), + cache: self.round_cache.clone(), + round_timeout_multiplier: self.round_timeout_multiplier, + key: self.key.clone(), + authorities: authorities, + }; + + let mut agreement = rhododendron::agree( + bft_instance, + n, + max_faulty, + input, + output, + ); + + // fast forward round number if necessary. + { + let mut cache = self.round_cache.lock(); + trace!(target: "bft", "Round cache: {:?}", &*cache); + if cache.hash.as_ref() == Some(&hash) { + trace!(target: "bft", "Fast-forwarding to round {}", cache.start_round); + let start_round = cache.start_round; + cache.start_round += 1; + + drop(cache); + agreement.fast_forward(start_round); + } else { + *cache = RoundCache { + hash: Some(hash.clone()), + start_round: 1, + }; + } + } + + let status = Arc::new(AtomicUsize::new(status::LIVE)); + let (tx, rx) = oneshot::channel(); + + // cancel current agreement. + *live_agreement = Some((header.clone(), AgreementHandle { + send_cancel: Some(tx), + status: status.clone(), + })); + + Ok(Some(BftFuture { + inner: agreement, + status: status, + cancel: rx, + import: self.client.clone(), + })) + } + + /// Cancel current agreement if any. + pub fn cancel_agreement(&self) { + self.live_agreement.lock().take(); + } + + /// Whether we can build using the given header. + pub fn can_build_on(&self, header: &B::Header) -> bool { + self.live_agreement.lock().as_ref() + .map_or(true, |x| self.can_build_on_inner(header, x)) + } + + /// Get a reference to the underyling client. + pub fn client(&self) -> &I { &*self.client } + + fn can_build_on_inner(&self, header: &B::Header, live: &(B::Header, AgreementHandle)) -> bool { + let hash = header.hash(); + let &(ref live_header, ref handle) = live; + match handle.status() { + _ if *header != *live_header && *live_header.parent_hash() != hash => true, // can always follow with next block. + status::BAD => hash == live_header.hash(), // bad block can be re-agreed on. + _ => false, // canceled won't appear since we overwrite the handle before returning. + } + } +} + +/// Given a total number of authorities, yield the maximum faulty that would be allowed. +/// This will always be under 1/3. +pub fn max_faulty_of(n: usize) -> usize { + n.saturating_sub(1) / 3 +} + +/// Given a total number of authorities, yield the minimum required signatures. +/// This will always be over 2/3. +pub fn bft_threshold(n: usize) -> usize { + n - max_faulty_of(n) +} + +fn check_justification_signed_message(authorities: &[AuthorityId], message: &[u8], just: UncheckedJustification) + -> Result, UncheckedJustification> +{ + // TODO: return additional error information. + just.0.check(authorities.len() - max_faulty_of(authorities.len()), |_, _, sig| { + let auth_id = sig.signer.clone().into(); + if !authorities.contains(&auth_id) { return None } + + if ed25519::verify_strong(&sig.signature, message, &sig.signer) { + Some(sig.signer.0) + } else { + None + } + }).map_err(UncheckedJustification) +} + +/// Check a full justification for a header hash. +/// Provide all valid authorities. +/// +/// On failure, returns the justification back. +pub fn check_justification(authorities: &[AuthorityId], parent: B::Hash, just: UncheckedJustification) + -> Result, UncheckedJustification> +{ + let message = Encode::encode(&PrimitiveMessage:: { + parent, + action: PrimitiveAction::Commit(just.0.round_number as u32, just.0.digest.clone()), + }); + + check_justification_signed_message(authorities, &message[..], just) +} + +/// Check a prepare justification for a header hash. +/// Provide all valid authorities. +/// +/// On failure, returns the justification back. +pub fn check_prepare_justification(authorities: &[AuthorityId], parent: B::Hash, just: UncheckedJustification) + -> Result, UncheckedJustification> +{ + let message = Encode::encode(&PrimitiveMessage:: { + parent, + action: PrimitiveAction::Prepare(just.0.round_number as u32, just.0.digest.clone()), + }); + + check_justification_signed_message(authorities, &message[..], just) +} + +/// Check proposal message signatures and authority. +/// Provide all valid authorities. +pub fn check_proposal( + authorities: &[AuthorityId], + parent_hash: &B::Hash, + propose: &::rhododendron::LocalizedProposal) + -> Result<(), Error> +{ + if !authorities.contains(&propose.sender) { + return Err(ErrorKind::InvalidAuthority(propose.sender.into()).into()); + } + + let action_header = PrimitiveAction::ProposeHeader(propose.round_number as u32, propose.digest.clone()); + let action_propose = PrimitiveAction::Propose(propose.round_number as u32, propose.proposal.clone()); + check_action::(action_header, parent_hash, &propose.digest_signature)?; + check_action::(action_propose, parent_hash, &propose.full_signature) +} + +/// Check vote message signatures and authority. +/// Provide all valid authorities. +pub fn check_vote( + authorities: &[AuthorityId], + parent_hash: &B::Hash, + vote: &::rhododendron::LocalizedVote) + -> Result<(), Error> +{ + if !authorities.contains(&vote.sender) { + return Err(ErrorKind::InvalidAuthority(vote.sender.into()).into()); + } + + let action = match vote.vote { + ::rhododendron::Vote::Prepare(r, ref h) => PrimitiveAction::Prepare(r as u32, h.clone()), + ::rhododendron::Vote::Commit(r, ref h) => PrimitiveAction::Commit(r as u32, h.clone()), + ::rhododendron::Vote::AdvanceRound(r) => PrimitiveAction::AdvanceRound(r as u32), + }; + check_action::(action, parent_hash, &vote.signature) +} + +fn check_action(action: PrimitiveAction, parent_hash: &B::Hash, sig: &LocalizedSignature) -> Result<(), Error> { + let primitive = PrimitiveMessage { + parent: parent_hash.clone(), + action, + }; + + let message = Encode::encode(&primitive); + if ed25519::verify_strong(&sig.signature, &message, &sig.signer) { + Ok(()) + } else { + Err(ErrorKind::InvalidSignature(sig.signature.into(), sig.signer.clone().into()).into()) + } +} + +/// Sign a BFT message with the given key. +pub fn sign_message(message: Message, key: &ed25519::Pair, parent_hash: B::Hash) -> LocalizedMessage { + let signer = key.public(); + + let sign_action = |action: PrimitiveAction| { + let primitive = PrimitiveMessage { + parent: parent_hash.clone(), + action, + }; + + let to_sign = Encode::encode(&primitive); + LocalizedSignature { + signer: signer.clone(), + signature: key.sign(&to_sign), + } + }; + + match message { + ::rhododendron::Message::Propose(r, proposal) => { + let header_hash = proposal.hash(); + let action_header = PrimitiveAction::ProposeHeader(r as u32, header_hash.clone()); + let action_propose = PrimitiveAction::Propose(r as u32, proposal.clone()); + + ::rhododendron::LocalizedMessage::Propose(::rhododendron::LocalizedProposal { + round_number: r, + proposal, + digest: header_hash, + sender: signer.clone().into(), + digest_signature: sign_action(action_header), + full_signature: sign_action(action_propose), + }) + } + ::rhododendron::Message::Vote(vote) => { + let action = match vote { + ::rhododendron::Vote::Prepare(r, ref h) => PrimitiveAction::Prepare(r as u32, h.clone()), + ::rhododendron::Vote::Commit(r, ref h) => PrimitiveAction::Commit(r as u32, h.clone()), + ::rhododendron::Vote::AdvanceRound(r) => PrimitiveAction::AdvanceRound(r as u32), + }; + + ::rhododendron::LocalizedMessage::Vote(::rhododendron::LocalizedVote { + vote: vote, + sender: signer.clone().into(), + signature: sign_action(action), + }) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::collections::HashSet; + use runtime_primitives::testing::{Block as GenericTestBlock, Header as TestHeader}; + use primitives::H256; + use self::keyring::Keyring; + + extern crate substrate_keyring as keyring; + + type TestBlock = GenericTestBlock<()>; + + struct FakeClient { + authorities: Vec, + imported_heights: Mutex> + } + + impl BlockImport for FakeClient { + fn import_block(&self, block: TestBlock, _justification: Justification, _authorities: &[AuthorityId]) -> bool { + assert!(self.imported_heights.lock().insert(block.header.number)); + true + } + } + + impl Authorities for FakeClient { + fn authorities(&self, _at: &BlockId) -> Result, Error> { + Ok(self.authorities.clone()) + } + } + + // "black hole" output sink. + struct Comms(::std::marker::PhantomData); + + impl Sink for Comms { + type SinkItem = Communication; + type SinkError = E; + + fn start_send(&mut self, _item: Communication) -> ::futures::StartSend, E> { + Ok(::futures::AsyncSink::Ready) + } + + fn poll_complete(&mut self) -> ::futures::Poll<(), E> { + Ok(Async::Ready(())) + } + } + + impl Stream for Comms { + type Item = Communication; + type Error = E; + + fn poll(&mut self) -> ::futures::Poll, Self::Error> { + Ok(::futures::Async::NotReady) + } + } + + struct DummyFactory; + struct DummyProposer(u64); + + impl Environment for DummyFactory { + type Proposer = DummyProposer; + type Input = Comms; + type Output = Comms; + type Error = Error; + + fn init(&self, parent_header: &TestHeader, _authorities: &[AuthorityId], _sign_with: Arc) + -> Result<(DummyProposer, Self::Input, Self::Output), Error> + { + Ok((DummyProposer(parent_header.number + 1), Comms(::std::marker::PhantomData), Comms(::std::marker::PhantomData))) + } + } + + impl Proposer for DummyProposer { + type Error = Error; + type Create = Result; + type Evaluate = Result; + + fn propose(&self) -> Result { + + Ok(TestBlock { + header: from_block_number(self.0), + extrinsics: Default::default() + }) + } + + fn evaluate(&self, proposal: &TestBlock) -> Result { + Ok(proposal.header.number == self.0) + } + + fn import_misbehavior(&self, _misbehavior: Vec<(AuthorityId, Misbehavior)>) {} + + fn round_proposer(&self, round_number: usize, authorities: &[AuthorityId]) -> AuthorityId { + authorities[round_number % authorities.len()].clone() + } + } + + fn make_service(client: FakeClient) + -> BftService + { + BftService { + client: Arc::new(client), + live_agreement: Mutex::new(None), + round_cache: Arc::new(Mutex::new(RoundCache { + hash: None, + start_round: 0, + })), + round_timeout_multiplier: 10, + key: Arc::new(Keyring::One.into()), + factory: DummyFactory + } + } + + fn sign_vote(vote: ::rhododendron::Vote, key: &ed25519::Pair, parent_hash: H256) -> LocalizedSignature { + match sign_message::(vote.into(), key, parent_hash) { + ::rhododendron::LocalizedMessage::Vote(vote) => vote.signature, + _ => panic!("signing vote leads to signed vote"), + } + } + + fn from_block_number(num: u64) -> TestHeader { + TestHeader::new( + num, + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ) + } + + #[test] + fn future_gets_preempted() { + let client = FakeClient { + authorities: vec![ + Keyring::One.to_raw_public().into(), + Keyring::Two.to_raw_public().into(), + Keyring::Alice.to_raw_public().into(), + Keyring::Eve.to_raw_public().into(), + ], + imported_heights: Mutex::new(HashSet::new()), + }; + + let service = make_service(client); + + let first = from_block_number(2); + let first_hash = first.hash(); + + let mut second = from_block_number(3); + second.parent_hash = first_hash; + let _second_hash = second.hash(); + + let mut first_bft = service.build_upon(&first).unwrap().unwrap(); + assert!(service.live_agreement.lock().as_ref().unwrap().0 == first); + + let _second_bft = service.build_upon(&second).unwrap(); + assert!(service.live_agreement.lock().as_ref().unwrap().0 != first); + assert!(service.live_agreement.lock().as_ref().unwrap().0 == second); + + // first_bft has been cancelled. need to swap out so we can check it. + let (_tx, mut rx) = oneshot::channel(); + ::std::mem::swap(&mut rx, &mut first_bft.cancel); + + assert!(rx.wait().is_ok()); + } + + #[test] + fn max_faulty() { + assert_eq!(max_faulty_of(3), 0); + assert_eq!(max_faulty_of(4), 1); + assert_eq!(max_faulty_of(100), 33); + assert_eq!(max_faulty_of(0), 0); + assert_eq!(max_faulty_of(11), 3); + assert_eq!(max_faulty_of(99), 32); + } + + #[test] + fn justification_check_works() { + let parent_hash = Default::default(); + let hash = [0xff; 32].into(); + + let authorities = vec![ + Keyring::One.to_raw_public().into(), + Keyring::Two.to_raw_public().into(), + Keyring::Alice.to_raw_public().into(), + Keyring::Eve.to_raw_public().into(), + ]; + + let authorities_keys = vec![ + Keyring::One.into(), + Keyring::Two.into(), + Keyring::Alice.into(), + Keyring::Eve.into(), + ]; + + let unchecked = UncheckedJustification(rhododendron::UncheckedJustification { + digest: hash, + round_number: 1, + signatures: authorities_keys.iter().take(3).map(|key| { + sign_vote(rhododendron::Vote::Commit(1, hash).into(), key, parent_hash) + }).collect(), + }); + + assert!(check_justification::(&authorities, parent_hash, unchecked).is_ok()); + + let unchecked = UncheckedJustification(rhododendron::UncheckedJustification { + digest: hash, + round_number: 0, // wrong round number (vs. the signatures) + signatures: authorities_keys.iter().take(3).map(|key| { + sign_vote(rhododendron::Vote::Commit(1, hash).into(), key, parent_hash) + }).collect(), + }); + + assert!(check_justification::(&authorities, parent_hash, unchecked).is_err()); + + // not enough signatures. + let unchecked = UncheckedJustification(rhododendron::UncheckedJustification { + digest: hash, + round_number: 1, + signatures: authorities_keys.iter().take(2).map(|key| { + sign_vote(rhododendron::Vote::Commit(1, hash).into(), key, parent_hash) + }).collect(), + }); + + assert!(check_justification::(&authorities, parent_hash, unchecked).is_err()); + + // wrong hash. + let unchecked = UncheckedJustification(rhododendron::UncheckedJustification { + digest: [0xfe; 32].into(), + round_number: 1, + signatures: authorities_keys.iter().take(3).map(|key| { + sign_vote(rhododendron::Vote::Commit(1, hash).into(), key, parent_hash) + }).collect(), + }); + + assert!(check_justification::(&authorities, parent_hash, unchecked).is_err()); + } + + #[test] + fn propose_check_works() { + let parent_hash = Default::default(); + + let authorities = vec![ + Keyring::Alice.to_raw_public().into(), + Keyring::Eve.to_raw_public().into(), + ]; + + let block = TestBlock { + header: from_block_number(1), + extrinsics: Default::default() + }; + + let proposal = sign_message(::rhododendron::Message::Propose(1, block.clone()), &Keyring::Alice.pair(), parent_hash);; + if let ::rhododendron::LocalizedMessage::Propose(proposal) = proposal { + assert!(check_proposal(&authorities, &parent_hash, &proposal).is_ok()); + let mut invalid_round = proposal.clone(); + invalid_round.round_number = 0; + assert!(check_proposal(&authorities, &parent_hash, &invalid_round).is_err()); + let mut invalid_digest = proposal.clone(); + invalid_digest.digest = [0xfe; 32].into(); + assert!(check_proposal(&authorities, &parent_hash, &invalid_digest).is_err()); + } else { + assert!(false); + } + + // Not an authority + let proposal = sign_message::(::rhododendron::Message::Propose(1, block), &Keyring::Bob.pair(), parent_hash);; + if let ::rhododendron::LocalizedMessage::Propose(proposal) = proposal { + assert!(check_proposal(&authorities, &parent_hash, &proposal).is_err()); + } else { + assert!(false); + } + } + + #[test] + fn vote_check_works() { + let parent_hash: H256 = Default::default(); + let hash: H256 = [0xff; 32].into(); + + let authorities = vec![ + Keyring::Alice.to_raw_public().into(), + Keyring::Eve.to_raw_public().into(), + ]; + + let vote = sign_message::(::rhododendron::Message::Vote(::rhododendron::Vote::Prepare(1, hash)), &Keyring::Alice.pair(), parent_hash);; + if let ::rhododendron::LocalizedMessage::Vote(vote) = vote { + assert!(check_vote::(&authorities, &parent_hash, &vote).is_ok()); + let mut invalid_sender = vote.clone(); + invalid_sender.signature.signer = Keyring::Eve.into(); + assert!(check_vote::(&authorities, &parent_hash, &invalid_sender).is_err()); + } else { + assert!(false); + } + + // Not an authority + let vote = sign_message::(::rhododendron::Message::Vote(::rhododendron::Vote::Prepare(1, hash)), &Keyring::Bob.pair(), parent_hash);; + if let ::rhododendron::LocalizedMessage::Vote(vote) = vote { + assert!(check_vote::(&authorities, &parent_hash, &vote).is_err()); + } else { + assert!(false); + } + } + + #[test] + fn drop_bft_future_does_not_deadlock() { + let client = FakeClient { + authorities: vec![ + Keyring::One.to_raw_public().into(), + Keyring::Two.to_raw_public().into(), + Keyring::Alice.to_raw_public().into(), + Keyring::Eve.to_raw_public().into(), + ], + imported_heights: Mutex::new(HashSet::new()), + }; + + let service = make_service(client); + + let first = from_block_number(2); + let first_hash = first.hash(); + + let mut second = from_block_number(3); + second.parent_hash = first_hash; + + let _ = service.build_upon(&first).unwrap(); + assert!(service.live_agreement.lock().as_ref().unwrap().0 == first); + service.live_agreement.lock().take(); + } + + #[test] + fn bft_can_build_though_skipped() { + let client = FakeClient { + authorities: vec![ + Keyring::One.to_raw_public().into(), + Keyring::Two.to_raw_public().into(), + Keyring::Alice.to_raw_public().into(), + Keyring::Eve.to_raw_public().into(), + ], + imported_heights: Mutex::new(HashSet::new()), + }; + + let service = make_service(client); + + let first = from_block_number(2); + let first_hash = first.hash(); + + let mut second = from_block_number(3); + second.parent_hash = first_hash; + + let mut third = from_block_number(4); + third.parent_hash = second.hash(); + + let _ = service.build_upon(&first).unwrap(); + assert!(service.live_agreement.lock().as_ref().unwrap().0 == first); + // BFT has not seen second, but will move forward on third + service.build_upon(&third).unwrap(); + assert!(service.live_agreement.lock().as_ref().unwrap().0 == third); + + // but we are not walking backwards + service.build_upon(&second).unwrap(); + assert!(service.live_agreement.lock().as_ref().unwrap().0 == third); + } +} diff --git a/core/cli/Cargo.toml b/core/cli/Cargo.toml new file mode 100644 index 0000000000000..ef24f7cf2b442 --- /dev/null +++ b/core/cli/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "substrate-cli" +version = "0.3.0" +authors = ["Parity Technologies "] +description = "Substrate CLI interface." +build = "build.rs" + +[dependencies] +clap = { version = "~2.32", features = ["yaml"] } +backtrace = "0.3" +env_logger = "0.4" +error-chain = "0.12" +log = "0.3" +atty = "0.2" +regex = "1" +time = "0.1" +slog = "^2" +ansi_term = "0.10" +lazy_static = "1.0" +app_dirs = "1.2" +tokio = "0.1.7" +futures = "0.1.17" +fdlimit = "0.1" +exit-future = "0.1" +sysinfo = "0.5.7" +substrate-client = { path = "../client" } +substrate-network = { path = "../network" } +substrate-network-libp2p = { path = "../network-libp2p" } +substrate-runtime-primitives = { path = "../../runtime/primitives" } +substrate-service = { path = "../service" } +substrate-telemetry = { path = "../telemetry" } +names = "0.11.0" + +[build-dependencies] +clap = "~2.32" diff --git a/core/cli/README.adoc b/core/cli/README.adoc new file mode 100644 index 0000000000000..2b9b74362d322 --- /dev/null +++ b/core/cli/README.adoc @@ -0,0 +1,13 @@ + += Substrate CLI + +.Summary +[source, toml] +---- +include::Cargo.toml[lines=2..5] +---- + +.Description +Polkadot CLI library + +include::doc/shell-completion.adoc[] diff --git a/core/cli/build.rs b/core/cli/build.rs new file mode 100644 index 0000000000000..645e98d5e8a9c --- /dev/null +++ b/core/cli/build.rs @@ -0,0 +1,59 @@ +// Copyright 2017 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 . + +#[macro_use] +extern crate clap; + +use std::fs; +use std::env; +use clap::Shell; +use std::path::Path; + +fn main() { + build_shell_completion(); +} + +/// Build shell completion scripts for all known shells +/// Full list in https://github.com/kbknapp/clap-rs/blob/e9d0562a1dc5dfe731ed7c767e6cee0af08f0cf9/src/app/parser.rs#L123 +fn build_shell_completion() { + let shells = [Shell::Bash, Shell::Fish, Shell::Zsh, Shell::Elvish, Shell::PowerShell]; + for shell in shells.iter() { + build_completion(shell); + } +} + +/// Build the shell auto-completion for a given Shell +fn build_completion(shell: &Shell) { + let yml = load_yaml!("src/cli.yml"); + + let outdir = match env::var_os("OUT_DIR") { + None => return, + Some(dir) => dir, + }; + let path = Path::new(&outdir) + .parent().unwrap() + .parent().unwrap() + .parent().unwrap() + .join("completion-scripts"); + + fs::create_dir(&path).ok(); + + let mut app = clap::App::from_yaml(&yml); + app.gen_completions( + "polkadot", + *shell, + &path); +} diff --git a/core/cli/doc/shell-completion.adoc b/core/cli/doc/shell-completion.adoc new file mode 100644 index 0000000000000..8afbd37adb9f4 --- /dev/null +++ b/core/cli/doc/shell-completion.adoc @@ -0,0 +1,41 @@ + +== Shell completion + +The Substrate cli command supports shell auto-completion. For this to work, you will need to run the completion script matching you build and system. + +Assuming you built a release version using `cargo build --release` and use `bash` run the following: + +[source, shell] +source target/release/completion-scripts/substrate.bash + +You can find completion scripts for: +- bash +- fish +- zsh +- elvish +- powershell + +To make this change persistent, you can proceed as follow: + +.First install + +[source, shell] +---- +COMPL_DIR=$HOME/.completion +mkdir -p $COMPL_DIR +cp -f target/release/completion-scripts/substrate.bash $COMPL_DIR/ +echo "source $COMPL_DIR/substrate.bash" >> $HOME/.bash_profile +source $HOME/.bash_profile +---- + +.Update + +When you build a new version of Substrate, the following will ensure you auto-completion script matches the current binary: + +[source, shell] +---- +COMPL_DIR=$HOME/.completion +mkdir -p $COMPL_DIR +cp -f target/release/completion-scripts/substrate.bash $COMPL_DIR/ +source $HOME/.bash_profile +---- diff --git a/core/cli/src/cli.yml b/core/cli/src/cli.yml new file mode 100644 index 0000000000000..96c80e111c536 --- /dev/null +++ b/core/cli/src/cli.yml @@ -0,0 +1,229 @@ +name: {name} +author: {author} +about: {description} +args: + - log: + short: l + long: log + value_name: LOG_PATTERN + help: Sets a custom logging filter + takes_value: true + - base-path: + long: base-path + short: d + value_name: PATH + help: Specify custom base path + takes_value: true + - keystore-path: + long: keystore-path + value_name: PATH + help: Specify custom keystore path + takes_value: true + - key: + long: key + value_name: STRING + help: Specify additional key seed + takes_value: true + - node-key: + long: node-key + value_name: KEY + help: Specify node secret key (64-character hex string) + takes_value: true + - validator: + long: validator + help: Enable validator mode + takes_value: false + - light: + long: light + help: Run in light client mode + takes_value: false + - dev: + long: dev + help: Run in development mode; implies --chain=dev --validator --key Alice + takes_value: false + - listen-addr: + long: listen-addr + value_name: LISTEN_ADDR + help: Listen on this multiaddress + takes_value: true + multiple: true + - port: + long: port + value_name: PORT + help: Specify p2p protocol TCP port. Only used if --listen-addr is not specified. + takes_value: true + - rpc-external: + long: rpc-external + help: Listen to all RPC interfaces (default is local) + takes_value: false + - ws-external: + long: ws-external + help: Listen to all Websocket interfaces (default is local) + takes_value: false + - rpc-port: + long: rpc-port + value_name: PORT + help: Specify HTTP RPC server TCP port + takes_value: true + - ws-port: + long: ws-port + value_name: PORT + help: Specify WebSockets RPC server TCP port + takes_value: true + - bootnodes: + long: bootnodes + value_name: URL + help: Specify a list of bootnodes + takes_value: true + multiple: true + - reserved-nodes: + long: reserved-nodes + value_name: URL + help: Specify a list of reserved node addresses + takes_value: true + multiple: true + - min-peers: + long: min-peers + value_name: MIN_PEERS + help: Specify the minimum number of peers + takes_value: true + - max-peers: + long: max-peers + value_name: MAX_PEERS + help: Specify the maximum number of peers + takes_value: true + - chain: + long: chain + value_name: CHAIN_SPEC + help: Specify the chain specification (one of krummelanke, dev, local or staging) + takes_value: true + - pruning: + long: pruning + value_name: PRUNING_MODE + help: Specify the pruning mode, a number of blocks to keep or "archive". Default is 256. + takes_value: true + - name: + long: name + value_name: NAME + help: The human-readable name for this node, as reported to the telemetry server, if enabled + takes_value: true + - telemetry: + short: t + long: telemetry + help: Should connect to the Polkadot telemetry server (telemetry is off by default on local chains) + takes_value: false + - no-telemetry: + long: no-telemetry + help: Should not connect to the Polkadot telemetry server (telemetry is on by default on global chains) + takes_value: false + - telemetry-url: + long: telemetry-url + value_name: TELEMETRY_URL + help: The URL of the telemetry server. Implies --telemetry + takes_value: true + - execution: + long: execution + value_name: STRATEGY + help: The means of execution used when calling into the runtime. Can be either wasm, native or both. +subcommands: + - build-spec: + about: Build a spec.json file, outputing to stdout + args: + - raw: + long: raw + help: Force raw genesis storage output. + takes_value: false + - chain: + long: chain + value_name: CHAIN_SPEC + help: Specify the chain specification (one of krummelanke, dev, local or staging) + takes_value: true + - export-blocks: + about: Export blocks to a file + args: + - OUTPUT: + index: 1 + help: Output file name or stdout if unspecified. + required: false + - chain: + long: chain + value_name: CHAIN_SPEC + help: Specify the chain specification. + takes_value: true + - base-path: + long: base-path + short: d + value_name: PATH + help: Specify custom base path. + takes_value: true + - from: + long: from + value_name: BLOCK + help: Specify starting block number. 1 by default. + takes_value: true + - to: + long: to + value_name: BLOCK + help: Specify last block number. Best block by default. + takes_value: true + - json: + long: json + help: Use JSON output rather than binary. + takes_value: false + - import-blocks: + about: Import blocks from file. + args: + - INPUT: + index: 1 + help: Input file or stdin if unspecified. + required: false + - chain: + long: chain + value_name: CHAIN_SPEC + help: Specify the chain specification. + takes_value: true + - base-path: + long: base-path + short: d + value_name: PATH + help: Specify custom base path. + takes_value: true + - execution: + long: execution + value_name: STRATEGY + help: The means of execution used when calling into the runtime. Can be either wasm, native or both. + - max-heap-pages: + long: max-heap-pages + value_name: COUNT + help: The maximum number of 64KB pages to ever allocate for Wasm execution. Don't alter this unless you know what you're doing. + - revert: + about: Revert chain to the previous state + args: + - NUM: + index: 1 + help: Number of blocks to revert. Default is 256. + - chain: + long: chain + value_name: CHAIN_SPEC + help: Specify the chain specification. + takes_value: true + - base-path: + long: base-path + short: d + value_name: PATH + help: Specify custom base path. + takes_value: true + - purge-chain: + about: Remove the whole chain data. + args: + - chain: + long: chain + value_name: CHAIN_SPEC + help: Specify the chain specification. + takes_value: true + - base-path: + long: base-path + short: d + value_name: PATH + help: Specify custom base path. + takes_value: true diff --git a/core/cli/src/error.rs b/core/cli/src/error.rs new file mode 100644 index 0000000000000..ec70a5b70b7c8 --- /dev/null +++ b/core/cli/src/error.rs @@ -0,0 +1,37 @@ +// Copyright 2017 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 . + +//! Initialization errors. + +use client; + +error_chain! { + foreign_links { + Io(::std::io::Error) #[doc="IO error"]; + Cli(::clap::Error) #[doc="CLI error"]; + Service(::service::Error) #[doc="Substrate service error"]; + } + links { + Client(client::error::Error, client::error::ErrorKind) #[doc="Client error"]; + } + errors { + /// Input error. + Input(m: String) { + description("Invalid input"), + display("{}", m), + } + } +} diff --git a/core/cli/src/informant.rs b/core/cli/src/informant.rs new file mode 100644 index 0000000000000..eacc95c50dbf7 --- /dev/null +++ b/core/cli/src/informant.rs @@ -0,0 +1,124 @@ +// Copyright 2017 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 . + +//! Console informant. Prints sync progress and block events. Runs on the calling thread. + +use ansi_term::Colour; +use std::time::{Duration, Instant}; +use futures::{Future, Stream}; +use service::{Service, Components}; +use tokio::runtime::TaskExecutor; +use tokio::timer::Interval; +use sysinfo::{get_current_pid, ProcessExt, System, SystemExt}; +use network::{SyncState, SyncProvider}; +use client::BlockchainEvents; +use runtime_primitives::traits::{Header, As}; + +const TIMER_INTERVAL_MS: u64 = 5000; + +/// Spawn informant on the event loop +pub fn start(service: &Service, exit: ::exit_future::Exit, handle: TaskExecutor) + where + C: Components, +{ + let interval = Interval::new(Instant::now(), Duration::from_millis(TIMER_INTERVAL_MS)); + + let network = service.network(); + let client = service.client(); + let txpool = service.extrinsic_pool(); + let mut last_number = None; + + let mut sys = System::new(); + let self_pid = get_current_pid(); + + let display_notifications = interval.map_err(|e| debug!("Timer error: {:?}", e)).for_each(move |_| { + let sync_status = network.status(); + + if let Ok(best_block) = client.best_block_header() { + let hash = best_block.hash(); + let num_peers = sync_status.num_peers; + let best_number: u64 = best_block.number().as_(); + let speed = move || speed(best_number, last_number); + let (status, target) = match (sync_status.sync.state, sync_status.sync.best_seen_block) { + (SyncState::Idle, _) => ("Idle".into(), "".into()), + (SyncState::Downloading, None) => (format!("Syncing{}", speed()), "".into()), + (SyncState::Downloading, Some(n)) => (format!("Syncing{}", speed()), format!(", target=#{}", n)), + }; + last_number = Some(best_number); + let txpool_status = txpool.light_status(); + info!( + target: "substrate", + "{}{} ({} peers), best: #{} ({})", + Colour::White.bold().paint(&status), + target, + Colour::White.bold().paint(format!("{}", sync_status.num_peers)), + Colour::White.paint(format!("{}", best_number)), + hash + ); + + // 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()) + } else { (0.0, 0) }; + + telemetry!( + "system.interval"; + "status" => format!("{}{}", status, target), + "peers" => num_peers, + "height" => best_number, + "best" => ?hash, + "txcount" => txpool_status.transaction_count, + "cpu" => cpu_usage, + "memory" => memory + ); + } else { + warn!("Error getting best block information"); + } + + Ok(()) + }); + + let client = service.client(); + let display_block_import = client.import_notification_stream().for_each(|n| { + info!(target: "substrate", "Imported #{} ({})", n.header.number(), n.hash); + Ok(()) + }); + + let txpool = service.extrinsic_pool(); + let display_txpool_import = txpool.import_notification_stream().for_each(move |_| { + let status = txpool.light_status(); + telemetry!("txpool.import"; "mem_usage" => status.mem_usage, "count" => status.transaction_count, "sender" => status.senders); + Ok(()) + }); + + let informant_work = display_notifications.join3(display_block_import, display_txpool_import); + handle.spawn(exit.until(informant_work).map(|_| ())); +} + +fn speed(best_number: u64, last_number: Option) -> String { + let speed = match last_number { + Some(num) => (best_number.saturating_sub(num) * 10_000 / TIMER_INTERVAL_MS) as f64, + None => 0.0 + }; + + if speed < 1.0 { + "".into() + } else { + format!(" {:4.1} bps", speed / 10.0) + } +} + diff --git a/core/cli/src/lib.rs b/core/cli/src/lib.rs new file mode 100644 index 0000000000000..94d8849b05b35 --- /dev/null +++ b/core/cli/src/lib.rs @@ -0,0 +1,571 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +// tag::description[] +//! Substrate CLI library. +// end::description[] + +#![warn(missing_docs)] +#![warn(unused_extern_crates)] + +extern crate app_dirs; +extern crate env_logger; +extern crate atty; +extern crate ansi_term; +extern crate regex; +extern crate time; +extern crate fdlimit; +extern crate futures; +extern crate tokio; +extern crate names; +extern crate backtrace; +extern crate sysinfo; + +extern crate substrate_client as client; +extern crate substrate_network as network; +extern crate substrate_network_libp2p as network_libp2p; +extern crate substrate_runtime_primitives as runtime_primitives; +extern crate substrate_service as service; +#[macro_use] +extern crate slog; // needed until we can reexport `slog_info` from `substrate_telemetry` +#[macro_use] +extern crate substrate_telemetry; +extern crate exit_future; + +#[macro_use] +extern crate lazy_static; +extern crate clap; +#[macro_use] +extern crate error_chain; +#[macro_use] +extern crate log; + +pub mod error; +pub mod informant; +mod panic_hook; + +use network_libp2p::AddrComponent; +use runtime_primitives::traits::As; +use service::{ + ServiceFactory, FactoryFullConfiguration, RuntimeGenesis, + FactoryGenesis, PruningMode, ChainSpec, +}; +use network::NonReservedPeerMode; + +use std::io::{Write, Read, stdin, stdout}; +use std::iter; +use std::fs; +use std::fs::File; +use std::net::{Ipv4Addr, SocketAddr}; +use std::path::{Path, PathBuf}; +use names::{Generator, Name}; +use regex::Regex; + +use futures::Future; + +/// Executable version. Used to pass version information from the root crate. +pub struct VersionInfo { + /// Implementation version. + pub version: &'static str, + /// SCM Commit hash. + pub commit: &'static str, + /// Executable file name. + pub executable_name: &'static str, + /// Executable file description. + pub description: &'static str, + /// Executable file author. + pub author: &'static str, +} + +/// CLI Action +pub enum Action { + /// Substrate handled the command. No need to do anything. + ExecutedInternally, + /// Service mode requested. Caller should start the service. + RunService((FactoryFullConfiguration, E)), +} + +/// Something that can be converted into an exit signal. +pub trait IntoExit { + /// Exit signal type. + type Exit: Future + Send + 'static; + /// Convert into exit signal. + fn into_exit(self) -> Self::Exit; +} + +fn load_spec(matches: &clap::ArgMatches, factory: F) -> Result, String> + where G: RuntimeGenesis, F: FnOnce(&str) -> Result>, String>, +{ + let chain_key = matches.value_of("chain").unwrap_or_else(|| if matches.is_present("dev") { "dev" } else { "" }); + let spec = match factory(chain_key)? { + Some(spec) => spec, + None => ChainSpec::from_json_file(PathBuf::from(chain_key))? + }; + Ok(spec) +} + +fn base_path(matches: &clap::ArgMatches) -> PathBuf { + matches.value_of("base-path") + .map(|x| Path::new(x).to_owned()) + .unwrap_or_else(default_base_path) +} + +/// Check whether a node name is considered as valid +fn is_node_name_valid(_name: &str) -> Result<(), &str> { + const MAX_NODE_NAME_LENGTH: usize = 32; + let name = _name.to_string(); + if name.chars().count() >= MAX_NODE_NAME_LENGTH { + return Err("Node name too long"); + } + + let invalid_chars = r"[\\.@]"; + let re = Regex::new(invalid_chars).unwrap(); + if re.is_match(&name) { + return Err("Node name should not contain invalid chars such as '.' and '@'"); + } + + let invalid_patterns = r"(https?:\\/+)?(www)+"; + let re = Regex::new(invalid_patterns).unwrap(); + if re.is_match(&name) { + return Err("Node name should not contain urls"); + } + + Ok(()) +} + +/// Parse command line arguments and execute commands or return service configuration. +/// +/// IANA unassigned port ranges that we could use: +/// 6717-6766 Unassigned +/// 8504-8553 Unassigned +/// 9556-9591 Unassigned +/// 9803-9874 Unassigned +/// 9926-9949 Unassigned +pub fn prepare_execution( + args: I, + exit: E, + version: VersionInfo, + spec_factory: S, + impl_name: &'static str, +) -> error::Result> +where + I: IntoIterator, + T: Into + Clone, + E: IntoExit, + F: ServiceFactory, + S: FnOnce(&str) -> Result>>, String>, +{ + panic_hook::set(); + + let full_version = service::config::full_version_from_strs( + version.version, + version.commit + ); + let yaml = format!(include_str!("./cli.yml"), + name = version.executable_name, + description = version.description, + author = version.author, + ); + let yaml = &clap::YamlLoader::load_from_str(&yaml).expect("Invalid yml file")[0]; + let matches = match clap::App::from_yaml(yaml) + .version(&(full_version + "\n")[..]) + .get_matches_from_safe(args) { + Ok(m) => m, + Err(e) => e.exit(), + }; + + // TODO [ToDr] Split parameters parsing from actual execution. + let log_pattern = matches.value_of("log").unwrap_or(""); + init_logger(log_pattern); + fdlimit::raise_fd_limit(); + + if let Some(matches) = matches.subcommand_matches("build-spec") { + let spec = load_spec(&matches, spec_factory)?; + build_spec::(matches, spec)?; + return Ok(Action::ExecutedInternally); + } + + if let Some(matches) = matches.subcommand_matches("export-blocks") { + let spec = load_spec(&matches, spec_factory)?; + export_blocks::(matches, spec, exit.into_exit())?; + return Ok(Action::ExecutedInternally); + } + + if let Some(matches) = matches.subcommand_matches("import-blocks") { + let spec = load_spec(&matches, spec_factory)?; + import_blocks::(matches, spec, exit.into_exit())?; + return Ok(Action::ExecutedInternally); + } + + if let Some(matches) = matches.subcommand_matches("revert") { + let spec = load_spec(&matches, spec_factory)?; + revert_chain::(matches, spec)?; + return Ok(Action::ExecutedInternally); + } + + if let Some(matches) = matches.subcommand_matches("purge-chain") { + let spec = load_spec(&matches, spec_factory)?; + purge_chain::(matches, spec)?; + return Ok(Action::ExecutedInternally); + } + + let spec = load_spec(&matches, spec_factory)?; + let mut config = service::Configuration::default_with_spec(spec); + + config.impl_name = impl_name; + config.impl_commit = version.commit; + config.impl_version = version.version; + + config.name = match matches.value_of("name") { + None => Generator::with_naming(Name::Numbered).next().unwrap(), + Some(name) => name.into(), + }; + match is_node_name_valid(&config.name) { + Ok(_) => (), + Err(msg) => return Err(error::ErrorKind::Input( + format!("Invalid node name '{}'. Reason: {}. If unsure, use none.", config.name, msg)).into()) + } + + let base_path = base_path(&matches); + + config.keystore_path = matches.value_of("keystore") + .map(|x| Path::new(x).to_owned()) + .unwrap_or_else(|| keystore_path(&base_path, config.chain_spec.id())) + .to_string_lossy() + .into(); + + config.database_path = db_path(&base_path, config.chain_spec.id()).to_string_lossy().into(); + + config.pruning = match matches.value_of("pruning") { + Some("archive") => PruningMode::ArchiveAll, + None => PruningMode::default(), + Some(s) => PruningMode::keep_blocks(s.parse() + .map_err(|_| error::ErrorKind::Input("Invalid pruning mode specified".to_owned()))?), + }; + + let role = + if matches.is_present("light") { + config.execution_strategy = service::ExecutionStrategy::NativeWhenPossible; + service::Roles::LIGHT + } else if matches.is_present("validator") || matches.is_present("dev") { + config.execution_strategy = service::ExecutionStrategy::Both; + service::Roles::AUTHORITY + } else { + config.execution_strategy = service::ExecutionStrategy::NativeWhenPossible; + service::Roles::FULL + }; + + if let Some(s) = matches.value_of("execution") { + config.execution_strategy = match s { + "both" => service::ExecutionStrategy::Both, + "native" => service::ExecutionStrategy::NativeWhenPossible, + "wasm" => service::ExecutionStrategy::AlwaysWasm, + _ => return Err(error::ErrorKind::Input("Invalid execution mode specified".to_owned()).into()), + }; + } + + config.roles = role; + { + config.network.boot_nodes.extend(matches + .values_of("bootnodes") + .map_or(Default::default(), |v| v.map(|n| n.to_owned()).collect::>())); + config.network.config_path = Some(network_path(&base_path, config.chain_spec.id()).to_string_lossy().into()); + config.network.net_config_path = config.network.config_path.clone(); + config.network.reserved_nodes.extend(matches + .values_of("reserved-nodes") + .map_or(Default::default(), |v| v.map(|n| n.to_owned()).collect::>())); + if !config.network.reserved_nodes.is_empty() { + config.network.non_reserved_mode = NonReservedPeerMode::Deny; + } + + config.network.listen_addresses = Vec::new(); + for addr in matches.values_of("listen-addr").unwrap_or_default() { + let addr = addr.parse().map_err(|_| "Invalid listen multiaddress")?; + config.network.listen_addresses.push(addr); + } + if config.network.listen_addresses.is_empty() { + let port = match matches.value_of("port") { + Some(port) => port.parse().map_err(|_| "Invalid p2p port value specified.")?, + None => 30333, + }; + config.network.listen_addresses = vec![ + iter::once(AddrComponent::IP4(Ipv4Addr::new(0, 0, 0, 0))) + .chain(iter::once(AddrComponent::TCP(port))) + .collect() + ]; + } + + config.network.public_addresses = Vec::new(); + + config.network.client_version = config.client_id(); + config.network.use_secret = match matches.value_of("node-key").map(|s| s.parse()) { + Some(Ok(secret)) => Some(secret), + Some(Err(err)) => return Err(format!("Error parsing node key: {}", err).into()), + None => None, + }; + + let min_peers = match matches.value_of("min-peers") { + Some(min_peers) => min_peers.parse().map_err(|_| "Invalid min-peers value specified.")?, + None => 25, + }; + let max_peers = match matches.value_of("max-peers") { + Some(max_peers) => max_peers.parse().map_err(|_| "Invalid max-peers value specified.")?, + None => 50, + }; + if min_peers > max_peers { + return Err(error::ErrorKind::Input("Min-peers mustn't be larger than max-peers.".to_owned()).into()); + } + config.network.min_peers = min_peers; + config.network.max_peers = max_peers; + } + + config.keys = matches.values_of("key").unwrap_or_default().map(str::to_owned).collect(); + if matches.is_present("dev") { + config.keys.push("Alice".into()); + } + + let rpc_interface: &str = if matches.is_present("rpc-external") { "0.0.0.0" } else { "127.0.0.1" }; + let ws_interface: &str = if matches.is_present("ws-external") { "0.0.0.0" } else { "127.0.0.1" }; + + config.rpc_http = Some(parse_address(&format!("{}:{}", rpc_interface, 9933), "rpc-port", &matches)?); + config.rpc_ws = Some(parse_address(&format!("{}:{}", ws_interface, 9944), "ws-port", &matches)?); + + // Override telemetry + if matches.is_present("no-telemetry") { + config.telemetry_url = None; + } else if let Some(url) = matches.value_of("telemetry-url") { + config.telemetry_url = Some(url.to_owned()); + } + + Ok(Action::RunService((config, exit))) +} + +fn build_spec(matches: &clap::ArgMatches, spec: ChainSpec>) -> error::Result<()> + where F: ServiceFactory, +{ + info!("Building chain spec"); + let raw = matches.is_present("raw"); + let json = service::chain_ops::build_spec::>(spec, raw)?; + print!("{}", json); + Ok(()) +} + +fn export_blocks(matches: &clap::ArgMatches, spec: ChainSpec>, exit: E) -> error::Result<()> + where F: ServiceFactory, E: Future + Send + 'static, +{ + let base_path = base_path(matches); + let mut config = service::Configuration::default_with_spec(spec); + config.database_path = db_path(&base_path, config.chain_spec.id()).to_string_lossy().into(); + info!("DB path: {}", config.database_path); + let from: u64 = match matches.value_of("from") { + Some(v) => v.parse().map_err(|_| "Invalid --from argument")?, + None => 1, + }; + + let to: Option = match matches.value_of("to") { + Some(v) => Some(v.parse().map_err(|_| "Invalid --to argument")?), + None => None, + }; + let json = matches.is_present("json"); + + let file: Box = match matches.value_of("OUTPUT") { + Some(filename) => Box::new(File::create(filename)?), + None => Box::new(stdout()), + }; + + Ok(service::chain_ops::export_blocks::(config, exit, file, As::sa(from), to.map(As::sa), json)?) +} + +fn import_blocks(matches: &clap::ArgMatches, spec: ChainSpec>, exit: E) -> error::Result<()> + where F: ServiceFactory, E: Future + Send + 'static, +{ + let base_path = base_path(matches); + let mut config = service::Configuration::default_with_spec(spec); + config.database_path = db_path(&base_path, config.chain_spec.id()).to_string_lossy().into(); + + if let Some(s) = matches.value_of("execution") { + config.execution_strategy = match s { + "both" => service::ExecutionStrategy::Both, + "native" => service::ExecutionStrategy::NativeWhenPossible, + "wasm" => service::ExecutionStrategy::AlwaysWasm, + _ => return Err(error::ErrorKind::Input("Invalid execution mode specified".to_owned()).into()), + }; + } + + let file: Box = match matches.value_of("INPUT") { + Some(filename) => Box::new(File::open(filename)?), + None => Box::new(stdin()), + }; + + Ok(service::chain_ops::import_blocks::(config, exit, file)?) +} + +fn revert_chain(matches: &clap::ArgMatches, spec: ChainSpec>) -> error::Result<()> + where F: ServiceFactory, +{ + let base_path = base_path(matches); + let mut config = service::Configuration::default_with_spec(spec); + config.database_path = db_path(&base_path, config.chain_spec.id()).to_string_lossy().into(); + + let blocks = match matches.value_of("NUM") { + Some(v) => v.parse().map_err(|_| "Invalid block count specified")?, + None => 256, + }; + + Ok(service::chain_ops::revert_chain::(config, As::sa(blocks))?) +} + +fn purge_chain(matches: &clap::ArgMatches, spec: ChainSpec>) -> error::Result<()> + where F: ServiceFactory, +{ + let base_path = base_path(matches); + let database_path = db_path(&base_path, spec.id()); + + print!("Are you sure to remove {:?}? (y/n)", &database_path); + stdout().flush().expect("failed to flush stdout"); + + let mut input = String::new(); + stdin().read_line(&mut input)?; + let input = input.trim(); + + match input.chars().nth(0) { + Some('y') | Some('Y') => { + fs::remove_dir_all(&database_path)?; + println!("{:?} removed.", &database_path); + }, + _ => println!("Aborted"), + } + + Ok(()) +} + +fn parse_address(default: &str, port_param: &str, matches: &clap::ArgMatches) -> Result { + let mut address: SocketAddr = default.parse().ok().ok_or_else(|| format!("Invalid address specified for --{}.", port_param))?; + if let Some(port) = matches.value_of(port_param) { + let port: u16 = port.parse().ok().ok_or_else(|| format!("Invalid port for --{} specified.", port_param))?; + address.set_port(port); + } + + Ok(address) +} + +fn keystore_path(base_path: &Path, chain_id: &str) -> PathBuf { + let mut path = base_path.to_owned(); + path.push("chains"); + path.push(chain_id); + path.push("keystore"); + path +} + +fn db_path(base_path: &Path, chain_id: &str) -> PathBuf { + let mut path = base_path.to_owned(); + path.push("chains"); + path.push(chain_id); + path.push("db"); + path +} + +fn network_path(base_path: &Path, chain_id: &str) -> PathBuf { + let mut path = base_path.to_owned(); + path.push("chains"); + path.push(chain_id); + path.push("network"); + path +} + +fn default_base_path() -> PathBuf { + use app_dirs::{AppInfo, AppDataType}; + + let app_info = AppInfo { + name: "Polkadot", + author: "Parity Technologies", + }; + + app_dirs::get_app_root( + AppDataType::UserData, + &app_info, + ).expect("app directories exist on all supported platforms; qed") +} + +fn init_logger(pattern: &str) { + use ansi_term::Colour; + + let mut builder = env_logger::LogBuilder::new(); + // Disable info logging by default for some modules: + builder.filter(Some("ws"), log::LogLevelFilter::Warn); + builder.filter(Some("hyper"), log::LogLevelFilter::Warn); + // Enable info for others. + builder.filter(None, log::LogLevelFilter::Info); + + if let Ok(lvl) = std::env::var("RUST_LOG") { + builder.parse(&lvl); + } + + builder.parse(pattern); + let isatty = atty::is(atty::Stream::Stderr); + let enable_color = isatty; + + let format = move |record: &log::LogRecord| { + let timestamp = time::strftime("%Y-%m-%d %H:%M:%S", &time::now()).expect("Error formatting log timestamp"); + + let mut output = if log::max_log_level() <= log::LogLevelFilter::Info { + format!("{} {}", Colour::Black.bold().paint(timestamp), record.args()) + } else { + let name = ::std::thread::current().name().map_or_else(Default::default, |x| format!("{}", Colour::Blue.bold().paint(x))); + format!("{} {} {} {} {}", Colour::Black.bold().paint(timestamp), name, record.level(), record.target(), record.args()) + }; + + if !enable_color { + output = kill_color(output.as_ref()); + } + + if !isatty && record.level() <= log::LogLevel::Info && atty::is(atty::Stream::Stdout) { + // duplicate INFO/WARN output to console + println!("{}", output); + } + output + }; + builder.format(format); + + builder.init().expect("Logger initialized only once."); +} + +fn kill_color(s: &str) -> String { + lazy_static! { + static ref RE: Regex = Regex::new("\x1b\\[[^m]+m").expect("Error initializing color regex"); + } + RE.replace_all(s, "").to_string() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn tests_node_name_good() { + assert!(is_node_name_valid("short name").is_ok()); + } + + #[test] + fn tests_node_name_bad() { + assert!(is_node_name_valid("long names are not very cool for the ui").is_err()); + assert!(is_node_name_valid("Dots.not.Ok").is_err()); + assert!(is_node_name_valid("http://visit.me").is_err()); + assert!(is_node_name_valid("https://visit.me").is_err()); + assert!(is_node_name_valid("www.visit.me").is_err()); + assert!(is_node_name_valid("email@domain").is_err()); + } +} diff --git a/core/cli/src/panic_hook.rs b/core/cli/src/panic_hook.rs new file mode 100644 index 0000000000000..c7cea5c8537b0 --- /dev/null +++ b/core/cli/src/panic_hook.rs @@ -0,0 +1,69 @@ +// Copyright 2017 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 . + +//! Custom panic hook with bug report link + +use backtrace::Backtrace; +use std::io::{self, Write}; +use std::panic::{self, PanicInfo}; +use std::thread; + +/// Set the panic hook +pub fn set() { + panic::set_hook(Box::new(panic_hook)); +} + +static ABOUT_PANIC: &str = " +This is a bug. Please report it at: + + https://github.com/paritytech/polkadot/issues/new +"; + +fn panic_hook(info: &PanicInfo) { + let location = info.location(); + let file = location.as_ref().map(|l| l.file()).unwrap_or(""); + let line = location.as_ref().map(|l| l.line()).unwrap_or(0); + + let msg = match info.payload().downcast_ref::<&'static str>() { + Some(s) => *s, + None => match info.payload().downcast_ref::() { + Some(s) => &s[..], + None => "Box", + } + }; + + let thread = thread::current(); + let name = thread.name().unwrap_or(""); + + let backtrace = Backtrace::new(); + + let mut stderr = io::stderr(); + + let _ = writeln!(stderr, ""); + let _ = writeln!(stderr, "===================="); + let _ = writeln!(stderr, ""); + let _ = writeln!(stderr, "{:?}", backtrace); + let _ = writeln!(stderr, ""); + let _ = writeln!( + stderr, + "Thread '{}' panicked at '{}', {}:{}", + name, msg, file, line + ); + + let _ = writeln!(stderr, "{}", ABOUT_PANIC); + ::std::process::exit(1); +} + diff --git a/core/client/Cargo.toml b/core/client/Cargo.toml new file mode 100644 index 0000000000000..886410f74eebd --- /dev/null +++ b/core/client/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "substrate-client" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +error-chain = "0.12" +fnv = "1.0" +log = "0.3" +parking_lot = "0.4" +triehash = "0.2" +hex-literal = "0.1" +futures = "0.1.17" + +slog = "^2" +heapsize = "0.4" +substrate-bft = { path = "../bft" } +substrate-codec = { path = "../codec" } +substrate-executor = { path = "../executor" } +substrate-primitives = { path = "../primitives" } +substrate-runtime-io = { path = "../runtime-io" } +substrate-runtime-support = { path = "../../runtime/support" } +substrate-runtime-primitives = { path = "../../runtime/primitives" } +substrate-state-machine = { path = "../state-machine" } +substrate-keyring = { path = "../keyring" } +substrate-telemetry = { path = "../telemetry" } +hashdb = "0.2.1" +patricia-trie = "0.2.1" +rlp = "0.2.4" + +[dev-dependencies] +substrate-test-client = { path = "../test-client" } diff --git a/core/client/README.adoc b/core/client/README.adoc new file mode 100644 index 0000000000000..d644b1d039b11 --- /dev/null +++ b/core/client/README.adoc @@ -0,0 +1,13 @@ + += Client + +.Summary +[source, toml] +---- +include::Cargo.toml[lines=2..5] +---- + +.Description +---- +include::src/lib.rs[tag=description] +---- diff --git a/core/client/db/Cargo.toml b/core/client/db/Cargo.toml new file mode 100644 index 0000000000000..b0a6071d210fc --- /dev/null +++ b/core/client/db/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "substrate-client-db" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +parking_lot = "0.4" +log = "0.3" +kvdb = "0.1" +kvdb-rocksdb = "0.1.3" +hashdb = "0.2.1" +memorydb = "0.2.1" +substrate-primitives = { path = "../../primitives" } +substrate-client = { path = "../../client" } +substrate-state-machine = { path = "../../state-machine" } +substrate-codec = { path = "../../codec" } +substrate-codec-derive = { path = "../../codec/derive" } +substrate-executor = { path = "../../executor" } +substrate-state-db = { path = "../../state-db" } +substrate-runtime-support = { path = "../../../runtime/support" } +substrate-runtime-primitives = { path = "../../../runtime/primitives" } + +[dev-dependencies] +kvdb-memorydb = "0.1" diff --git a/core/client/db/src/cache.rs b/core/client/db/src/cache.rs new file mode 100644 index 0000000000000..194e307e069ae --- /dev/null +++ b/core/client/db/src/cache.rs @@ -0,0 +1,432 @@ +// Copyright 2017 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 . + +//! DB-backed cache of blockchain data. + +use std::sync::Arc; +use parking_lot::RwLock; + +use kvdb::{KeyValueDB, DBTransaction}; + +use client::blockchain::Cache as BlockchainCache; +use client::error::Result as ClientResult; +use codec::{Codec, Encode, Decode}; +use primitives::AuthorityId; +use runtime_primitives::generic::BlockId; +use runtime_primitives::traits::{Block as BlockT, As, NumberFor}; +use utils::{COLUMN_META, BlockKey, db_err, meta_keys, read_id, db_key_to_number, number_to_db_key}; + +/// Database-backed cache of blockchain data. +pub struct DbCache { + db: Arc, + block_index_column: Option, + authorities_at: DbCacheList>, +} + +impl DbCache + where + Block: BlockT, + NumberFor: As, +{ + /// Create new cache. + pub fn new( + db: Arc, + block_index_column: Option, + authorities_column: Option + ) -> ClientResult { + Ok(DbCache { + db: db.clone(), + block_index_column, + authorities_at: DbCacheList::new(db, meta_keys::BEST_AUTHORITIES, authorities_column)?, + }) + } + + /// Get authorities_cache. + pub fn authorities_at_cache(&self) -> &DbCacheList> { + &self.authorities_at + } +} + +impl BlockchainCache for DbCache + where + Block: BlockT, + NumberFor: As, +{ + fn authorities_at(&self, at: BlockId) -> Option> { + let authorities_at = read_id(&*self.db, self.block_index_column, at).and_then(|at| match at { + Some(at) => self.authorities_at.value_at_key(at), + None => Ok(None), + }); + + match authorities_at { + Ok(authorities) => authorities, + Err(error) => { + warn!("Trying to read authorities from db cache has failed with: {}", error); + None + }, + } + } +} + +/// Database-backed blockchain cache which holds its entries as a list. +/// The meta column holds the pointer to the best known cache entry and +/// every entry points to the previous entry. +/// New entry appears when the set of authorities changes in block, so the +/// best entry here means the entry that is valid for the best block (and +/// probably for its ascendants). +pub struct DbCacheList { + db: Arc, + meta_key: &'static [u8], + column: Option, + /// Best entry at the moment. None means that cache has no entries at all. + best_entry: RwLock, T>>>, +} + +/// Single cache entry. +#[derive(Clone)] +#[cfg_attr(test, derive(Debug, PartialEq))] +pub struct Entry { + /// first block, when this value became actual + valid_from: N, + /// None means that we do not know the value starting from `valid_from` block + value: Option, +} + +/// Internal representation of the single cache entry. The entry points to the +/// previous entry in the cache, allowing us to traverse back in time in list-style. +#[derive(Encode, Decode)] +#[cfg_attr(test, derive(Debug, PartialEq))] +struct StorageEntry { + /// None if valid from the beginning + prev_valid_from: Option, + /// None means that we do not know the value starting from `valid_from` block + value: Option, +} + +impl DbCacheList + where + Block: BlockT, + NumberFor: As, + T: Clone + PartialEq + Codec, +{ + /// Creates new cache list. + fn new(db: Arc, meta_key: &'static [u8], column: Option) -> ClientResult { + let best_entry = RwLock::new(db.get(COLUMN_META, meta_key) + .map_err(db_err) + .and_then(|block| match block { + Some(block) => { + let valid_from = db_key_to_number(&block)?; + read_storage_entry::(&*db, column, valid_from) + .map(|entry| Some(Entry { + valid_from, + value: entry + .expect("meta entry references the entry at the block; storage entry at block exists when referenced; qed") + .value, + })) + }, + None => Ok(None), + })?); + + Ok(DbCacheList { + db, + column, + meta_key, + best_entry, + }) + } + + /// Gets the best known entry. + pub fn best_entry(&self) -> Option, T>> { + self.best_entry.read().clone() + } + + /// Commits the new best pending value to the database. Returns Some if best entry must + /// be updated after transaction is committed. + pub fn commit_best_entry( + &self, + transaction: &mut DBTransaction, + valid_from: NumberFor, + pending_value: Option + ) -> Option, T>> { + let best_entry = self.best_entry(); + let update_best_entry = match ( + best_entry.as_ref().and_then(|a| a.value.as_ref()), + pending_value.as_ref() + ) { + (Some(best_value), Some(pending_value)) => best_value != pending_value, + (None, Some(_)) | (Some(_), None) => true, + (None, None) => false, + }; + if !update_best_entry { + return None; + } + + let valid_from_key = number_to_db_key(valid_from); + transaction.put(COLUMN_META, self.meta_key, &valid_from_key); + transaction.put(self.column, &valid_from_key, &StorageEntry { + prev_valid_from: best_entry.map(|b| b.valid_from), + value: pending_value.clone(), + }.encode()); + + Some(Entry { + valid_from, + value: pending_value, + }) + } + + /// Updates the best in-memory cache entry. Must be called after transaction with changes + /// from commit_best_entry has been committed. + pub fn update_best_entry(&self, best_entry: Option, T>>) { + *self.best_entry.write() = best_entry; + } + + /// Prune all entries from the beginning up to the block (including entry at the number). Returns + /// the number of pruned entries. Pruning never deletes the latest entry in the cache. + pub fn prune_entries( + &self, + transaction: &mut DBTransaction, + last_to_prune: NumberFor + ) -> ClientResult { + // find the last entry we want to keep + let mut last_entry_to_keep = match self.best_entry() { + Some(best_entry) => best_entry.valid_from, + None => return Ok(0), + }; + let mut first_entry_to_remove = last_entry_to_keep; + while first_entry_to_remove > last_to_prune { + last_entry_to_keep = first_entry_to_remove; + + let entry = read_storage_entry::(&*self.db, self.column, first_entry_to_remove)? + .expect("entry referenced from the next entry; entry exists when referenced; qed"); + // if we have reached the first list entry + // AND all list entries are for blocks that are later than last_to_prune + // => nothing to prune + first_entry_to_remove = match entry.prev_valid_from { + Some(prev_valid_from) => prev_valid_from, + None => return Ok(0), + } + } + + // remove all entries, starting from entry_to_remove + let mut pruned = 0; + let mut entry_to_remove = Some(first_entry_to_remove); + while let Some(current_entry) = entry_to_remove { + let entry = read_storage_entry::(&*self.db, self.column, current_entry)? + .expect("referenced entry exists; entry_to_remove is a reference to the entry; qed"); + + if current_entry != last_entry_to_keep { + transaction.delete(self.column, &number_to_db_key(current_entry)); + pruned += 1; + } + entry_to_remove = entry.prev_valid_from; + } + + let mut entry = read_storage_entry::(&*self.db, self.column, last_entry_to_keep)? + .expect("last_entry_to_keep >= first_entry_to_remove; that means that we're leaving this entry in the db; qed"); + entry.prev_valid_from = None; + transaction.put(self.column, &number_to_db_key(last_entry_to_keep), &entry.encode()); + + Ok(pruned) + } + + /// Reads the cached value, actual at given block. Returns None if the value was not cached + /// or if it has been pruned. + fn value_at_key(&self, key: BlockKey) -> ClientResult> { + let at = db_key_to_number::>(&key)?; + let best_valid_from = match self.best_entry() { + // there are entries in cache + Some(best_entry) => { + // we're looking for the best value + if at >= best_entry.valid_from { + return Ok(best_entry.value); + } + + // we're looking for the value of older blocks + best_entry.valid_from + }, + // there are no entries in the cache + None => return Ok(None), + }; + + let mut entry = read_storage_entry::(&*self.db, self.column, best_valid_from)? + .expect("self.best_entry().is_some() if there's entry for best_valid_from; qed"); + loop { + let prev_valid_from = match entry.prev_valid_from { + Some(prev_valid_from) => prev_valid_from, + None => return Ok(None), + }; + + let prev_entry = read_storage_entry::(&*self.db, self.column, prev_valid_from)? + .expect("entry referenced from the next entry; entry exists when referenced; qed"); + if at >= prev_valid_from { + return Ok(prev_entry.value); + } + + entry = prev_entry; + } + } +} + +/// Reads the entry at the block with given number. +fn read_storage_entry( + db: &KeyValueDB, + column: Option, + number: NumberFor +) -> ClientResult, T>>> + where + Block: BlockT, + NumberFor: As, + T: Codec, +{ + db.get(column, &number_to_db_key(number)) + .and_then(|entry| match entry { + Some(entry) => Ok(StorageEntry::, T>::decode(&mut &entry[..])), + None => Ok(None), + }) + .map_err(db_err) +} + +#[cfg(test)] +mod tests { + use runtime_primitives::testing::Block as RawBlock; + use light::{AUTHORITIES_ENTRIES_TO_KEEP, columns, LightStorage}; + use light::tests::insert_block; + use super::*; + + type Block = RawBlock; + + #[test] + fn authorities_storage_entry_serialized() { + let test_cases: Vec>> = vec![ + StorageEntry { prev_valid_from: Some(42), value: Some(vec![[1u8; 32].into()]) }, + StorageEntry { prev_valid_from: None, value: Some(vec![[1u8; 32].into(), [2u8; 32].into()]) }, + StorageEntry { prev_valid_from: None, value: None }, + ]; + + for expected in test_cases { + let serialized = expected.encode(); + let deserialized = StorageEntry::decode(&mut &serialized[..]).unwrap(); + assert_eq!(expected, deserialized); + } + } + + #[test] + fn best_authorities_are_updated() { + let db = LightStorage::new_test(); + let authorities_at: Vec<(usize, Option>>)> = vec![ + (0, None), + (0, None), + (1, Some(Entry { valid_from: 1, value: Some(vec![[2u8; 32].into()]) })), + (1, Some(Entry { valid_from: 1, value: Some(vec![[2u8; 32].into()]) })), + (2, Some(Entry { valid_from: 3, value: Some(vec![[4u8; 32].into()]) })), + (2, Some(Entry { valid_from: 3, value: Some(vec![[4u8; 32].into()]) })), + (3, Some(Entry { valid_from: 5, value: None })), + (3, Some(Entry { valid_from: 5, value: None })), + ]; + + // before any block, there are no entries in cache + assert!(db.cache().authorities_at_cache().best_entry().is_none()); + assert_eq!(db.db().iter(columns::AUTHORITIES).count(), 0); + + // insert blocks and check that best_authorities() returns correct result + let mut prev_hash = Default::default(); + for number in 0..authorities_at.len() { + let authorities_at_number = authorities_at[number].1.clone().and_then(|e| e.value); + prev_hash = insert_block(&db, &prev_hash, number as u64, authorities_at_number); + assert_eq!(db.cache().authorities_at_cache().best_entry(), authorities_at[number].1); + assert_eq!(db.db().iter(columns::AUTHORITIES).count(), authorities_at[number].0); + } + + // check that authorities_at() returns correct results for all retrospective blocks + for number in 1..authorities_at.len() + 1 { + assert_eq!(db.cache().authorities_at(BlockId::Number(number as u64)), + authorities_at.get(number + 1) + .or_else(|| authorities_at.last()) + .unwrap().1.clone().and_then(|e| e.value)); + } + + // now check that cache entries are pruned when new blocks are inserted + let mut current_entries_count = authorities_at.last().unwrap().0; + let pruning_starts_at = AUTHORITIES_ENTRIES_TO_KEEP as usize; + for number in authorities_at.len()..authorities_at.len() + pruning_starts_at { + prev_hash = insert_block(&db, &prev_hash, number as u64, None); + if number > pruning_starts_at { + let prev_entries_count = authorities_at[number - pruning_starts_at].0; + let entries_count = authorities_at.get(number - pruning_starts_at + 1).map(|e| e.0) + .unwrap_or_else(|| authorities_at.last().unwrap().0); + current_entries_count -= entries_count - prev_entries_count; + } + + // there's always at least 1 entry in the cache (after first insertion) + assert_eq!(db.db().iter(columns::AUTHORITIES).count(), ::std::cmp::max(current_entries_count, 1)); + } + } + + #[test] + fn best_authorities_are_pruned() { + let db = LightStorage::::new_test(); + let mut transaction = DBTransaction::new(); + + // insert first entry at block#100 + db.cache().authorities_at_cache().update_best_entry( + db.cache().authorities_at_cache().commit_best_entry(&mut transaction, 100, Some(vec![[1u8; 32].into()]))); + db.db().write(transaction).unwrap(); + + // no entries are pruned, since there's only one entry in the cache + let mut transaction = DBTransaction::new(); + assert_eq!(db.cache().authorities_at_cache().prune_entries(&mut transaction, 50).unwrap(), 0); + assert_eq!(db.cache().authorities_at_cache().prune_entries(&mut transaction, 100).unwrap(), 0); + assert_eq!(db.cache().authorities_at_cache().prune_entries(&mut transaction, 150).unwrap(), 0); + + // insert second entry at block#200 + let mut transaction = DBTransaction::new(); + db.cache().authorities_at_cache().update_best_entry( + db.cache().authorities_at_cache().commit_best_entry(&mut transaction, 200, Some(vec![[2u8; 32].into()]))); + db.db().write(transaction).unwrap(); + + let mut transaction = DBTransaction::new(); + assert_eq!(db.cache().authorities_at_cache().prune_entries(&mut transaction, 50).unwrap(), 0); + assert_eq!(db.cache().authorities_at_cache().prune_entries(&mut transaction, 100).unwrap(), 1); + assert_eq!(db.cache().authorities_at_cache().prune_entries(&mut transaction, 150).unwrap(), 1); + // still only 1 entry is removed since pruning never deletes the last entry + assert_eq!(db.cache().authorities_at_cache().prune_entries(&mut transaction, 200).unwrap(), 1); + assert_eq!(db.cache().authorities_at_cache().prune_entries(&mut transaction, 250).unwrap(), 1); + + // physically remove entry for block#100 from db + let mut transaction = DBTransaction::new(); + assert_eq!(db.cache().authorities_at_cache().prune_entries(&mut transaction, 150).unwrap(), 1); + db.db().write(transaction).unwrap(); + + assert_eq!(db.cache().authorities_at_cache().best_entry().unwrap().value, Some(vec![[2u8; 32].into()])); + assert_eq!(db.cache().authorities_at(BlockId::Number(50)), None); + assert_eq!(db.cache().authorities_at(BlockId::Number(100)), None); + assert_eq!(db.cache().authorities_at(BlockId::Number(150)), None); + assert_eq!(db.cache().authorities_at(BlockId::Number(200)), Some(vec![[2u8; 32].into()])); + assert_eq!(db.cache().authorities_at(BlockId::Number(250)), Some(vec![[2u8; 32].into()])); + + // try to delete last entry => failure (no entries are removed) + let mut transaction = DBTransaction::new(); + assert_eq!(db.cache().authorities_at_cache().prune_entries(&mut transaction, 300).unwrap(), 0); + db.db().write(transaction).unwrap(); + + assert_eq!(db.cache().authorities_at_cache().best_entry().unwrap().value, Some(vec![[2u8; 32].into()])); + assert_eq!(db.cache().authorities_at(BlockId::Number(50)), None); + assert_eq!(db.cache().authorities_at(BlockId::Number(100)), None); + assert_eq!(db.cache().authorities_at(BlockId::Number(150)), None); + assert_eq!(db.cache().authorities_at(BlockId::Number(200)), Some(vec![[2u8; 32].into()])); + assert_eq!(db.cache().authorities_at(BlockId::Number(250)), Some(vec![[2u8; 32].into()])); + } +} diff --git a/core/client/db/src/lib.rs b/core/client/db/src/lib.rs new file mode 100644 index 0000000000000..acbd935ce8c32 --- /dev/null +++ b/core/client/db/src/lib.rs @@ -0,0 +1,715 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +// tag::description[] +//! Client backend that uses RocksDB database as storage. +// end::description[] + +extern crate substrate_client as client; +extern crate kvdb_rocksdb; +extern crate kvdb; +extern crate hashdb; +extern crate memorydb; +extern crate parking_lot; +extern crate substrate_state_machine as state_machine; +extern crate substrate_primitives as primitives; +extern crate substrate_runtime_support as runtime_support; +extern crate substrate_runtime_primitives as runtime_primitives; +extern crate substrate_codec as codec; +extern crate substrate_executor as executor; +extern crate substrate_state_db as state_db; + +#[macro_use] +extern crate log; + +#[macro_use] +extern crate substrate_codec_derive; + +#[cfg(test)] +extern crate kvdb_memorydb; + +pub mod light; + +mod cache; +mod utils; + +use std::sync::Arc; +use std::path::PathBuf; +use std::io; + +use codec::{Decode, Encode}; +use hashdb::Hasher; +use kvdb::{KeyValueDB, DBTransaction}; +use memorydb::MemoryDB; +use parking_lot::RwLock; +use primitives::{H256, AuthorityId, Blake2Hasher, RlpCodec}; +use runtime_primitives::generic::BlockId; +use runtime_primitives::bft::Justification; +use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, As, Hash, HashFor, NumberFor, Zero}; +use runtime_primitives::BuildStorage; +use state_machine::backend::Backend as StateBackend; +use executor::RuntimeInfo; +use state_machine::{CodeExecutor, DBValue, ExecutionStrategy}; +use utils::{Meta, db_err, meta_keys, number_to_db_key, db_key_to_number, open_database, + read_db, read_id, read_meta}; +use state_db::StateDb; +pub use state_db::PruningMode; + +const FINALIZATION_WINDOW: u64 = 32; + +/// DB-backed patricia trie state, transaction type is an overlay of changes to commit. +pub type DbState = state_machine::TrieBackend; + +/// Database settings. +pub struct DatabaseSettings { + /// Cache size in bytes. If `None` default is used. + pub cache_size: Option, + /// Path to the database. + pub path: PathBuf, + /// Pruning mode. + pub pruning: PruningMode, +} + +/// Create an instance of db-backed client. +pub fn new_client( + settings: DatabaseSettings, + executor: E, + genesis_storage: S, + execution_strategy: ExecutionStrategy, +) -> Result, client::LocalCallExecutor, E>, Block>, client::error::Error> + where + Block: BlockT, + E: CodeExecutor + RuntimeInfo, + S: BuildStorage, +{ + let backend = Arc::new(Backend::new(settings, FINALIZATION_WINDOW)?); + let executor = client::LocalCallExecutor::new(backend.clone(), executor); + Ok(client::Client::new(backend, executor, genesis_storage, execution_strategy)?) +} + +mod columns { + pub const META: Option = Some(0); + pub const STATE: Option = Some(1); + pub const STATE_META: Option = Some(2); + pub const BLOCK_INDEX: Option = Some(3); + pub const HEADER: Option = Some(4); + pub const BODY: Option = Some(5); + pub const JUSTIFICATION: Option = Some(6); +} + +struct PendingBlock { + header: Block::Header, + justification: Option>, + body: Option>, + is_best: bool, +} + +// wrapper that implements trait required for state_db +struct StateMetaDb<'a>(&'a KeyValueDB); + +impl<'a> state_db::MetaDb for StateMetaDb<'a> { + type Error = io::Error; + + fn get_meta(&self, key: &[u8]) -> Result>, Self::Error> { + self.0.get(columns::STATE_META, key).map(|r| r.map(|v| v.to_vec())) + } +} + +/// Block database +pub struct BlockchainDb { + db: Arc, + meta: RwLock::Number, Block::Hash>>, +} + +impl BlockchainDb { + fn new(db: Arc) -> Result { + let meta = read_meta::(&*db, columns::HEADER)?; + Ok(BlockchainDb { + db, + meta: RwLock::new(meta) + }) + } + + fn update_meta(&self, hash: Block::Hash, number: ::Number, is_best: bool) { + if is_best { + let mut meta = self.meta.write(); + if number == Zero::zero() { + meta.genesis_hash = hash; + } + meta.best_number = number; + meta.best_hash = hash; + } + } +} + +impl client::blockchain::HeaderBackend for BlockchainDb { + fn header(&self, id: BlockId) -> Result, client::error::Error> { + match read_db(&*self.db, columns::BLOCK_INDEX, columns::HEADER, id)? { + Some(header) => match Block::Header::decode(&mut &header[..]) { + Some(header) => Ok(Some(header)), + None => return Err(client::error::ErrorKind::Backend("Error decoding header".into()).into()), + } + None => Ok(None), + } + } + + fn info(&self) -> Result, client::error::Error> { + let meta = self.meta.read(); + Ok(client::blockchain::Info { + best_hash: meta.best_hash, + best_number: meta.best_number, + genesis_hash: meta.genesis_hash, + }) + } + + fn status(&self, id: BlockId) -> Result { + let exists = match id { + BlockId::Hash(_) => read_id(&*self.db, columns::BLOCK_INDEX, id)?.is_some(), + BlockId::Number(n) => n <= self.meta.read().best_number, + }; + match exists { + true => Ok(client::blockchain::BlockStatus::InChain), + false => Ok(client::blockchain::BlockStatus::Unknown), + } + } + + fn number(&self, hash: Block::Hash) -> Result::Number>, client::error::Error> { + read_id::(&*self.db, columns::BLOCK_INDEX, BlockId::Hash(hash)) + .and_then(|key| match key { + Some(key) => Ok(Some(db_key_to_number(&key)?)), + None => Ok(None), + }) + } + + fn hash(&self, number: ::Number) -> Result, client::error::Error> { + read_db::(&*self.db, columns::BLOCK_INDEX, columns::HEADER, BlockId::Number(number)).map(|x| + x.map(|raw| HashFor::::hash(&raw[..])).map(Into::into) + ) + } +} + +impl client::blockchain::Backend for BlockchainDb { + fn body(&self, id: BlockId) -> Result>, client::error::Error> { + match read_db(&*self.db, columns::BLOCK_INDEX, columns::BODY, id)? { + Some(body) => match Decode::decode(&mut &body[..]) { + Some(body) => Ok(Some(body)), + None => return Err(client::error::ErrorKind::Backend("Error decoding body".into()).into()), + } + None => Ok(None), + } + } + + fn justification(&self, id: BlockId) -> Result>, client::error::Error> { + match read_db(&*self.db, columns::BLOCK_INDEX, columns::JUSTIFICATION, id)? { + Some(justification) => match Decode::decode(&mut &justification[..]) { + Some(justification) => Ok(Some(justification)), + None => return Err(client::error::ErrorKind::Backend("Error decoding justification".into()).into()), + } + None => Ok(None), + } + } + + fn cache(&self) -> Option<&client::blockchain::Cache> { + None + } +} + +/// Database transaction +pub struct BlockImportOperation { + old_state: DbState, + updates: MemoryDB, + pending_block: Option>, +} + +impl client::backend::BlockImportOperation +for BlockImportOperation +where Block: BlockT, +{ + type State = DbState; + + fn state(&self) -> Result, client::error::Error> { + Ok(Some(&self.old_state)) + } + + fn set_block_data(&mut self, header: Block::Header, body: Option>, justification: Option>, is_best: bool) -> Result<(), client::error::Error> { + assert!(self.pending_block.is_none(), "Only one block per operation is allowed"); + self.pending_block = Some(PendingBlock { + header, + body, + justification, + is_best, + }); + Ok(()) + } + + fn update_authorities(&mut self, _authorities: Vec) { + // currently authorities are not cached on full nodes + } + + fn update_storage(&mut self, update: MemoryDB) -> Result<(), client::error::Error> { + self.updates = update; + Ok(()) + } + + fn reset_storage, Vec)>>(&mut self, iter: I) -> Result<(), client::error::Error> { + // TODO: wipe out existing trie. + let (_, update) = self.old_state.storage_root(iter.into_iter().map(|(k, v)| (k, Some(v)))); + self.updates = update; + Ok(()) + } +} + +struct StorageDb { + pub db: Arc, + pub state_db: StateDb, +} + +impl state_machine::Storage for StorageDb { + fn get(&self, key: &H256) -> Result, String> { + self.state_db.get(&key.0.into(), self).map(|r| r.map(|v| DBValue::from_slice(&v))) + .map_err(|e| format!("Database backend error: {:?}", e)) + } +} + +impl state_db::HashDb for StorageDb { + type Error = io::Error; + type Hash = H256; + + fn get(&self, key: &H256) -> Result>, Self::Error> { + self.db.get(columns::STATE, &key[..]).map(|r| r.map(|v| v.to_vec())) + } +} + + +/// Disk backend. Keeps data in a key-value store. In archive mode, trie nodes are kept from all blocks. +/// Otherwise, trie nodes are kept only from the most recent block. +pub struct Backend { + storage: Arc>, + blockchain: BlockchainDb, + finalization_window: u64, +} + +impl Backend { + /// Create a new instance of database backend. + pub fn new(config: DatabaseSettings, finalization_window: u64) -> Result { + let db = open_database(&config, "full")?; + + Backend::from_kvdb(db as Arc<_>, config.pruning, finalization_window) + } + + #[cfg(test)] + fn new_test(keep_blocks: u32) -> Self { + use utils::NUM_COLUMNS; + + let db = Arc::new(::kvdb_memorydb::create(NUM_COLUMNS)); + + Backend::from_kvdb(db as Arc<_>, PruningMode::keep_blocks(keep_blocks), 0).expect("failed to create test-db") + } + + fn from_kvdb(db: Arc, pruning: PruningMode, finalization_window: u64) -> Result { + let blockchain = BlockchainDb::new(db.clone())?; + let map_e = |e: state_db::Error| ::client::error::Error::from(format!("State database error: {:?}", e)); + let state_db: StateDb = StateDb::new(pruning, &StateMetaDb(&*db)).map_err(map_e)?; + let storage_db = StorageDb { + db, + state_db, + }; + + Ok(Backend { + storage: Arc::new(storage_db), + blockchain, + finalization_window, + }) + } +} + +fn apply_state_commit(transaction: &mut DBTransaction, commit: state_db::CommitSet) { + for (key, val) in commit.data.inserted.into_iter() { + transaction.put(columns::STATE, &key[..], &val); + } + for key in commit.data.deleted.into_iter() { + transaction.delete(columns::STATE, &key[..]); + } + for (key, val) in commit.meta.inserted.into_iter() { + transaction.put(columns::STATE_META, &key[..], &val); + } + for key in commit.meta.deleted.into_iter() { + transaction.delete(columns::STATE_META, &key[..]); + } +} + +impl client::backend::Backend for Backend where Block: BlockT { + type BlockImportOperation = BlockImportOperation; + type Blockchain = BlockchainDb; + type State = DbState; + + fn begin_operation(&self, block: BlockId) -> Result { + let state = self.state_at(block)?; + Ok(BlockImportOperation { + pending_block: None, + old_state: state, + updates: MemoryDB::default(), + }) + } + + fn commit_operation(&self, mut operation: Self::BlockImportOperation) -> Result<(), client::error::Error> { + use client::blockchain::HeaderBackend; + let mut transaction = DBTransaction::new(); + if let Some(pending_block) = operation.pending_block { + let hash = pending_block.header.hash(); + let number = pending_block.header.number().clone(); + let key = number_to_db_key(number.clone()); + transaction.put(columns::HEADER, &key, &pending_block.header.encode()); + if let Some(body) = pending_block.body { + transaction.put(columns::BODY, &key, &body.encode()); + } + if let Some(justification) = pending_block.justification { + transaction.put(columns::JUSTIFICATION, &key, &justification.encode()); + } + transaction.put(columns::BLOCK_INDEX, hash.as_ref(), &key); + if pending_block.is_best { + transaction.put(columns::META, meta_keys::BEST_BLOCK, &key); + } + let mut changeset: state_db::ChangeSet = state_db::ChangeSet::default(); + for (key, (val, rc)) in operation.updates.drain() { + if rc > 0 { + changeset.inserted.push((key.0.into(), val.to_vec())); + } else if rc < 0 { + changeset.deleted.push(key.0.into()); + } + } + let number_u64 = number.as_().into(); + let commit = self.storage.state_db.insert_block(&hash, number_u64, &pending_block.header.parent_hash(), changeset); + apply_state_commit(&mut transaction, commit); + + //finalize an older block + if number_u64 > self.finalization_window { + let finalizing_hash = if self.finalization_window == 0 { + Some(hash) + } else { + let finalizing = number_u64 - self.finalization_window; + if finalizing > self.storage.state_db.best_finalized() { + self.blockchain.hash(As::sa(finalizing))? + } else { + None + } + }; + if let Some(finalizing_hash) = finalizing_hash { + trace!(target: "db", "Finalizing block #{} ({:?})", number_u64 - self.finalization_window, finalizing_hash); + let commit = self.storage.state_db.finalize_block(&finalizing_hash); + apply_state_commit(&mut transaction, commit); + } + } + + debug!(target: "db", "DB Commit {:?} ({}), best = {}", hash, number, pending_block.is_best); + self.storage.db.write(transaction).map_err(db_err)?; + self.blockchain.update_meta(hash, number, pending_block.is_best); + } + Ok(()) + } + + fn revert(&self, n: NumberFor) -> Result, client::error::Error> { + use client::blockchain::HeaderBackend; + let mut best = self.blockchain.info()?.best_number; + for c in 0 .. n.as_() { + if best == As::sa(0) { + return Ok(As::sa(c)) + } + let mut transaction = DBTransaction::new(); + match self.storage.state_db.revert_one() { + Some(commit) => { + apply_state_commit(&mut transaction, commit); + let removed = self.blockchain.hash(best)?.ok_or_else( + || client::error::ErrorKind::UnknownBlock( + format!("Error reverting to {}. Block hash not found.", best)))?; + best -= As::sa(1); + let key = number_to_db_key(best.clone()); + let hash = self.blockchain.hash(best)?.ok_or_else( + || client::error::ErrorKind::UnknownBlock( + format!("Error reverting to {}. Block hash not found.", best)))?; + transaction.put(columns::META, meta_keys::BEST_BLOCK, &key); + transaction.delete(columns::BLOCK_INDEX, removed.as_ref()); + self.storage.db.write(transaction).map_err(db_err)?; + self.blockchain.update_meta(hash, best, true); + } + None => return Ok(As::sa(c)) + } + } + Ok(n) + } + + fn blockchain(&self) -> &BlockchainDb { + &self.blockchain + } + + fn state_at(&self, block: BlockId) -> Result { + use client::blockchain::HeaderBackend as BcHeaderBackend; + + // special case for genesis initialization + match block { + BlockId::Hash(h) if h == Default::default() => + return Ok(DbState::with_storage_for_genesis(self.storage.clone())), + _ => {} + } + + match self.blockchain.header(block) { + Ok(Some(ref hdr)) if !self.storage.state_db.is_pruned(hdr.number().as_()) => { + let root = H256::from_slice(hdr.state_root().as_ref()); + Ok(DbState::with_storage(self.storage.clone(), root)) + }, + Err(e) => Err(e), + _ => Err(client::error::ErrorKind::UnknownBlock(format!("{:?}", block)).into()), + } + } +} + +impl client::backend::LocalBackend for Backend +where Block: BlockT {} + +#[cfg(test)] +mod tests { + use hashdb::HashDB; + use super::*; + use client::backend::Backend as BTrait; + use client::backend::BlockImportOperation as Op; + use client::blockchain::HeaderBackend as BlockchainHeaderBackend; + use runtime_primitives::testing::{Header, Block as RawBlock}; + + type Block = RawBlock; + + #[test] + fn block_hash_inserted_correctly() { + let db = Backend::::new_test(1); + for i in 0..10 { + assert!(db.blockchain().hash(i).unwrap().is_none()); + + { + let id = if i == 0 { + BlockId::Hash(Default::default()) + } else { + BlockId::Number(i - 1) + }; + + let mut op = db.begin_operation(id).unwrap(); + let header = Header { + number: i, + parent_hash: if i == 0 { + Default::default() + } else { + db.blockchain.hash(i - 1).unwrap().unwrap() + }, + state_root: Default::default(), + digest: Default::default(), + extrinsics_root: Default::default(), + }; + + op.set_block_data( + header, + Some(vec![]), + None, + true, + ).unwrap(); + db.commit_operation(op).unwrap(); + } + + assert!(db.blockchain().hash(i).unwrap().is_some()) + } + } + + #[test] + fn set_state_data() { + let db = Backend::::new_test(2); + { + let mut op = db.begin_operation(BlockId::Hash(Default::default())).unwrap(); + let mut header = Header { + number: 0, + parent_hash: Default::default(), + state_root: Default::default(), + digest: Default::default(), + extrinsics_root: Default::default(), + }; + + let storage = vec![ + (vec![1, 3, 5], vec![2, 4, 6]), + (vec![1, 2, 3], vec![9, 9, 9]), + ]; + + header.state_root = op.old_state.storage_root(storage + .iter() + .cloned() + .map(|(x, y)| (x, Some(y))) + ).0.into(); + + op.reset_storage(storage.iter().cloned()).unwrap(); + op.set_block_data( + header, + Some(vec![]), + None, + true + ).unwrap(); + + db.commit_operation(op).unwrap(); + + let state = db.state_at(BlockId::Number(0)).unwrap(); + + assert_eq!(state.storage(&[1, 3, 5]).unwrap(), Some(vec![2, 4, 6])); + assert_eq!(state.storage(&[1, 2, 3]).unwrap(), Some(vec![9, 9, 9])); + assert_eq!(state.storage(&[5, 5, 5]).unwrap(), None); + + } + + { + let mut op = db.begin_operation(BlockId::Number(0)).unwrap(); + let mut header = Header { + number: 1, + parent_hash: Default::default(), + state_root: Default::default(), + digest: Default::default(), + extrinsics_root: Default::default(), + }; + + let storage = vec![ + (vec![1, 3, 5], None), + (vec![5, 5, 5], Some(vec![4, 5, 6])), + ]; + + let (root, overlay) = op.old_state.storage_root(storage.iter().cloned()); + op.update_storage(overlay).unwrap(); + header.state_root = root.into(); + + op.set_block_data( + header, + Some(vec![]), + None, + true + ).unwrap(); + + db.commit_operation(op).unwrap(); + + let state = db.state_at(BlockId::Number(1)).unwrap(); + + assert_eq!(state.storage(&[1, 3, 5]).unwrap(), None); + assert_eq!(state.storage(&[1, 2, 3]).unwrap(), Some(vec![9, 9, 9])); + assert_eq!(state.storage(&[5, 5, 5]).unwrap(), Some(vec![4, 5, 6])); + } + } + + #[test] + fn delete_only_when_negative_rc() { + let key; + let backend = Backend::::new_test(0); + + let hash = { + let mut op = backend.begin_operation(BlockId::Hash(Default::default())).unwrap(); + let mut header = Header { + number: 0, + parent_hash: Default::default(), + state_root: Default::default(), + digest: Default::default(), + extrinsics_root: Default::default(), + }; + + let storage: Vec<(_, _)> = vec![]; + + header.state_root = op.old_state.storage_root(storage + .iter() + .cloned() + .map(|(x, y)| (x, Some(y))) + ).0.into(); + let hash = header.hash(); + + op.reset_storage(storage.iter().cloned()).unwrap(); + + key = op.updates.insert(b"hello"); + op.set_block_data( + header, + Some(vec![]), + None, + true + ).unwrap(); + + backend.commit_operation(op).unwrap(); + + assert_eq!(backend.storage.db.get(::columns::STATE, &key.0[..]).unwrap().unwrap(), &b"hello"[..]); + hash + }; + + let hash = { + let mut op = backend.begin_operation(BlockId::Number(0)).unwrap(); + let mut header = Header { + number: 1, + parent_hash: hash, + state_root: Default::default(), + digest: Default::default(), + extrinsics_root: Default::default(), + }; + + let storage: Vec<(_, _)> = vec![]; + + header.state_root = op.old_state.storage_root(storage + .iter() + .cloned() + .map(|(x, y)| (x, Some(y))) + ).0.into(); + let hash = header.hash(); + + op.updates.insert(b"hello"); + op.updates.remove(&key); + op.set_block_data( + header, + Some(vec![]), + None, + true + ).unwrap(); + + backend.commit_operation(op).unwrap(); + + assert_eq!(backend.storage.db.get(::columns::STATE, &key.0[..]).unwrap().unwrap(), &b"hello"[..]); + hash + }; + + { + let mut op = backend.begin_operation(BlockId::Number(1)).unwrap(); + let mut header = Header { + number: 2, + parent_hash: hash, + state_root: Default::default(), + digest: Default::default(), + extrinsics_root: Default::default(), + }; + + let storage: Vec<(_, _)> = vec![]; + + header.state_root = op.old_state.storage_root(storage + .iter() + .cloned() + .map(|(x, y)| (x, Some(y))) + ).0.into(); + + op.updates.remove(&key); + op.set_block_data( + header, + Some(vec![]), + None, + true + ).unwrap(); + + backend.commit_operation(op).unwrap(); + + assert!(backend.storage.db.get(::columns::STATE, &key.0[..]).unwrap().is_none()); + } + } +} diff --git a/core/client/db/src/light.rs b/core/client/db/src/light.rs new file mode 100644 index 0000000000000..9b82aec1f3087 --- /dev/null +++ b/core/client/db/src/light.rs @@ -0,0 +1,392 @@ +// Copyright 2017 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 light client blockchain storage. + +use std::sync::Arc; +use parking_lot::RwLock; + +use kvdb::{KeyValueDB, DBTransaction}; + +use client::blockchain::{BlockStatus, Cache as BlockchainCache, + HeaderBackend as BlockchainHeaderBackend, Info as BlockchainInfo}; +use client::cht; +use client::error::{ErrorKind as ClientErrorKind, Result as ClientResult}; +use client::light::blockchain::Storage as LightBlockchainStorage; +use codec::{Decode, Encode}; +use primitives::{AuthorityId, H256, Blake2Hasher}; +use runtime_primitives::generic::BlockId; +use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, Hash, HashFor, + Zero, One, As, NumberFor}; +use cache::DbCache; +use utils::{meta_keys, Meta, db_err, number_to_db_key, db_key_to_number, open_database, + read_db, read_id, read_meta}; +use DatabaseSettings; + +pub(crate) mod columns { + pub const META: Option = ::utils::COLUMN_META; + pub const BLOCK_INDEX: Option = Some(1); + pub const HEADER: Option = Some(2); + pub const AUTHORITIES: Option = Some(3); + pub const CHT: Option = Some(4); +} + +/// Keep authorities for last 'AUTHORITIES_ENTRIES_TO_KEEP' blocks. +pub(crate) const AUTHORITIES_ENTRIES_TO_KEEP: u64 = cht::SIZE; + +/// Light blockchain storage. Stores most recent headers + CHTs for older headers. +pub struct LightStorage { + db: Arc, + meta: RwLock::Header as HeaderT>::Number, Block::Hash>>, + cache: DbCache, +} + +#[derive(Clone, PartialEq, Debug)] +struct BestAuthorities { + /// first block, when this set became actual + valid_from: N, + /// None means that we do not know the set starting from `valid_from` block + authorities: Option>, +} + +impl LightStorage + where + Block: BlockT, +{ + /// Create new storage with given settings. + pub fn new(config: DatabaseSettings) -> ClientResult { + let db = open_database(&config, "light")?; + + Self::from_kvdb(db as Arc<_>) + } + + #[cfg(test)] + pub(crate) fn new_test() -> Self { + use utils::NUM_COLUMNS; + + let db = Arc::new(::kvdb_memorydb::create(NUM_COLUMNS)); + + Self::from_kvdb(db as Arc<_>).expect("failed to create test-db") + } + + fn from_kvdb(db: Arc) -> ClientResult { + let cache = DbCache::new(db.clone(), columns::BLOCK_INDEX, columns::AUTHORITIES)?; + let meta = RwLock::new(read_meta::(&*db, columns::HEADER)?); + + Ok(LightStorage { + db, + meta, + cache, + }) + } + + #[cfg(test)] + pub(crate) fn db(&self) -> &Arc { + &self.db + } + + #[cfg(test)] + pub(crate) fn cache(&self) -> &DbCache { + &self.cache + } + + fn update_meta(&self, hash: Block::Hash, number: <::Header as HeaderT>::Number, is_best: bool) { + if is_best { + let mut meta = self.meta.write(); + if number == <::Header as HeaderT>::Number::zero() { + meta.genesis_hash = hash; + } + + meta.best_number = number; + meta.best_hash = hash; + } + } +} + +impl BlockchainHeaderBackend for LightStorage + where + Block: BlockT, +{ + fn header(&self, id: BlockId) -> ClientResult> { + match read_db(&*self.db, columns::BLOCK_INDEX, columns::HEADER, id)? { + Some(header) => match Block::Header::decode(&mut &header[..]) { + Some(header) => Ok(Some(header)), + None => return Err(ClientErrorKind::Backend("Error decoding header".into()).into()), + } + None => Ok(None), + } + } + + fn info(&self) -> ClientResult> { + let meta = self.meta.read(); + Ok(BlockchainInfo { + best_hash: meta.best_hash, + best_number: meta.best_number, + genesis_hash: meta.genesis_hash, + }) + } + + fn status(&self, id: BlockId) -> ClientResult { + let exists = match id { + BlockId::Hash(_) => read_id(&*self.db, columns::BLOCK_INDEX, id)?.is_some(), + BlockId::Number(n) => n <= self.meta.read().best_number, + }; + match exists { + true => Ok(BlockStatus::InChain), + false => Ok(BlockStatus::Unknown), + } + } + + fn number(&self, hash: Block::Hash) -> ClientResult::Header as HeaderT>::Number>> { + read_id::(&*self.db, columns::BLOCK_INDEX, BlockId::Hash(hash)) + .and_then(|key| match key { + Some(key) => Ok(Some(db_key_to_number(&key)?)), + None => Ok(None), + }) + } + + fn hash(&self, number: <::Header as HeaderT>::Number) -> ClientResult> { + read_db::(&*self.db, columns::BLOCK_INDEX, columns::HEADER, BlockId::Number(number)).map(|x| + x.map(|raw| HashFor::::hash(&raw[..])).map(Into::into) + ) + } +} + +impl LightBlockchainStorage for LightStorage + where + Block: BlockT, + Block::Hash: From, +{ + fn import_header(&self, is_new_best: bool, header: Block::Header, authorities: Option>) -> ClientResult<()> { + let mut transaction = DBTransaction::new(); + + let hash = header.hash(); + let number = *header.number(); + let key = number_to_db_key(number); + + transaction.put(columns::HEADER, &key, &header.encode()); + transaction.put(columns::BLOCK_INDEX, hash.as_ref(), &key); + + let best_authorities = if is_new_best { + transaction.put(columns::META, meta_keys::BEST_BLOCK, &key); + + // cache authorities for previous block + let number: u64 = number.as_(); + let previous_number = number.checked_sub(1); + let best_authorities = previous_number + .and_then(|previous_number| self.cache.authorities_at_cache() + .commit_best_entry(&mut transaction, As::sa(previous_number), authorities)); + + // prune authorities from 'ancient' blocks + if let Some(ancient_number) = number.checked_sub(AUTHORITIES_ENTRIES_TO_KEEP) { + self.cache.authorities_at_cache().prune_entries(&mut transaction, As::sa(ancient_number))?; + } + + best_authorities + } else { + None + }; + + // build new CHT if required + if let Some(new_cht_number) = cht::is_build_required(cht::SIZE, *header.number()) { + let new_cht_start: NumberFor = cht::start_number(cht::SIZE, new_cht_number); + let new_cht_root: Option = cht::compute_root::( + cht::SIZE, new_cht_number, (new_cht_start.as_()..) + .map(|num| self.hash(As::sa(num)).unwrap_or_default())); + + if let Some(new_cht_root) = new_cht_root { + transaction.put(columns::CHT, &number_to_db_key(new_cht_start), new_cht_root.as_ref()); + + let mut prune_block = new_cht_start; + let new_cht_end = cht::end_number(cht::SIZE, new_cht_number); + trace!(target: "db", "Replacing blocks [{}..{}] with CHT#{}", new_cht_start, new_cht_end, new_cht_number); + + while prune_block <= new_cht_end { + transaction.delete(columns::HEADER, &number_to_db_key(prune_block)); + prune_block += <::Header as HeaderT>::Number::one(); + } + } + } + + debug!("Light DB Commit {:?} ({})", hash, number); + self.db.write(transaction).map_err(db_err)?; + self.update_meta(hash, number, is_new_best); + if let Some(best_authorities) = best_authorities { + self.cache.authorities_at_cache().update_best_entry(Some(best_authorities)); + } + + Ok(()) + } + + fn cht_root(&self, cht_size: u64, block: <::Header as HeaderT>::Number) -> ClientResult { + let no_cht_for_block = || ClientErrorKind::Backend(format!("CHT for block {} not exists", block)).into(); + + 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, &number_to_db_key(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)) + } + + fn cache(&self) -> Option<&BlockchainCache> { + Some(&self.cache) + } +} + +#[cfg(test)] +pub(crate) mod tests { + use client::cht; + use runtime_primitives::testing::{H256 as Hash, Header, Block as RawBlock}; + use super::*; + + type Block = RawBlock; + + pub fn insert_block( + db: &LightStorage, + parent: &Hash, + number: u64, + authorities: Option> + ) -> Hash { + let header = Header { + number: number.into(), + parent_hash: *parent, + state_root: Default::default(), + digest: Default::default(), + extrinsics_root: Default::default(), + }; + + let hash = header.hash(); + db.import_header(true, header, authorities).unwrap(); + hash + } + + #[test] + fn returns_known_header() { + let db = LightStorage::new_test(); + let known_hash = insert_block(&db, &Default::default(), 0, None); + let header_by_hash = db.header(BlockId::Hash(known_hash)).unwrap().unwrap(); + let header_by_number = db.header(BlockId::Number(0)).unwrap().unwrap(); + assert_eq!(header_by_hash, header_by_number); + } + + #[test] + fn does_not_return_unknown_header() { + let db = LightStorage::::new_test(); + assert!(db.header(BlockId::Hash(1.into())).unwrap().is_none()); + assert!(db.header(BlockId::Number(0)).unwrap().is_none()); + } + + #[test] + fn returns_info() { + let db = LightStorage::new_test(); + let genesis_hash = insert_block(&db, &Default::default(), 0, None); + let info = db.info().unwrap(); + assert_eq!(info.best_hash, genesis_hash); + assert_eq!(info.best_number, 0); + assert_eq!(info.genesis_hash, genesis_hash); + let best_hash = insert_block(&db, &genesis_hash, 1, None); + let info = db.info().unwrap(); + assert_eq!(info.best_hash, best_hash); + assert_eq!(info.best_number, 1); + assert_eq!(info.genesis_hash, genesis_hash); + } + + #[test] + fn returns_block_status() { + let db = LightStorage::new_test(); + let genesis_hash = insert_block(&db, &Default::default(), 0, None); + assert_eq!(db.status(BlockId::Hash(genesis_hash)).unwrap(), BlockStatus::InChain); + assert_eq!(db.status(BlockId::Number(0)).unwrap(), BlockStatus::InChain); + assert_eq!(db.status(BlockId::Hash(1.into())).unwrap(), BlockStatus::Unknown); + assert_eq!(db.status(BlockId::Number(1)).unwrap(), BlockStatus::Unknown); + } + + #[test] + fn returns_block_hash() { + let db = LightStorage::new_test(); + let genesis_hash = insert_block(&db, &Default::default(), 0, None); + assert_eq!(db.hash(0).unwrap(), Some(genesis_hash)); + assert_eq!(db.hash(1).unwrap(), None); + } + + #[test] + fn import_header_works() { + let db = LightStorage::new_test(); + + let genesis_hash = insert_block(&db, &Default::default(), 0, None); + assert_eq!(db.db.iter(columns::HEADER).count(), 1); + assert_eq!(db.db.iter(columns::BLOCK_INDEX).count(), 1); + + let _ = insert_block(&db, &genesis_hash, 1, None); + assert_eq!(db.db.iter(columns::HEADER).count(), 2); + assert_eq!(db.db.iter(columns::BLOCK_INDEX).count(), 2); + } + + #[test] + fn ancient_headers_are_replaced_with_cht() { + let db = LightStorage::new_test(); + + // insert genesis block header (never pruned) + let mut prev_hash = insert_block(&db, &Default::default(), 0, None); + + // insert SIZE blocks && ensure that nothing is pruned + for number in 0..cht::SIZE { + prev_hash = insert_block(&db, &prev_hash, 1 + number, None); + } + assert_eq!(db.db.iter(columns::HEADER).count(), (1 + cht::SIZE) as usize); + assert_eq!(db.db.iter(columns::CHT).count(), 0); + + // insert next SIZE blocks && ensure that nothing is pruned + for number in 0..cht::SIZE { + prev_hash = insert_block(&db, &prev_hash, 1 + cht::SIZE + number, None); + } + assert_eq!(db.db.iter(columns::HEADER).count(), (1 + cht::SIZE + cht::SIZE) as usize); + assert_eq!(db.db.iter(columns::CHT).count(), 0); + + // insert block #{2 * cht::SIZE + 1} && check that new CHT is created + headers of this CHT are pruned + insert_block(&db, &prev_hash, 1 + cht::SIZE + cht::SIZE, None); + assert_eq!(db.db.iter(columns::HEADER).count(), (1 + cht::SIZE + 1) as usize); + assert_eq!(db.db.iter(columns::CHT).count(), 1); + assert!((0..cht::SIZE).all(|i| db.db.get(columns::HEADER, &number_to_db_key(1 + i)).unwrap().is_none())); + } + + #[test] + fn get_cht_fails_for_genesis_block() { + assert!(LightStorage::::new_test().cht_root(cht::SIZE, 0).is_err()); + } + + #[test] + fn get_cht_fails_for_non_existant_cht() { + assert!(LightStorage::::new_test().cht_root(cht::SIZE, (cht::SIZE / 2) as u64).is_err()); + } + + #[test] + fn get_cht_works() { + let db = LightStorage::new_test(); + + // insert 1 + SIZE + SIZE + 1 blocks so that CHT#0 is created + let mut prev_hash = Default::default(); + for i in 0..1 + cht::SIZE + cht::SIZE + 1 { + prev_hash = insert_block(&db, &prev_hash, i as u64, None); + } + + let cht_root_1 = db.cht_root(cht::SIZE, cht::start_number(cht::SIZE, 0)).unwrap(); + let cht_root_2 = db.cht_root(cht::SIZE, (cht::start_number(cht::SIZE, 0) + cht::SIZE / 2) as u64).unwrap(); + let cht_root_3 = db.cht_root(cht::SIZE, cht::end_number(cht::SIZE, 0)).unwrap(); + assert_eq!(cht_root_1, cht_root_2); + assert_eq!(cht_root_2, cht_root_3); + } +} diff --git a/core/client/db/src/utils.rs b/core/client/db/src/utils.rs new file mode 100644 index 0000000000000..a2c24ddce0635 --- /dev/null +++ b/core/client/db/src/utils.rs @@ -0,0 +1,174 @@ +// Copyright 2017 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 . + +//! Db-based backend utility structures and functions, used by both +//! full and light storages. + +use std::sync::Arc; +use std::io; + +use kvdb::{KeyValueDB, DBTransaction}; +use kvdb_rocksdb::{Database, DatabaseConfig}; + +use client; +use codec::Decode; +use hashdb::DBValue; +use runtime_primitives::generic::BlockId; +use runtime_primitives::traits::{As, Block as BlockT, Header as HeaderT, Hash, HashFor, Zero}; +use 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 = 7; +/// Meta column. The set of keys in the column is shared by full && light storages. +pub const COLUMN_META: Option = Some(0); + +/// Keys of entries in COLUMN_META. +pub mod meta_keys { + /// Type of storage (full or light). + pub const TYPE: &[u8; 4] = b"type"; + /// Best block key. + pub const BEST_BLOCK: &[u8; 4] = b"best"; + /// Best authorities block key. + pub const BEST_AUTHORITIES: &[u8; 4] = b"auth"; +} + +/// Database metadata. +pub struct Meta { + /// Hash of the best known block. + pub best_hash: H, + /// Number of the best known block. + pub best_number: N, + /// Hash of the genesis block. + pub genesis_hash: H, +} + +/// Type of block key in the database (LE block number). +pub type BlockKey = [u8; 4]; + +/// Convert block number into key (LE representation). +pub fn number_to_db_key(n: N) -> BlockKey where N: As { + let n: u64 = n.as_(); + assert!(n & 0xffffffff00000000 == 0); + + [ + (n >> 24) as u8, + ((n >> 16) & 0xff) as u8, + ((n >> 8) & 0xff) as u8, + (n & 0xff) as u8 + ] +} + +/// Convert block key into block number. +pub fn db_key_to_number(key: &[u8]) -> client::error::Result where N: As { + match key.len() { + 4 => Ok((key[0] as u64) << 24 + | (key[1] as u64) << 16 + | (key[2] as u64) << 8 + | (key[3] as u64)).map(As::sa), + _ => Err(client::error::ErrorKind::Backend("Invalid block key".into()).into()), + } +} + +/// Maps database error to client error +pub fn db_err(err: io::Error) -> client::error::Error { + use std::error::Error; + client::error::ErrorKind::Backend(err.description().into()).into() +} + +/// Open RocksDB database. +pub fn open_database(config: &DatabaseSettings, db_type: &str) -> client::error::Result> { + let mut db_config = DatabaseConfig::with_columns(Some(NUM_COLUMNS)); + db_config.memory_budget = config.cache_size; + let path = config.path.to_str().ok_or_else(|| client::error::ErrorKind::Backend("Invalid database path".into()))?; + let db = Database::open(&db_config, &path).map_err(db_err)?; + + // check database type + match db.get(COLUMN_META, meta_keys::TYPE).map_err(db_err)? { + Some(stored_type) => { + if db_type.as_bytes() != &*stored_type { + return Err(client::error::ErrorKind::Backend( + format!("Unexpected database type. Expected: {}", db_type)).into()); + } + }, + None => { + let mut transaction = DBTransaction::new(); + transaction.put(COLUMN_META, meta_keys::TYPE, db_type.as_bytes()); + db.write(transaction).map_err(db_err)?; + }, + } + + Ok(Arc::new(db)) +} + +/// Convert block id to block key, reading number from db if required. +pub fn read_id(db: &KeyValueDB, col_index: Option, id: BlockId) -> Result, client::error::Error> + where + Block: BlockT, +{ + match id { + BlockId::Hash(h) => db.get(col_index, h.as_ref()) + .map(|v| v.map(|v| { + let mut key: [u8; 4] = [0; 4]; + key.copy_from_slice(&v); + key + })).map_err(db_err), + BlockId::Number(n) => Ok(Some(number_to_db_key(n))), + } +} + +/// Read database column entry for the given block. +pub fn read_db(db: &KeyValueDB, col_index: Option, col: Option, id: BlockId) -> client::error::Result> + where + Block: BlockT, +{ + read_id(db, col_index, id).and_then(|key| match key { + Some(key) => db.get(col, &key).map_err(db_err), + None => Ok(None), + }) +} + +/// Read meta from the database. +pub fn read_meta(db: &KeyValueDB, col_header: Option) -> Result::Header as HeaderT>::Number, Block::Hash>, client::error::Error> + where + Block: BlockT, +{ + let genesis_number = <::Header as HeaderT>::Number::zero(); + let (best_hash, best_number) = if let Some(Some(header)) = db.get(COLUMN_META, meta_keys::BEST_BLOCK).and_then(|id| + match id { + Some(id) => db.get(col_header, &id).map(|h| h.map(|b| Block::Header::decode(&mut &b[..]))), + None => Ok(None), + }).map_err(db_err)? + { + let hash = header.hash(); + debug!("DB Opened blockchain db, best {:?} ({})", hash, header.number()); + (hash, *header.number()) + } else { + (Default::default(), genesis_number) + }; + + let genesis_hash = db.get(col_header, &number_to_db_key(genesis_number)) + .map_err(db_err)? + .map(|raw| HashFor::::hash(&raw[..])) + .unwrap_or_default() + .into(); + + Ok(Meta { + best_hash, + best_number, + genesis_hash, + }) +} diff --git a/core/client/src/backend.rs b/core/client/src/backend.rs new file mode 100644 index 0000000000000..7466c8fb33260 --- /dev/null +++ b/core/client/src/backend.rs @@ -0,0 +1,107 @@ +// Copyright 2017 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 . + +//! Polkadot Client data backend + +use error; +use primitives::AuthorityId; +use runtime_primitives::bft::Justification; +use runtime_primitives::generic::BlockId; +use runtime_primitives::traits::{Block as BlockT, NumberFor}; +use state_machine::backend::Backend as StateBackend; +use patricia_trie::NodeCodec; +use hashdb::Hasher; + +/// Block insertion operation. Keeps hold if the inserted block state and data. +pub trait BlockImportOperation +where + Block: BlockT, + H: Hasher, + C: NodeCodec, +{ + /// Associated state backend type. + type State: StateBackend; + + /// Returns pending state. Returns None for backends with locally-unavailable state data. + fn state(&self) -> error::Result>; + /// Append block data to the transaction. + fn set_block_data( + &mut self, + header: Block::Header, + body: Option>, + justification: Option>, + is_new_best: bool + ) -> error::Result<()>; + + /// Append authorities set to the transaction. This is a set of parent block (set which + /// has been used to check justification of this block). + fn update_authorities(&mut self, authorities: Vec); + /// Inject storage data into the database. + fn update_storage(&mut self, update: >::Transaction) -> error::Result<()>; + /// Inject storage data into the database replacing any existing data. + fn reset_storage, Vec)>>(&mut self, iter: I) -> error::Result<()>; +} + +/// Client backend. Manages the data layer. +/// +/// Note on state pruning: while an object from `state_at` is alive, the state +/// should not be pruned. The backend should internally reference-count +/// its state objects. +/// +/// The same applies for live `BlockImportOperation`s: while an import operation building on a parent `P` +/// is alive, the state for `P` should not be pruned. +pub trait Backend: Send + Sync +where + Block: BlockT, + H: Hasher, + C: NodeCodec, +{ + /// Associated block insertion operation type. + type BlockImportOperation: BlockImportOperation; + /// Associated blockchain backend type. + type Blockchain: ::blockchain::Backend; + /// Associated state backend type. + type State: StateBackend; + + /// Begin a new block insertion transaction with given parent block id. + /// When constructing the genesis, this is called with all-zero hash. + fn begin_operation(&self, block: BlockId) -> error::Result; + /// Commit block insertion. + fn commit_operation(&self, transaction: Self::BlockImportOperation) -> error::Result<()>; + /// Returns reference to blockchain backend. + fn blockchain(&self) -> &Self::Blockchain; + /// Returns state backend with post-state of given block. + fn state_at(&self, block: BlockId) -> error::Result; + /// Attempts to revert the chain by `n` blocks. Returns the number of blocks that were + /// successfully reverted. + fn revert(&self, n: NumberFor) -> error::Result>; +} + +/// Mark for all Backend implementations, that are making use of state data, stored locally. +pub trait LocalBackend: Backend +where + Block: BlockT, + H: Hasher, + C: NodeCodec, +{} + +/// Mark for all Backend implementations, that are fetching required state data from remote nodes. +pub trait RemoteBackend: Backend +where + Block: BlockT, + H: Hasher, + C: NodeCodec, +{} diff --git a/core/client/src/block_builder.rs b/core/client/src/block_builder.rs new file mode 100644 index 0000000000000..0d78f30cc413b --- /dev/null +++ b/core/client/src/block_builder.rs @@ -0,0 +1,140 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Utility struct to build a block. + +use std::vec::Vec; +use codec::{Decode, Encode}; +use state_machine::{self, native_when_possible}; +use runtime_primitives::traits::{Header as HeaderT, Hash, Block as BlockT, One, HashFor}; +use runtime_primitives::generic::BlockId; +use {backend, error, Client, CallExecutor}; +use runtime_primitives::{ApplyResult, ApplyOutcome}; +use patricia_trie::NodeCodec; +use primitives::{Blake2Hasher, RlpCodec}; +use hashdb::Hasher; +use rlp::Encodable; + +/// Utility for building new (valid) blocks from a stream of extrinsics. +pub struct BlockBuilder +where + B: backend::Backend, + E: CallExecutor + Clone, + Block: BlockT, + H: Hasher, + H::Out: Encodable + Ord, + C: NodeCodec, +{ + header: ::Header, + extrinsics: Vec<::Extrinsic>, + executor: E, + state: B::State, + changes: state_machine::OverlayedChanges, +} + +impl BlockBuilder +where + B: backend::Backend, + E: CallExecutor + Clone, + Block: BlockT, +{ + /// Create a new instance of builder from the given client, building on the latest block. + pub fn new(client: &Client) -> error::Result { + client.info().and_then(|i| Self::at_block(&BlockId::Hash(i.chain.best_hash), client)) + } + + /// Create a new instance of builder from the given client using a particular block's ID to + /// build upon. + pub fn at_block(block_id: &BlockId, client: &Client) -> error::Result { + let number = client.block_number_from_id(block_id)? + .ok_or_else(|| error::ErrorKind::UnknownBlock(format!("{}", block_id)))? + + One::one(); + + let parent_hash = client.block_hash_from_id(block_id)? + .ok_or_else(|| error::ErrorKind::UnknownBlock(format!("{}", block_id)))?; + + let executor = client.executor().clone(); + let state = client.state_at(block_id)?; + let mut changes = Default::default(); + let header = <::Header as HeaderT>::new( + number, + Default::default(), + Default::default(), + parent_hash, + Default::default() + ); + + executor.call_at_state(&state, &mut changes, "initialise_block", &header.encode(), native_when_possible())?; + changes.commit_prospective(); + + Ok(BlockBuilder { + header, + extrinsics: Vec::new(), + executor, + state, + changes, + }) + } + + /// Push onto the block's list of extrinsics. This will ensure the extrinsic + /// can be validly executed (by executing it); if it is invalid, it'll be returned along with + /// the error. Otherwise, it will return a mutable reference to self (in order to chain). + pub fn push(&mut self, xt: ::Extrinsic) -> error::Result<()> { + match self.executor.call_at_state(&self.state, &mut self.changes, "apply_extrinsic", &xt.encode(), native_when_possible()) { + Ok((result, _)) => { + match ApplyResult::decode(&mut result.as_slice()) { + Some(Ok(ApplyOutcome::Success)) | Some(Ok(ApplyOutcome::Fail)) => { + self.extrinsics.push(xt); + self.changes.commit_prospective(); + Ok(()) + } + Some(Err(e)) => { + self.changes.discard_prospective(); + Err(error::ErrorKind::ApplyExtinsicFailed(e).into()) + } + None => { + self.changes.discard_prospective(); + Err(error::ErrorKind::CallResultDecode("apply_extrinsic").into()) + } + } + } + Err(e) => { + self.changes.discard_prospective(); + Err(e) + } + } + } + + /// Consume the builder to return a valid `Block` containing all pushed extrinsics. + pub fn bake(mut self) -> error::Result { + let (output, _) = self.executor.call_at_state( + &self.state, + &mut self.changes, + "finalise_block", + &[], + native_when_possible(), + )?; + self.header = <::Header as Decode>::decode(&mut &output[..]) + .expect("Header came straight out of runtime so must be valid"); + + debug_assert_eq!( + self.header.extrinsics_root().clone(), + HashFor::::ordered_trie_root(self.extrinsics.iter().map(Encode::encode)), + ); + + Ok(::new(self.header, self.extrinsics)) + } +} diff --git a/core/client/src/blockchain.rs b/core/client/src/blockchain.rs new file mode 100644 index 0000000000000..ab5dc036e960e --- /dev/null +++ b/core/client/src/blockchain.rs @@ -0,0 +1,92 @@ +// Copyright 2017 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 . + +//! Polkadot blockchain trait + +use primitives::AuthorityId; +use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, NumberFor}; +use runtime_primitives::generic::BlockId; +use runtime_primitives::bft::Justification; + +use error::{ErrorKind, Result}; + +/// Blockchain database header backend. Does not perform any validation. +pub trait HeaderBackend: Send + Sync { + /// Get block header. Returns `None` if block is not found. + fn header(&self, id: BlockId) -> Result>; + /// Get blockchain info. + fn info(&self) -> Result>; + /// Get block status. + fn status(&self, id: BlockId) -> Result; + /// Get block number by hash. Returns `None` if the header is not in the chain. + fn number(&self, hash: Block::Hash) -> Result::Header as HeaderT>::Number>>; + /// Get block hash by number. Returns `None` if the header is not in the chain. + fn hash(&self, number: NumberFor) -> Result>; + + /// Get block header. Returns `UnknownBlock` error if block is not found. + fn expect_header(&self, id: BlockId) -> Result { + self.header(id)?.ok_or_else(|| ErrorKind::UnknownBlock(format!("{}", id)).into()) + } +} + +/// Blockchain database backend. Does not perform any validation. +pub trait Backend: HeaderBackend { + /// Get block body. Returns `None` if block is not found. + fn body(&self, id: BlockId) -> Result::Extrinsic>>>; + /// Get block justification. Returns `None` if justification does not exist. + fn justification(&self, id: BlockId) -> Result>>; + + /// Returns data cache reference, if it is enabled on this backend. + fn cache(&self) -> Option<&Cache>; +} + +/// Blockchain optional data cache. +pub trait Cache: Send + Sync { + /// Returns the set of authorities, that was active at given block or None if there's no entry in the cache. + fn authorities_at(&self, block: BlockId) -> Option>; +} + +/// Block import outcome +pub enum ImportResult { + /// Imported successfully. + Imported, + /// Block already exists, skippped. + AlreadyInChain, + /// Unknown parent. + UnknownParent, + /// Other errror. + Err(E), +} + +/// Blockchain info +#[derive(Debug)] +pub struct Info { + /// Best block hash. + pub best_hash: <::Header as HeaderT>::Hash, + /// Best block number. + pub best_number: <::Header as HeaderT>::Number, + /// Genesis block hash. + pub genesis_hash: <::Header as HeaderT>::Hash, +} + +/// Block status. +#[derive(Debug, PartialEq, Eq)] +pub enum BlockStatus { + /// Already in the blockchain. + InChain, + /// Not in the queue or the blockchain. + Unknown, +} diff --git a/core/client/src/call_executor.rs b/core/client/src/call_executor.rs new file mode 100644 index 0000000000000..d78025574185a --- /dev/null +++ b/core/client/src/call_executor.rs @@ -0,0 +1,199 @@ +// Copyright 2017 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 std::cmp::Ord; +use runtime_primitives::generic::BlockId; +use runtime_primitives::traits::Block as BlockT; +use state_machine::{self, OverlayedChanges, Ext, + CodeExecutor, ExecutionManager, native_when_possible}; +use executor::{RuntimeVersion, RuntimeInfo}; +use patricia_trie::NodeCodec; +use hashdb::Hasher; +use rlp::Encodable; +use codec::Decode; +use primitives::{Blake2Hasher, RlpCodec}; + +use backend; +use error; + +/// Information regarding the result of a call. +#[derive(Debug, Clone)] +pub struct CallResult { + /// The data that was returned from the call. + pub return_data: Vec, + /// The changes made to the state by the call. + pub changes: OverlayedChanges, +} + +/// Method call executor. +pub trait CallExecutor +where + B: BlockT, + H: Hasher, + H::Out: Ord + Encodable, + C: NodeCodec, +{ + /// Externalities error type. + type Error: state_machine::Error; + + /// Execute a call to a contract on top of state in a block of given hash. + /// + /// No changes are made. + fn call(&self, + id: &BlockId, + method: &str, + call_data: &[u8], + ) -> Result; + + /// Extract RuntimeVersion of given block + /// + /// No changes are made. + fn runtime_version(&self, id: &BlockId) -> Result; + + /// Execute a call to a contract on top of given state. + /// + /// No changes are made. + fn call_at_state< + S: state_machine::Backend, + F: FnOnce(Result, Self::Error>, Result, Self::Error>) -> Result, Self::Error>, + >(&self, + state: &S, + overlay: &mut OverlayedChanges, + method: &str, + call_data: &[u8], + manager: ExecutionManager + ) -> Result<(Vec, S::Transaction), error::Error>; + + /// Execute a call to a contract on top of given state, gathering execution proof. + /// + /// No changes are made. + fn prove_at_state>(&self, + state: S, + overlay: &mut OverlayedChanges, + method: &str, + call_data: &[u8] + ) -> Result<(Vec, Vec>), error::Error>; + + /// Get runtime version if supported. + fn native_runtime_version(&self) -> Option; +} + +/// Call executor that executes methods locally, querying all required +/// data from local backend. +pub struct LocalCallExecutor { + backend: Arc, + executor: E, +} + +impl LocalCallExecutor { + /// Creates new instance of local call executor. + pub fn new(backend: Arc, executor: E) -> Self { + LocalCallExecutor { backend, executor } + } +} + +impl Clone for LocalCallExecutor where E: Clone { + fn clone(&self) -> Self { + LocalCallExecutor { + backend: self.backend.clone(), + executor: self.executor.clone(), + } + } +} + +impl CallExecutor for LocalCallExecutor +where + B: backend::LocalBackend, + E: CodeExecutor + RuntimeInfo, + Block: BlockT, +{ + type Error = E::Error; + + fn call(&self, + id: &BlockId, + method: &str, + call_data: &[u8], + ) -> error::Result { + let mut changes = OverlayedChanges::default(); + let (return_data, _) = self.call_at_state( + &self.backend.state_at(*id)?, + &mut changes, + method, + call_data, + native_when_possible(), + )?; + Ok(CallResult { return_data, changes }) + } + + fn runtime_version(&self, id: &BlockId) -> error::Result { + let mut overlay = OverlayedChanges::default(); + let state = self.backend.state_at(*id)?; + use state_machine::Backend; + let code = state.storage(b":code") + .map_err(|e| error::ErrorKind::Execution(Box::new(e)))? + .ok_or(error::ErrorKind::VersionInvalid)? + .to_vec(); + let heap_pages = state.storage(b":heappages") + .map_err(|e| error::ErrorKind::Execution(Box::new(e)))? + .and_then(|v| u64::decode(&mut &v[..])) + .unwrap_or(8) as usize; + + self.executor.runtime_version(&mut Ext::new(&mut overlay, &state), heap_pages, &code) + .ok_or(error::ErrorKind::VersionInvalid.into()) + } + + fn call_at_state< + S: state_machine::Backend, + F: FnOnce(Result, Self::Error>, Result, Self::Error>) -> Result, Self::Error>, + >(&self, + state: &S, + changes: &mut OverlayedChanges, + method: &str, + call_data: &[u8], + manager: ExecutionManager, + ) -> error::Result<(Vec, S::Transaction)> { + state_machine::execute_using_consensus_failure_handler( + state, + changes, + &self.executor, + method, + call_data, + manager, + ).map_err(Into::into) + } + + fn prove_at_state>(&self, + state: S, + changes: &mut OverlayedChanges, + method: &str, + call_data: &[u8] + ) -> Result<(Vec, Vec>), error::Error> { + state_machine::prove_execution( + state, + changes, + &self.executor, + method, + call_data, + ) + .map(|(result, proof, _)| (result, proof)) + .map_err(Into::into) + } + + fn native_runtime_version(&self) -> Option { + ::NATIVE_VERSION + } +} diff --git a/core/client/src/cht.rs b/core/client/src/cht.rs new file mode 100644 index 0000000000000..2f2f38523b179 --- /dev/null +++ b/core/client/src/cht.rs @@ -0,0 +1,274 @@ +// Copyright 2017 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 . + +//! Canonical hash trie definitions and helper functions. +//! +//! Each CHT is a trie mapping block numbers to canonical hash. +//! One is generated for every `SIZE` blocks, allowing us to discard those blocks in +//! favor of the trie root. When the "ancient" blocks need to be accessed, we simply +//! request an inclusion proof of a specific block number against the trie with the +//! root has. A correct proof implies that the claimed block is identical to the one +//! we discarded. + +use hashdb; +use heapsize::HeapSizeOf; +use patricia_trie::NodeCodec; +use rlp::Encodable; +use triehash; + +use primitives::H256; +use runtime_primitives::traits::{As, Header as HeaderT, SimpleArithmetic, One}; +use state_machine::backend::InMemory as InMemoryState; +use state_machine::{prove_read, read_proof_check}; + +use error::{Error as ClientError, ErrorKind as ClientErrorKind, Result as ClientResult}; + +/// The size of each CHT. This value is passed to every CHT-related function from +/// production code. Other values are passed from tests. +pub const SIZE: u64 = 2048; + +/// Returns Some(cht_number) if CHT is need to be built when the block with given number is canonized. +pub fn is_build_required(cht_size: u64, block_num: N) -> Option + where + N: Clone + SimpleArithmetic, +{ + let block_cht_num = block_to_cht_number(cht_size, block_num.clone())?; + let two = N::one() + N::one(); + if block_cht_num < two { + return None; + } + let cht_start = start_number(cht_size, block_cht_num.clone()); + if cht_start != block_num { + return None; + } + + Some(block_cht_num - two) +} + +/// Compute a CHT root from an iterator of block hashes. Fails if shorter than +/// SIZE items. The items are assumed to proceed sequentially from `start_number(cht_num)`. +/// Discards the trie's nodes. +pub fn compute_root( + cht_size: u64, + cht_num: Header::Number, + hashes: I, +) -> Option + where + Header: HeaderT, + Header::Hash: From, + Hasher: hashdb::Hasher, + Hasher::Out: Ord + Encodable, + I: IntoIterator>, +{ + build_pairs::(cht_size, cht_num, hashes) + .map(|pairs| triehash::trie_root::(pairs).into()) +} + +/// Build CHT-based header proof. +pub fn build_proof( + cht_size: u64, + cht_num: Header::Number, + block_num: Header::Number, + hashes: I +) -> Option>> + where + Header: HeaderT, + Hasher: hashdb::Hasher, + Hasher::Out: Ord + Encodable + HeapSizeOf, + Codec: NodeCodec, + I: IntoIterator>, +{ + let transaction = build_pairs::(cht_size, cht_num, hashes)? + .into_iter() + .map(|(k, v)| (k, Some(v))) + .collect::>(); + let storage = InMemoryState::::default().update(transaction); + let (value, proof) = prove_read(storage, &encode_cht_key(block_num)).ok()?; + if value.is_none() { + None + } else { + Some(proof) + } +} + +/// Check CHT-based header proof. +pub fn check_proof( + local_root: Header::Hash, + local_number: Header::Number, + remote_hash: Header::Hash, + remote_proof: Vec> +) -> ClientResult<()> + where + Header: HeaderT, + Header::Hash: From, + Hasher: hashdb::Hasher, + Hasher::Out: Ord + Encodable + HeapSizeOf + From, + Codec: NodeCodec, +{ + let local_cht_key = encode_cht_key(local_number); + let local_cht_value = read_proof_check::(local_root.into(), remote_proof, + &local_cht_key).map_err(|e| ClientError::from(e))?; + let local_cht_value = local_cht_value.ok_or_else(|| ClientErrorKind::InvalidHeaderProof)?; + let local_hash: Header::Hash = decode_cht_value(&local_cht_value).ok_or_else(|| ClientErrorKind::InvalidHeaderProof)?; + match local_hash == remote_hash { + true => Ok(()), + false => Err(ClientErrorKind::InvalidHeaderProof.into()), + } +} + +/// Build pairs for computing CHT. +fn build_pairs( + cht_size: u64, + cht_num: Header::Number, + hashes: I +) -> Option, Vec)>> + where + Header: HeaderT, + I: IntoIterator>, +{ + let start_num = start_number(cht_size, cht_num); + let mut pairs = Vec::new(); + let mut hash_number = start_num; + for hash in hashes.into_iter().take(cht_size as usize) { + pairs.push(hash.map(|hash| ( + encode_cht_key(hash_number).to_vec(), + encode_cht_value(hash) + ))?); + hash_number += Header::Number::one(); + } + + if pairs.len() as u64 == cht_size { + Some(pairs) + } else { + None + } +} + +/// Get the starting block of a given CHT. +/// CHT 0 includes block 1...SIZE, +/// CHT 1 includes block SIZE + 1 ... 2*SIZE +/// More generally: CHT N includes block (1 + N*SIZE)...((N+1)*SIZE). +/// This is because the genesis hash is assumed to be known +/// and including it would be redundant. +pub fn start_number(cht_size: u64, cht_num: N) -> N { + (cht_num * As::sa(cht_size)) + N::one() +} + +/// Get the ending block of a given CHT. +pub fn end_number(cht_size: u64, cht_num: N) -> N { + (cht_num + N::one()) * As::sa(cht_size) +} + +/// Convert a block number to a CHT number. +/// Returns `None` for `block_num` == 0, `Some` otherwise. +pub fn block_to_cht_number(cht_size: u64, block_num: N) -> Option { + if block_num == N::zero() { + None + } else { + Some((block_num - N::one()) / As::sa(cht_size)) + } +} + +/// Convert header number into CHT key. +pub fn encode_cht_key>(number: N) -> Vec { + let number: u64 = number.as_(); + vec![ + (number >> 56) as u8, + ((number >> 48) & 0xff) as u8, + ((number >> 40) & 0xff) as u8, + ((number >> 32) & 0xff) as u8, + ((number >> 24) & 0xff) as u8, + ((number >> 16) & 0xff) as u8, + ((number >> 8) & 0xff) as u8, + (number & 0xff) as u8 + ] +} + +/// Convert header hash into CHT value. +fn encode_cht_value>(hash: Hash) -> Vec { + hash.as_ref().to_vec() +} + +/// Convert CHT value into block header hash. +pub fn decode_cht_value>(value: &[u8]) -> Option { + match value.len() { + 32 => Some(H256::from_slice(&value[0..32]).into()), + _ => None, + } + +} + +#[cfg(test)] +mod tests { + use primitives::{Blake2Hasher, RlpCodec}; + use test_client::runtime::Header; + use super::*; + + #[test] + fn is_build_required_works() { + assert_eq!(is_build_required(SIZE, 0), None); + assert_eq!(is_build_required(SIZE, 1), None); + assert_eq!(is_build_required(SIZE, SIZE), None); + assert_eq!(is_build_required(SIZE, SIZE + 1), None); + assert_eq!(is_build_required(SIZE, 2 * SIZE), None); + assert_eq!(is_build_required(SIZE, 2 * SIZE + 1), Some(0)); + assert_eq!(is_build_required(SIZE, 3 * SIZE), None); + assert_eq!(is_build_required(SIZE, 3 * SIZE + 1), Some(1)); + } + + #[test] + fn start_number_works() { + assert_eq!(start_number(SIZE, 0), 1); + assert_eq!(start_number(SIZE, 1), SIZE + 1); + assert_eq!(start_number(SIZE, 2), SIZE + SIZE + 1); + } + + #[test] + fn end_number_works() { + assert_eq!(end_number(SIZE, 0), SIZE); + assert_eq!(end_number(SIZE, 1), SIZE + SIZE); + assert_eq!(end_number(SIZE, 2), SIZE + SIZE + SIZE); + } + + #[test] + fn build_pairs_fails_when_no_enough_blocks() { + assert!(build_pairs::(SIZE, 0, vec![Some(1.into()); SIZE as usize / 2]).is_none()); + } + + #[test] + fn build_pairs_fails_when_missing_block() { + assert!(build_pairs::(SIZE, 0, ::std::iter::repeat(Some(1.into())).take(SIZE as usize / 2) + .chain(::std::iter::once(None)) + .chain(::std::iter::repeat(Some(2.into())).take(SIZE as usize / 2 - 1))).is_none()); + } + + #[test] + fn compute_root_works() { + assert!(compute_root::(SIZE, 42, vec![Some(1.into()); SIZE as usize]).is_some()); + } + + #[test] + fn build_proof_fails_when_querying_wrong_block() { + assert!(build_proof::( + SIZE, 0, (SIZE * 1000) as u64, vec![Some(1.into()); SIZE as usize]).is_none()); + } + + #[test] + fn build_proof_works() { + assert!(build_proof::( + SIZE, 0, (SIZE / 2) as u64, vec![Some(1.into()); SIZE as usize]).is_some()); + } +} diff --git a/core/client/src/client.rs b/core/client/src/client.rs new file mode 100644 index 0000000000000..9246e61ec85d9 --- /dev/null +++ b/core/client/src/client.rs @@ -0,0 +1,810 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Substrate Client + +use std::sync::Arc; +use futures::sync::mpsc; +use parking_lot::{Mutex, RwLock}; +use primitives::AuthorityId; +use runtime_primitives::{bft::Justification, generic::{BlockId, SignedBlock, Block as RuntimeBlock}}; +use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, Zero, One, As, NumberFor}; +use runtime_primitives::BuildStorage; +use runtime_support::metadata::JSONMetadataDecodable; +use primitives::{Blake2Hasher, RlpCodec}; +use primitives::storage::{StorageKey, StorageData}; +use codec::{Encode, Decode}; +use state_machine::{ + Ext, OverlayedChanges, Backend as StateBackend, CodeExecutor, + ExecutionStrategy, ExecutionManager, prove_read +}; + +use backend::{self, BlockImportOperation}; +use blockchain::{self, Info as ChainInfo, Backend as ChainBackend, HeaderBackend as ChainHeaderBackend}; +use call_executor::{CallExecutor, LocalCallExecutor}; +use executor::{RuntimeVersion, RuntimeInfo}; +use notifications::{StorageNotifications, StorageEventStream}; +use {cht, error, in_mem, block_builder, runtime_io, bft, genesis}; + +/// Type that implements `futures::Stream` of block import events. +pub type BlockchainEventStream = mpsc::UnboundedReceiver>; + +/// Substrate Client +pub struct Client where Block: BlockT { + backend: Arc, + executor: E, + storage_notifications: Mutex>, + import_notification_sinks: Mutex>>>, + import_lock: Mutex<()>, + importing_block: RwLock>, // holds the block hash currently being imported. TODO: replace this with block queue + execution_strategy: ExecutionStrategy, +} + +/// A source of blockchain evenets. +pub trait BlockchainEvents { + /// Get block import event stream. + fn import_notification_stream(&self) -> BlockchainEventStream; + + /// Get storage changes event stream. + /// + /// Passing `None` as `filter_keys` subscribes to all storage changes. + fn storage_changes_notification_stream(&self, filter_keys: Option<&[StorageKey]>) -> error::Result>; +} + +/// Chain head information. +pub trait ChainHead { + /// Get best block header. + fn best_block_header(&self) -> Result<::Header, error::Error>; +} + +/// Fetch block body by ID. +pub trait BlockBody { + /// Get block body by ID. Returns `None` if the body is not stored. + fn block_body(&self, id: &BlockId) -> error::Result::Extrinsic>>>; +} + +/// Client info +// TODO: split queue info from chain info and amalgamate into single struct. +#[derive(Debug)] +pub struct ClientInfo { + /// Best block hash. + pub chain: ChainInfo, + /// Best block number in the queue. + pub best_queued_number: Option<<::Header as HeaderT>::Number>, + /// Best queued block hash. + pub best_queued_hash: Option, +} + +/// Block import result. +#[derive(Debug)] +pub enum ImportResult { + /// Added to the import queue. + Queued, + /// Already in the import queue. + AlreadyQueued, + /// Already in the blockchain. + AlreadyInChain, + /// Block or parent is known to be bad. + KnownBad, + /// Block parent is not in the chain. + UnknownParent, +} + +/// Block status. +#[derive(Debug, PartialEq, Eq)] +pub enum BlockStatus { + /// Added to the import queue. + Queued, + /// Already in the blockchain. + InChain, + /// Block or parent is known to be bad. + KnownBad, + /// Not in the queue or the blockchain. + Unknown, +} + +/// Block data origin. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum BlockOrigin { + /// Genesis block built into the client. + Genesis, + /// Block is part of the initial sync with the network. + NetworkInitialSync, + /// Block was broadcasted on the network. + NetworkBroadcast, + /// Block that was received from the network and validated in the consensus process. + ConsensusBroadcast, + /// Block that was collated by this node. + Own, + /// Block was imported from a file. + File, +} + +/// Summary of an imported block +#[derive(Clone, Debug)] +pub struct BlockImportNotification { + /// Imported block header hash. + pub hash: Block::Hash, + /// Imported block origin. + pub origin: BlockOrigin, + /// Imported block header. + pub header: Block::Header, + /// Is this the new best block. + pub is_new_best: bool, +} + +/// A header paired with a justification which has already been checked. +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct JustifiedHeader { + header: ::Header, + justification: ::bft::Justification, + authorities: Vec, +} + +impl JustifiedHeader { + /// Deconstruct the justified header into parts. + pub fn into_inner(self) -> (::Header, ::bft::Justification, Vec) { + (self.header, self.justification, self.authorities) + } +} + +/// Create an instance of in-memory client. +pub fn new_in_mem( + executor: E, + genesis_storage: S, +) -> error::Result, LocalCallExecutor, E>, Block>> + where + E: CodeExecutor + RuntimeInfo, + S: BuildStorage, + Block: BlockT, +{ + let backend = Arc::new(in_mem::Backend::new()); + let executor = LocalCallExecutor::new(backend.clone(), executor); + Client::new(backend, executor, genesis_storage, ExecutionStrategy::NativeWhenPossible) +} + +impl Client where + B: backend::Backend, + E: CallExecutor, + Block: BlockT, +{ + /// Creates new Substrate Client with given blockchain and code executor. + pub fn new( + backend: Arc, + executor: E, + build_genesis_storage: S, + execution_strategy: ExecutionStrategy, + ) -> error::Result { + if backend.blockchain().header(BlockId::Number(Zero::zero()))?.is_none() { + let genesis_storage = build_genesis_storage.build_storage()?; + let genesis_block = genesis::construct_genesis_block::(&genesis_storage); + info!("Initialising Genesis block/state (state: {}, header-hash: {})", genesis_block.header().state_root(), genesis_block.header().hash()); + let mut op = backend.begin_operation(BlockId::Hash(Default::default()))?; + op.reset_storage(genesis_storage.into_iter())?; + op.set_block_data(genesis_block.deconstruct().0, Some(vec![]), None, true)?; + backend.commit_operation(op)?; + } + Ok(Client { + backend, + executor, + storage_notifications: Default::default(), + import_notification_sinks: Default::default(), + import_lock: Default::default(), + importing_block: Default::default(), + execution_strategy, + }) + } + + /// Get a reference to the state at a given block. + pub fn state_at(&self, block: &BlockId) -> error::Result { + self.backend.state_at(*block) + } + + /// Expose backend reference. To be used in tests only + pub fn backend(&self) -> &Arc { + &self.backend + } + + /// Return single storage entry of contract under given address in state in a block of given hash. + pub fn storage(&self, id: &BlockId, key: &StorageKey) -> error::Result> { + Ok(self.state_at(id)? + .storage(&key.0).map_err(|e| error::Error::from_state(Box::new(e)))? + .map(StorageData)) + } + + /// Get the code at a given block. + pub fn code_at(&self, id: &BlockId) -> error::Result> { + Ok(self.storage(id, &StorageKey(b":code".to_vec()))? + .expect("None is returned if there's no value stored for the given key; ':code' key is always defined; qed").0) + } + + /// Get the set of authorities at a given block. + pub fn authorities_at(&self, id: &BlockId) -> error::Result> { + match self.backend.blockchain().cache().and_then(|cache| cache.authorities_at(*id)) { + Some(cached_value) => Ok(cached_value), + None => self.executor.call(id, "authorities",&[]) + .and_then(|r| Vec::::decode(&mut &r.return_data[..]) + .ok_or(error::ErrorKind::AuthLenInvalid.into())) + } + } + + /// Get the RuntimeVersion at a given block. + pub fn runtime_version_at(&self, id: &BlockId) -> error::Result { + // TODO: Post Poc-2 return an error if version is missing + self.executor.runtime_version(id) + } + + /// Get call executor reference. + pub fn executor(&self) -> &E { + &self.executor + } + + /// Returns the runtime metadata as JSON. + pub fn json_metadata(&self, id: &BlockId) -> error::Result { + self.executor.call(id, "json_metadata",&[]) + .and_then(|r| Vec::::decode(&mut &r.return_data[..]) + .ok_or("JSON Metadata decoding failed".into())) + .and_then(|metadata| { + let mut json = metadata.into_iter().enumerate().fold(String::from("{"), + |mut json, (i, m)| { + if i > 0 { + json.push_str(","); + } + let (mtype, val) = m.into_json_string(); + json.push_str(&format!(r#" "{}": {}"#, mtype, val)); + json + } + ); + json.push_str(" }"); + + Ok(json) + }) + } + + /// Reads storage value at a given block + key, returning read proof. + pub fn read_proof(&self, id: &BlockId, key: &[u8]) -> error::Result>> { + self.state_at(id) + .and_then(|state| prove_read(state, key) + .map(|(_, proof)| proof) + .map_err(Into::into)) + } + + /// Execute a call to a contract on top of state in a block of given hash + /// AND returning execution proof. + /// + /// No changes are made. + pub fn execution_proof(&self, id: &BlockId, method: &str, call_data: &[u8]) -> error::Result<(Vec, Vec>)> { + self.state_at(id).and_then(|state| self.executor.prove_at_state(state, &mut Default::default(), method, call_data)) + } + + /// Reads given header and generates CHT-based header proof. + pub fn header_proof(&self, id: &BlockId) -> error::Result<(Block::Header, Vec>)> { + self.header_proof_with_cht_size(id, cht::SIZE) + } + + /// Reads given header and generates CHT-based header proof for CHT of given size. + pub fn header_proof_with_cht_size(&self, id: &BlockId, cht_size: u64) -> error::Result<(Block::Header, Vec>)> { + let proof_error = || error::ErrorKind::Backend(format!("Failed to generate header proof for {:?}", id)); + let header = self.header(id)?.ok_or_else(|| error::ErrorKind::UnknownBlock(format!("{:?}", id)))?; + let block_num = *header.number(); + let cht_num = cht::block_to_cht_number(cht_size, block_num).ok_or_else(proof_error)?; + let cht_start = cht::start_number(cht_size, cht_num); + let headers = (cht_start.as_()..).map(|num| self.block_hash(As::sa(num)).unwrap_or_default()); + let proof = cht::build_proof::(cht_size, cht_num, block_num, headers) + .ok_or_else(proof_error)?; + Ok((header, proof)) + } + + /// Set up the native execution environment to call into a native runtime code. + pub fn using_environment T, T>( + &self, f: F + ) -> error::Result { + self.using_environment_at(&BlockId::Number(self.info()?.chain.best_number), &mut Default::default(), f) + } + + /// Set up the native execution environment to call into a native runtime code. + pub fn using_environment_at T, T>( + &self, + id: &BlockId, + overlay: &mut OverlayedChanges, + f: F + ) -> error::Result { + Ok(runtime_io::with_externalities(&mut Ext::new(overlay, &self.state_at(id)?), f)) + } + + /// Create a new block, built on the head of the chain. + pub fn new_block(&self) -> error::Result> + where E: Clone + { + block_builder::BlockBuilder::new(self) + } + + /// Create a new block, built on top of `parent`. + pub fn new_block_at(&self, parent: &BlockId) -> error::Result> + where E: Clone + { + block_builder::BlockBuilder::at_block(parent, &self) + } + + /// Call a runtime function at given block. + pub fn call_api(&self, at: &BlockId, function: &'static str, args: &A) -> error::Result + where + A: Encode, + R: Decode, + { + let parent = at; + let header = <::Header as HeaderT>::new( + self.block_number_from_id(&parent)? + .ok_or_else(|| error::ErrorKind::UnknownBlock(format!("{:?}", parent)))? + As::sa(1), + Default::default(), + Default::default(), + self.block_hash_from_id(&parent)? + .ok_or_else(|| error::ErrorKind::UnknownBlock(format!("{:?}", parent)))?, + Default::default() + ); + self.state_at(&parent).and_then(|state| { + let mut overlay = Default::default(); + let execution_manager = || ExecutionManager::Both(|wasm_result, native_result| { + warn!("Consensus error between wasm and native runtime execution at block {:?}", at); + warn!(" Function {:?}", function); + warn!(" Native result {:?}", native_result); + warn!(" Wasm result {:?}", wasm_result); + wasm_result + }); + self.executor().call_at_state( + &state, + &mut overlay, + "initialise_block", + &header.encode(), + execution_manager() + )?; + let (r, _) = args.using_encoded(|input| + self.executor().call_at_state( + &state, + &mut overlay, + function, + input, + execution_manager() + ))?; + Ok(R::decode(&mut &r[..]) + .ok_or_else(|| error::Error::from(error::ErrorKind::CallResultDecode(function)))?) + }) + } + + /// Check a header's justification. + pub fn check_justification( + &self, + header: ::Header, + justification: ::bft::UncheckedJustification, + ) -> error::Result> { + let parent_hash = header.parent_hash().clone(); + let authorities = self.authorities_at(&BlockId::Hash(parent_hash))?; + let just = ::bft::check_justification::(&authorities[..], parent_hash, justification) + .map_err(|_| + error::ErrorKind::BadJustification( + format!("{}", header.hash()) + ) + )?; + Ok(JustifiedHeader { + header, + justification: just, + authorities, + }) + } + + /// Queue a block for import. + pub fn import_block( + &self, + origin: BlockOrigin, + header: JustifiedHeader, + body: Option::Extrinsic>>, + ) -> error::Result { + let (header, justification, authorities) = header.into_inner(); + let parent_hash = header.parent_hash().clone(); + match self.backend.blockchain().status(BlockId::Hash(parent_hash))? { + blockchain::BlockStatus::InChain => {}, + blockchain::BlockStatus::Unknown => return Ok(ImportResult::UnknownParent), + } + let hash = header.hash(); + let _import_lock = self.import_lock.lock(); + let height: u64 = header.number().as_(); + *self.importing_block.write() = Some(hash); + let result = self.execute_and_import_block(origin, hash, header, justification, body, authorities); + *self.importing_block.write() = None; + telemetry!("block.import"; + "height" => height, + "best" => ?hash, + "origin" => ?origin + ); + result + } + + fn execute_and_import_block( + &self, + origin: BlockOrigin, + hash: Block::Hash, + header: Block::Header, + justification: bft::Justification, + body: Option>, + authorities: Vec, + ) -> error::Result { + let parent_hash = header.parent_hash().clone(); + match self.backend.blockchain().status(BlockId::Hash(hash))? { + blockchain::BlockStatus::InChain => return Ok(ImportResult::AlreadyInChain), + blockchain::BlockStatus::Unknown => {}, + } + + let mut transaction = self.backend.begin_operation(BlockId::Hash(parent_hash))?; + let (storage_update, storage_changes) = match transaction.state()? { + Some(transaction_state) => { + let mut overlay = Default::default(); + let mut r = self.executor.call_at_state( + transaction_state, + &mut overlay, + "execute_block", + &::new(header.clone(), body.clone().unwrap_or_default()).encode(), + match (origin, self.execution_strategy) { + (BlockOrigin::NetworkInitialSync, _) | (_, ExecutionStrategy::NativeWhenPossible) => + ExecutionManager::NativeWhenPossible, + (_, ExecutionStrategy::AlwaysWasm) => ExecutionManager::AlwaysWasm, + _ => ExecutionManager::Both(|wasm_result, native_result| { + warn!("Consensus error between wasm and native block execution at block {}", hash); + warn!(" Header {:?}", header); + warn!(" Native result {:?}", native_result); + warn!(" Wasm result {:?}", wasm_result); + telemetry!("block.execute.consensus_failure"; + "hash" => ?hash, + "origin" => ?origin, + "header" => ?header + ); + wasm_result + }), + }, + ); + let (_, storage_update) = r?; + overlay.commit_prospective(); + (Some(storage_update), Some(overlay.into_committed())) + }, + None => (None, None) + }; + + let is_new_best = header.number() == &(self.backend.blockchain().info()?.best_number + One::one()); + trace!("Imported {}, (#{}), best={}, origin={:?}", hash, header.number(), is_new_best, origin); + let unchecked: bft::UncheckedJustification<_> = justification.uncheck().into(); + transaction.set_block_data(header.clone(), body, Some(unchecked.into()), is_new_best)?; + transaction.update_authorities(authorities); + if let Some(storage_update) = storage_update { + transaction.update_storage(storage_update)?; + } + self.backend.commit_operation(transaction)?; + + if origin == BlockOrigin::NetworkBroadcast || origin == BlockOrigin::Own || origin == BlockOrigin::ConsensusBroadcast { + + if let Some(storage_changes) = storage_changes { + // TODO [ToDr] How to handle re-orgs? Should we re-emit all storage changes? + self.storage_notifications.lock() + .trigger(&hash, storage_changes); + } + + let notification = BlockImportNotification:: { + hash: hash, + origin: origin, + header: header, + is_new_best: is_new_best, + }; + self.import_notification_sinks.lock() + .retain(|sink| sink.unbounded_send(notification.clone()).is_ok()); + } + Ok(ImportResult::Queued) + } + + /// Attempts to revert the chain by `n` blocks. Returns the number of blocks that were + /// successfully reverted. + pub fn revert(&self, n: NumberFor) -> error::Result> { + Ok(self.backend.revert(n)?) + } + + /// Get blockchain info. + pub fn info(&self) -> error::Result> { + let info = self.backend.blockchain().info().map_err(|e| error::Error::from_blockchain(Box::new(e)))?; + Ok(ClientInfo { + chain: info, + best_queued_hash: None, + best_queued_number: None, + }) + } + + /// Get block status. + pub fn block_status(&self, id: &BlockId) -> error::Result { + // TODO: more efficient implementation + if let BlockId::Hash(ref h) = id { + if self.importing_block.read().as_ref().map_or(false, |importing| h == importing) { + return Ok(BlockStatus::Queued); + } + } + match self.backend.blockchain().header(*id).map_err(|e| error::Error::from_blockchain(Box::new(e)))?.is_some() { + true => Ok(BlockStatus::InChain), + false => Ok(BlockStatus::Unknown), + } + } + + /// Get block hash by number. + pub fn block_hash(&self, block_number: <::Header as HeaderT>::Number) -> error::Result> { + self.backend.blockchain().hash(block_number) + } + + /// Convert an arbitrary block ID into a block hash. + pub fn block_hash_from_id(&self, id: &BlockId) -> error::Result> { + match *id { + BlockId::Hash(h) => Ok(Some(h)), + BlockId::Number(n) => self.block_hash(n), + } + } + + /// Convert an arbitrary block ID into a block hash. + pub fn block_number_from_id(&self, id: &BlockId) -> error::Result::Header as HeaderT>::Number>> { + match *id { + BlockId::Hash(_) => Ok(self.header(id)?.map(|h| h.number().clone())), + BlockId::Number(n) => Ok(Some(n)), + } + } + + /// Get block header by id. + pub fn header(&self, id: &BlockId) -> error::Result::Header>> { + self.backend.blockchain().header(*id) + } + + /// Get block body by id. + pub fn body(&self, id: &BlockId) -> error::Result::Extrinsic>>> { + self.backend.blockchain().body(*id) + } + + /// Get block justification set by id. + pub fn justification(&self, id: &BlockId) -> error::Result>> { + self.backend.blockchain().justification(*id) + } + + /// Get full block by id. + pub fn block(&self, id: &BlockId) -> error::Result>> { + Ok(match (self.header(id)?, self.body(id)?, self.justification(id)?) { + (Some(header), Some(extrinsics), Some(justification)) => + Some(SignedBlock { block: RuntimeBlock { header, extrinsics }, justification }), + _ => None, + }) + } + + /// Get best block header. + pub fn best_block_header(&self) -> error::Result<::Header> { + let info = self.backend.blockchain().info().map_err(|e| error::Error::from_blockchain(Box::new(e)))?; + Ok(self.header(&BlockId::Hash(info.best_hash))?.expect("Best block header must always exist")) + } +} + +impl bft::BlockImport for Client + where + B: backend::Backend, + E: CallExecutor, + Block: BlockT, +{ + fn import_block( + &self, + block: Block, + justification: ::bft::Justification, + authorities: &[AuthorityId] + ) -> bool { + let (header, extrinsics) = block.deconstruct(); + let justified_header = JustifiedHeader { + header: header, + justification, + authorities: authorities.to_vec(), + }; + + self.import_block(BlockOrigin::ConsensusBroadcast, justified_header, Some(extrinsics)).is_ok() + } +} + +impl bft::Authorities for Client + where + B: backend::Backend, + E: CallExecutor, + Block: BlockT, +{ + fn authorities(&self, at: &BlockId) -> Result, bft::Error> { + let on_chain_version: Result<_, bft::Error> = self.runtime_version_at(at) + .map_err(|e| { trace!("Error getting runtime version {:?}", e); bft::ErrorKind::RuntimeVersionMissing.into() }); + let on_chain_version = on_chain_version?; + let native_version: Result<_, bft::Error> = self.executor.native_runtime_version() + .ok_or_else(|| bft::ErrorKind::NativeRuntimeMissing.into()); + let native_version = native_version?; + if !on_chain_version.can_author_with(&native_version) { + return Err(bft::ErrorKind::IncompatibleAuthoringRuntime(on_chain_version, native_version).into()) + } + self.authorities_at(at).map_err(|_| { + let descriptor = format!("{:?}", at); + bft::ErrorKind::StateUnavailable(descriptor).into() + }) + } +} + +impl BlockchainEvents for Client +where + E: CallExecutor, + Block: BlockT, +{ + /// Get block import event stream. + fn import_notification_stream(&self) -> BlockchainEventStream { + let (sink, stream) = mpsc::unbounded(); + self.import_notification_sinks.lock().push(sink); + stream + } + + /// Get storage changes event stream. + fn storage_changes_notification_stream(&self, filter_keys: Option<&[StorageKey]>) -> error::Result> { + Ok(self.storage_notifications.lock().listen(filter_keys)) + } +} + +impl ChainHead for Client +where + B: backend::Backend, + E: CallExecutor, + Block: BlockT, +{ + fn best_block_header(&self) -> error::Result<::Header> { + Client::best_block_header(self) + } +} + +impl BlockBody for Client + where + B: backend::Backend, + E: CallExecutor, + Block: BlockT, +{ + fn block_body(&self, id: &BlockId) -> error::Result::Extrinsic>>> { + self.body(id) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use keyring::Keyring; + use test_client::{self, TestClient}; + use test_client::client::BlockOrigin; + use test_client::client::backend::Backend as TestBackend; + use test_client::{runtime as test_runtime, BlockBuilderExt}; + use test_client::runtime::Transfer; + + #[test] + fn client_initialises_from_genesis_ok() { + let client = test_client::new(); + + assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Alice.to_raw_public().into())).unwrap(), 1000); + assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Ferdie.to_raw_public().into())).unwrap(), 0); + } + + #[test] + fn authorities_call_works() { + let client = test_client::new(); + + assert_eq!(client.info().unwrap().chain.best_number, 0); + assert_eq!(client.authorities_at(&BlockId::Number(0)).unwrap(), vec![ + Keyring::Alice.to_raw_public().into(), + Keyring::Bob.to_raw_public().into(), + Keyring::Charlie.to_raw_public().into() + ]); + } + + #[test] + fn block_builder_works_with_no_transactions() { + let client = test_client::new(); + + let builder = client.new_block().unwrap(); + + client.justify_and_import(BlockOrigin::Own, builder.bake().unwrap()).unwrap(); + + assert_eq!(client.info().unwrap().chain.best_number, 1); + } + + #[test] + fn block_builder_works_with_transactions() { + let client = test_client::new(); + + let mut builder = client.new_block().unwrap(); + + builder.push_transfer(Transfer { + from: Keyring::Alice.to_raw_public().into(), + to: Keyring::Ferdie.to_raw_public().into(), + amount: 42, + nonce: 0, + }).unwrap(); + + client.justify_and_import(BlockOrigin::Own, builder.bake().unwrap()).unwrap(); + + assert_eq!(client.info().unwrap().chain.best_number, 1); + assert!(client.state_at(&BlockId::Number(1)).unwrap() != client.state_at(&BlockId::Number(0)).unwrap()); + assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Alice.to_raw_public().into())).unwrap(), 958); + assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Ferdie.to_raw_public().into())).unwrap(), 42); + } + + #[test] + fn client_uses_authorities_from_blockchain_cache() { + let client = test_client::new(); + test_client::client::in_mem::cache_authorities_at( + client.backend().blockchain(), + Default::default(), + Some(vec![[1u8; 32].into()])); + assert_eq!(client.authorities_at( + &BlockId::Hash(Default::default())).unwrap(), + vec![[1u8; 32].into()]); + } + + #[test] + fn block_builder_does_not_include_invalid() { + let client = test_client::new(); + + let mut builder = client.new_block().unwrap(); + + builder.push_transfer(Transfer { + from: Keyring::Alice.to_raw_public().into(), + to: Keyring::Ferdie.to_raw_public().into(), + amount: 42, + nonce: 0, + }).unwrap(); + + assert!(builder.push_transfer(Transfer { + from: Keyring::Eve.to_raw_public().into(), + to: Keyring::Alice.to_raw_public().into(), + amount: 42, + nonce: 0, + }).is_err()); + + client.justify_and_import(BlockOrigin::Own, builder.bake().unwrap()).unwrap(); + + assert_eq!(client.info().unwrap().chain.best_number, 1); + assert!(client.state_at(&BlockId::Number(1)).unwrap() != client.state_at(&BlockId::Number(0)).unwrap()); + assert_eq!(client.body(&BlockId::Number(1)).unwrap().unwrap().len(), 1) + } + + #[test] + fn json_metadata() { + let client = test_client::new(); + + let mut builder = client.new_block().unwrap(); + + builder.push_transfer(Transfer { + from: Keyring::Alice.to_raw_public().into(), + to: Keyring::Ferdie.to_raw_public().into(), + amount: 42, + nonce: 0, + }).unwrap(); + + assert!(builder.push_transfer(Transfer { + from: Keyring::Eve.to_raw_public().into(), + to: Keyring::Alice.to_raw_public().into(), + amount: 42, + nonce: 0, + }).is_err()); + + client.justify_and_import(BlockOrigin::Own, builder.bake().unwrap()).unwrap(); + + assert_eq!( + client.json_metadata(&BlockId::Number(1)).unwrap(), + r#"{ "events": { "name": "Test", "events": { "event": hallo } } }"# + ); + } +} diff --git a/core/client/src/error.rs b/core/client/src/error.rs new file mode 100644 index 0000000000000..b1eb27385ba44 --- /dev/null +++ b/core/client/src/error.rs @@ -0,0 +1,154 @@ +// Copyright 2017 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 . + +//! Polkadot client possible errors. + +use std; +use state_machine; +use runtime_primitives::ApplyError; + +error_chain! { + errors { + /// Backend error. + Backend(s: String) { + description("Unrecoverable backend error"), + display("Backend error: {}", s), + } + + /// Unknown block. + UnknownBlock(h: String) { + description("unknown block"), + display("UnknownBlock: {}", &*h), + } + + /// Applying extrinsic error. + ApplyExtinsicFailed(e: ApplyError) { + description("Extrinsic error"), + display("Extrinsic error: {:?}", e), + } + + /// Execution error. + Execution(e: Box) { + description("execution error"), + display("Execution: {}", e), + } + + /// Blockchain error. + Blockchain(e: Box) { + description("Blockchain error"), + display("Blockchain: {}", e), + } + + /// Invalid state data. + AuthLenEmpty { + description("authority count state error"), + display("Current state of blockchain has no authority count value"), + } + + /// Invalid state data. + AuthEmpty(i: u32) { + description("authority value state error"), + display("Current state of blockchain has no authority value for index {}", i), + } + + /// Invalid state data. + AuthLenInvalid { + description("authority count state error"), + display("Current state of blockchain has invalid authority count value"), + } + + /// Cound not get runtime version. + VersionInvalid { + description("Runtime version error"), + display("On-chain runtime does not specify version"), + } + + /// Invalid state data. + AuthInvalid(i: u32) { + description("authority value state error"), + display("Current state of blockchain has invalid authority value for index {}", i), + } + + /// Bad justification for header. + BadJustification(h: String) { + description("bad justification for header"), + display("bad justification for header: {}", &*h), + } + + /// Not available on light client. + NotAvailableOnLightClient { + description("not available on light client"), + display("This method is not currently available when running in light client mode"), + } + + /// Invalid remote header proof. + InvalidHeaderProof { + description("invalid header proof"), + display("Remote node has responded with invalid header proof"), + } + + /// Invalid remote execution proof. + InvalidExecutionProof { + description("invalid execution proof"), + display("Remote node has responded with invalid execution proof"), + } + + /// Remote fetch has been cancelled. + RemoteFetchCancelled { + description("remote fetch cancelled"), + display("Remote data fetch has been cancelled"), + } + + /// Remote fetch has been failed. + RemoteFetchFailed { + description("remote fetch failed"), + display("Remote data fetch has been failed"), + } + + /// Error decoding call result. + CallResultDecode(method: &'static str) { + description("Error decoding call result") + display("Error decoding call result of {}", method) + } + } +} + +// TODO [ToDr] Temporary, state_machine::Error should be a regular error not Box. +impl From> for Error { + fn from(e: Box) -> Self { + ErrorKind::Execution(e).into() + } +} + +impl From for Error { + fn from(e: state_machine::backend::Void) -> Self { + match e {} + } +} + +impl Error { + /// Chain a blockchain error. + pub fn from_blockchain(e: Box) -> Self { + ErrorKind::Blockchain(e).into() + } + + /// Chain a state error. + pub fn from_state(e: Box) -> Self { + ErrorKind::Execution(e).into() + } +} + +impl state_machine::Error for Error {} diff --git a/core/client/src/genesis.rs b/core/client/src/genesis.rs new file mode 100644 index 0000000000000..9c7810aedca3d --- /dev/null +++ b/core/client/src/genesis.rs @@ -0,0 +1,202 @@ +// Copyright 2017 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 . + +//! Tool for creating the genesis block. + +use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, Hash as HashT, Zero}; +use runtime_primitives::StorageMap; + +/// Create a genesis block, given the initial storage. +pub fn construct_genesis_block< + Block: BlockT +> ( + storage: &StorageMap +) -> Block { + let state_root = <<::Header as HeaderT>::Hashing as HashT>::trie_root(storage.clone().into_iter()); + let extrinsics_root = <<::Header as HeaderT>::Hashing as HashT>::trie_root(::std::iter::empty::<(&[u8], &[u8])>()); + Block::new( + <::Header as HeaderT>::new( + Zero::zero(), + extrinsics_root, + state_root, + Default::default(), + Default::default() + ), + Default::default() + ) +} + +#[cfg(test)] +mod tests { + use super::*; + use codec::{Encode, Decode, Joiner}; + use keyring::Keyring; + use executor::NativeExecutionDispatch; + use state_machine::{execute, OverlayedChanges, ExecutionStrategy}; + use state_machine::backend::InMemory; + use test_client; + use test_client::runtime::genesismap::{GenesisConfig, additional_storage_with_genesis}; + use test_client::runtime::{Hash, Transfer, Block, BlockNumber, Header, Digest, Extrinsic}; + use primitives::{Blake2Hasher, RlpCodec, ed25519::{Public, Pair}}; + + native_executor_instance!(Executor, test_client::runtime::api::dispatch, test_client::runtime::VERSION, include_bytes!("../../test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm")); + + fn executor() -> ::executor::NativeExecutor { + NativeExecutionDispatch::new() + } + + fn construct_block(backend: &InMemory, number: BlockNumber, parent_hash: Hash, state_root: Hash, txs: Vec) -> (Vec, Hash) { + use triehash::ordered_trie_root; + + let transactions = txs.into_iter().map(|tx| { + let signature = Pair::from(Keyring::from_public(Public::from_raw(tx.from.0)).unwrap()) + .sign(&tx.encode()).into(); + + Extrinsic { transfer: tx, signature } + }).collect::>(); + + let extrinsics_root = ordered_trie_root::(transactions.iter().map(Encode::encode)).into(); + + println!("root before: {:?}", extrinsics_root); + let mut header = Header { + parent_hash, + number, + state_root, + extrinsics_root, + digest: Digest { logs: vec![], }, + }; + let hash = header.hash(); + let mut overlay = OverlayedChanges::default(); + + execute( + backend, + &mut overlay, + &executor(), + "initialise_block", + &header.encode(), + ExecutionStrategy::NativeWhenPossible, + ).unwrap(); + + for tx in transactions.iter() { + execute( + backend, + &mut overlay, + &executor(), + "apply_extrinsic", + &tx.encode(), + ExecutionStrategy::NativeWhenPossible, + ).unwrap(); + } + + let (ret_data, _) = execute( + backend, + &mut overlay, + &executor(), + "finalise_block", + &[], + ExecutionStrategy::NativeWhenPossible, + ).unwrap(); + header = Header::decode(&mut &ret_data[..]).unwrap(); + println!("root after: {:?}", header.extrinsics_root); + + (vec![].and(&Block { header, extrinsics: transactions }), hash) + } + + fn block1(genesis_hash: Hash, backend: &InMemory) -> (Vec, Hash) { + construct_block( + backend, + 1, + genesis_hash, + hex!("25e5b37074063ab75c889326246640729b40d0c86932edc527bc80db0e04fe5c").into(), + vec![Transfer { + from: Keyring::One.to_raw_public().into(), + to: Keyring::Two.to_raw_public().into(), + amount: 69, + nonce: 0, + }] + ) + } + + #[test] + fn construct_genesis_should_work_with_native() { + let mut storage = GenesisConfig::new_simple( + vec![Keyring::One.to_raw_public().into(), Keyring::Two.to_raw_public().into()], 1000 + ).genesis_map(); + let block = construct_genesis_block::(&storage); + let genesis_hash = block.header.hash(); + storage.extend(additional_storage_with_genesis(&block).into_iter()); + + let backend = InMemory::from(storage); + let (b1data, _b1hash) = block1(genesis_hash, &backend); + + let mut overlay = OverlayedChanges::default(); + let _ = execute( + &backend, + &mut overlay, + &executor(), + "execute_block", + &b1data, + ExecutionStrategy::NativeWhenPossible, + ).unwrap(); + } + + #[test] + fn construct_genesis_should_work_with_wasm() { + let mut storage = GenesisConfig::new_simple( + vec![Keyring::One.to_raw_public().into(), Keyring::Two.to_raw_public().into()], 1000 + ).genesis_map(); + let block = construct_genesis_block::(&storage); + let genesis_hash = block.header.hash(); + storage.extend(additional_storage_with_genesis(&block).into_iter()); + + let backend = InMemory::from(storage); + let (b1data, _b1hash) = block1(genesis_hash, &backend); + + let mut overlay = OverlayedChanges::default(); + let _ = execute( + &backend, + &mut overlay, + &executor(), + "execute_block", + &b1data, + ExecutionStrategy::AlwaysWasm, + ).unwrap(); + } + + #[test] + #[should_panic] + fn construct_genesis_with_bad_transaction_should_panic() { + let mut storage = GenesisConfig::new_simple( + vec![Keyring::One.to_raw_public().into(), Keyring::Two.to_raw_public().into()], 68 + ).genesis_map(); + let block = construct_genesis_block::(&storage); + let genesis_hash = block.header.hash(); + storage.extend(additional_storage_with_genesis(&block).into_iter()); + + let backend = InMemory::from(storage); + let (b1data, _b1hash) = block1(genesis_hash, &backend); + + let mut overlay = OverlayedChanges::default(); + let _ = execute( + &backend, + &mut overlay, + &Executor::new(), + "execute_block", + &b1data, + ExecutionStrategy::NativeWhenPossible, + ).unwrap(); + } +} diff --git a/core/client/src/in_mem.rs b/core/client/src/in_mem.rs new file mode 100644 index 0000000000000..74a1fdbabe52c --- /dev/null +++ b/core/client/src/in_mem.rs @@ -0,0 +1,439 @@ +// Copyright 2017 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 . + +//! In memory client backend + +use std::collections::HashMap; +use std::sync::Arc; +use parking_lot::RwLock; +use error; +use backend; +use light; +use primitives::AuthorityId; +use runtime_primitives::generic::BlockId; +use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, Zero, NumberFor, As}; +use runtime_primitives::bft::Justification; +use blockchain::{self, BlockStatus}; +use state_machine::backend::{Backend as StateBackend, InMemory}; +use patricia_trie::NodeCodec; +use hashdb::Hasher; +use heapsize::HeapSizeOf; + +struct PendingBlock { + block: StoredBlock, + is_best: bool, +} + +#[derive(PartialEq, Eq, Clone)] +enum StoredBlock { + Header(B::Header, Option>), + Full(B, Option>), +} + +impl StoredBlock { + fn new(header: B::Header, body: Option>, just: Option>) -> Self { + match body { + Some(body) => StoredBlock::Full(B::new(header, body), just), + None => StoredBlock::Header(header, just), + } + } + + fn header(&self) -> &B::Header { + match *self { + StoredBlock::Header(ref h, _) => h, + StoredBlock::Full(ref b, _) => b.header(), + } + } + + fn justification(&self) -> Option<&Justification> { + match *self { + StoredBlock::Header(_, ref j) | StoredBlock::Full(_, ref j) => j.as_ref() + } + } + + fn extrinsics(&self) -> Option<&[B::Extrinsic]> { + match *self { + StoredBlock::Header(_, _) => None, + StoredBlock::Full(ref b, _) => Some(b.extrinsics()) + } + } + + fn into_inner(self) -> (B::Header, Option>, Option>) { + match self { + StoredBlock::Header(header, just) => (header, None, just), + StoredBlock::Full(block, just) => { + let (header, body) = block.deconstruct(); + (header, Some(body), just) + } + } + } +} + +#[derive(Clone)] +struct BlockchainStorage { + blocks: HashMap>, + hashes: HashMap<<::Header as HeaderT>::Number, Block::Hash>, + best_hash: Block::Hash, + best_number: <::Header as HeaderT>::Number, + genesis_hash: Block::Hash, + cht_roots: HashMap, Block::Hash>, +} + +/// In-memory blockchain. Supports concurrent reads. +pub struct Blockchain { + storage: Arc>>, + cache: Cache, +} + +struct Cache { + storage: Arc>>, + authorities_at: RwLock>>>, +} + +impl Clone for Blockchain { + fn clone(&self) -> Self { + let storage = Arc::new(RwLock::new(self.storage.read().clone())); + Blockchain { + storage: storage.clone(), + cache: Cache { + storage, + authorities_at: RwLock::new(self.cache.authorities_at.read().clone()), + }, + } + } +} + +impl Blockchain { + /// Get header hash of given block. + pub fn id(&self, id: BlockId) -> Option { + match id { + BlockId::Hash(h) => Some(h), + BlockId::Number(n) => self.storage.read().hashes.get(&n).cloned(), + } + } + + /// Create new in-memory blockchain storage. + pub fn new() -> Blockchain { + let storage = Arc::new(RwLock::new( + BlockchainStorage { + blocks: HashMap::new(), + hashes: HashMap::new(), + best_hash: Default::default(), + best_number: Zero::zero(), + genesis_hash: Default::default(), + cht_roots: HashMap::new(), + })); + Blockchain { + storage: storage.clone(), + cache: Cache { + storage: storage, + authorities_at: Default::default(), + }, + } + } + + /// Insert a block header and associated data. + pub fn insert( + &self, + hash: Block::Hash, + header: ::Header, + justification: Option>, + body: Option::Extrinsic>>, + is_new_best: bool + ) { + let number = header.number().clone(); + let mut storage = self.storage.write(); + storage.blocks.insert(hash.clone(), StoredBlock::new(header, body, justification)); + storage.hashes.insert(number, hash.clone()); + if is_new_best { + storage.best_hash = hash.clone(); + storage.best_number = number.clone(); + } + if number == Zero::zero() { + storage.genesis_hash = hash; + } + } + + /// Compare this blockchain with another in-mem blockchain + pub fn equals_to(&self, other: &Self) -> bool { + self.canon_equals_to(other) && self.storage.read().blocks == other.storage.read().blocks + } + + /// Compare canonical chain to other canonical chain. + pub fn canon_equals_to(&self, other: &Self) -> bool { + let this = self.storage.read(); + let other = other.storage.read(); + this.hashes == other.hashes + && this.best_hash == other.best_hash + && this.best_number == other.best_number + && this.genesis_hash == other.genesis_hash + } + + /// Insert CHT root. + pub fn insert_cht_root(&self, block: NumberFor, cht_root: Block::Hash) { + self.storage.write().cht_roots.insert(block, cht_root); + } +} + +impl blockchain::HeaderBackend for Blockchain { + fn header(&self, id: BlockId) -> error::Result::Header>> { + Ok(self.id(id).and_then(|hash| { + self.storage.read().blocks.get(&hash).map(|b| b.header().clone()) + })) + } + + fn info(&self) -> error::Result> { + let storage = self.storage.read(); + Ok(blockchain::Info { + best_hash: storage.best_hash, + best_number: storage.best_number, + genesis_hash: storage.genesis_hash, + }) + } + + fn status(&self, id: BlockId) -> error::Result { + match self.id(id).map_or(false, |hash| self.storage.read().blocks.contains_key(&hash)) { + true => Ok(BlockStatus::InChain), + false => Ok(BlockStatus::Unknown), + } + } + + fn number(&self, hash: Block::Hash) -> error::Result>> { + Ok(self.storage.read().blocks.get(&hash).map(|b| *b.header().number())) + } + + fn hash(&self, number: <::Header as HeaderT>::Number) -> error::Result> { + Ok(self.id(BlockId::Number(number))) + } +} + + +impl blockchain::Backend for Blockchain { + fn body(&self, id: BlockId) -> error::Result::Extrinsic>>> { + Ok(self.id(id).and_then(|hash| { + self.storage.read().blocks.get(&hash) + .and_then(|b| b.extrinsics().map(|x| x.to_vec())) + })) + } + + fn justification(&self, id: BlockId) -> error::Result>> { + Ok(self.id(id).and_then(|hash| self.storage.read().blocks.get(&hash).and_then(|b| + b.justification().map(|x| x.clone())) + )) + } + + fn cache(&self) -> Option<&blockchain::Cache> { + Some(&self.cache) + } +} + +impl light::blockchain::Storage for Blockchain + where + Block::Hash: From<[u8; 32]>, +{ + fn import_header( + &self, + is_new_best: bool, + header: Block::Header, + authorities: Option> + ) -> error::Result<()> { + let hash = header.hash(); + let parent_hash = *header.parent_hash(); + self.insert(hash, header, None, None, is_new_best); + if is_new_best { + self.cache.insert(parent_hash, authorities); + } + Ok(()) + } + + fn cht_root(&self, _cht_size: u64, block: NumberFor) -> error::Result { + self.storage.read().cht_roots.get(&block).cloned() + .ok_or_else(|| error::ErrorKind::Backend(format!("CHT for block {} not exists", block)).into()) + } + + fn cache(&self) -> Option<&blockchain::Cache> { + Some(&self.cache) + } +} + +/// In-memory operation. +pub struct BlockImportOperation> { + pending_block: Option>, + pending_authorities: Option>, + old_state: InMemory, + new_state: Option>, +} + +impl backend::BlockImportOperation for BlockImportOperation +where + Block: BlockT, + H: Hasher, + C: NodeCodec, + H::Out: HeapSizeOf, +{ + type State = InMemory; + + fn state(&self) -> error::Result> { + Ok(Some(&self.old_state)) + } + + fn set_block_data( + &mut self, + header: ::Header, + body: Option::Extrinsic>>, + justification: Option>, + is_new_best: bool + ) -> error::Result<()> { + assert!(self.pending_block.is_none(), "Only one block per operation is allowed"); + self.pending_block = Some(PendingBlock { + block: StoredBlock::new(header, body, justification), + is_best: is_new_best, + }); + Ok(()) + } + + fn update_authorities(&mut self, authorities: Vec) { + self.pending_authorities = Some(authorities); + } + + fn update_storage(&mut self, update: as StateBackend>::Transaction) -> error::Result<()> { + self.new_state = Some(self.old_state.update(update)); + Ok(()) + } + + fn reset_storage, Vec)>>(&mut self, iter: I) -> error::Result<()> { + self.new_state = Some(InMemory::from(iter.collect::>())); + Ok(()) + } +} + +/// In-memory backend. Keeps all states and blocks in memory. Useful for testing. +pub struct Backend +where + Block: BlockT, + H: Hasher, + C: NodeCodec +{ + states: RwLock>>, + blockchain: Blockchain, +} + +impl Backend +where + Block: BlockT, + H: Hasher, + C: NodeCodec +{ + /// Create a new instance of in-mem backend. + pub fn new() -> Backend { + Backend { + states: RwLock::new(HashMap::new()), + blockchain: Blockchain::new(), + } + } +} + +impl backend::Backend for Backend +where + Block: BlockT, + H: Hasher, + H::Out: HeapSizeOf, + C: NodeCodec + Send + Sync, +{ + type BlockImportOperation = BlockImportOperation; + type Blockchain = Blockchain; + type State = InMemory; + + fn begin_operation(&self, block: BlockId) -> error::Result { + let state = match block { + BlockId::Hash(ref h) if h.clone() == Default::default() => Self::State::default(), + _ => self.state_at(block)?, + }; + + Ok(BlockImportOperation { + pending_block: None, + pending_authorities: None, + old_state: state, + new_state: None, + }) + } + + fn commit_operation(&self, operation: Self::BlockImportOperation) -> error::Result<()> { + if let Some(pending_block) = operation.pending_block { + let old_state = &operation.old_state; + let (header, body, justification) = pending_block.block.into_inner(); + let hash = header.hash(); + let parent_hash = *header.parent_hash(); + + self.states.write().insert(hash, operation.new_state.unwrap_or_else(|| old_state.clone())); + self.blockchain.insert(hash, header, justification, body, pending_block.is_best); + // dumb implementation - store value for each block + if pending_block.is_best { + self.blockchain.cache.insert(parent_hash, operation.pending_authorities); + } + } + Ok(()) + } + + fn blockchain(&self) -> &Self::Blockchain { + &self.blockchain + } + + fn state_at(&self, block: BlockId) -> error::Result { + match self.blockchain.id(block).and_then(|id| self.states.read().get(&id).cloned()) { + Some(state) => Ok(state), + None => Err(error::ErrorKind::UnknownBlock(format!("{}", block)).into()), + } + } + + fn revert(&self, _n: NumberFor) -> error::Result> { + Ok(As::sa(0)) + } +} + +impl backend::LocalBackend for Backend +where + Block: BlockT, + H: Hasher, + H::Out: HeapSizeOf, + C: NodeCodec + Send + Sync, +{} + +impl Cache { + fn insert(&self, at: Block::Hash, authorities: Option>) { + self.authorities_at.write().insert(at, authorities); + } +} + +impl blockchain::Cache for Cache { + fn authorities_at(&self, block: BlockId) -> Option> { + let hash = match block { + BlockId::Hash(hash) => hash, + BlockId::Number(number) => self.storage.read().hashes.get(&number).cloned()?, + }; + + self.authorities_at.read().get(&hash).cloned().unwrap_or(None) + } +} + +/// Insert authorities entry into in-memory blockchain cache. Extracted as a separate function to use it in tests. +pub fn cache_authorities_at( + blockchain: &Blockchain, + at: Block::Hash, + authorities: Option> +) { + blockchain.cache.insert(at, authorities); +} diff --git a/core/client/src/lib.rs b/core/client/src/lib.rs new file mode 100644 index 0000000000000..fd30eaae72e32 --- /dev/null +++ b/core/client/src/lib.rs @@ -0,0 +1,69 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Substrate Client and associated logic. + +#![warn(missing_docs)] +#![recursion_limit="128"] + +extern crate substrate_bft as bft; +extern crate substrate_codec as codec; +extern crate substrate_primitives as primitives; +extern crate substrate_runtime_io as runtime_io; +extern crate substrate_runtime_support as runtime_support; +extern crate substrate_runtime_primitives as runtime_primitives; +extern crate substrate_state_machine as state_machine; +#[cfg(test)] extern crate substrate_keyring as keyring; +#[cfg(test)] extern crate substrate_test_client as test_client; +#[macro_use] extern crate substrate_telemetry; +#[macro_use] extern crate slog; // needed until we can reexport `slog_info` from `substrate_telemetry` + +extern crate fnv; +extern crate futures; +extern crate parking_lot; +extern crate triehash; +extern crate patricia_trie; +extern crate hashdb; +extern crate rlp; +extern crate heapsize; + +#[macro_use] extern crate error_chain; +#[macro_use] extern crate log; +#[cfg_attr(test, macro_use)] extern crate substrate_executor as executor; +#[cfg(test)] #[macro_use] extern crate hex_literal; + +pub mod error; +pub mod blockchain; +pub mod backend; +pub mod cht; +pub mod in_mem; +pub mod genesis; +pub mod block_builder; +pub mod light; +mod call_executor; +mod client; +mod notifications; + +pub use blockchain::Info as ChainInfo; +pub use call_executor::{CallResult, CallExecutor, LocalCallExecutor}; +pub use client::{ + new_in_mem, + BlockBody, BlockStatus, BlockOrigin, BlockchainEventStream, BlockchainEvents, + Client, ClientInfo, ChainHead, + ImportResult, JustifiedHeader, +}; +pub use notifications::{StorageEventStream, StorageChangeSet}; +pub use state_machine::ExecutionStrategy; diff --git a/core/client/src/light/backend.rs b/core/client/src/light/backend.rs new file mode 100644 index 0000000000000..54291521bb0da --- /dev/null +++ b/core/client/src/light/backend.rs @@ -0,0 +1,229 @@ +// Copyright 2017 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 . + +//! Light client backend. Only stores headers and justifications of blocks. +//! Everything else is requested from full nodes on demand. + +use std::sync::{Arc, Weak}; +use futures::{Future, IntoFuture}; +use parking_lot::RwLock; + +use primitives::AuthorityId; +use runtime_primitives::{bft::Justification, generic::BlockId}; +use runtime_primitives::traits::{Block as BlockT, NumberFor}; +use state_machine::{ + Backend as StateBackend, + TrieBackend as StateTrieBackend, + TryIntoTrieBackend as TryIntoStateTrieBackend +}; + +use backend::{Backend as ClientBackend, BlockImportOperation, RemoteBackend}; +use blockchain::HeaderBackend as BlockchainHeaderBackend; +use error::{Error as ClientError, ErrorKind as ClientErrorKind, Result as ClientResult}; +use light::blockchain::{Blockchain, Storage as BlockchainStorage}; +use light::fetcher::{Fetcher, RemoteReadRequest}; +use patricia_trie::NodeCodec; +use hashdb::Hasher; + +/// Light client backend. +pub struct Backend { + blockchain: Arc>, +} + +/// Light block (header and justification) import operation. +pub struct ImportOperation { + is_new_best: bool, + header: Option, + authorities: Option>, + _phantom: ::std::marker::PhantomData<(S, F)>, +} + +/// On-demand state. +pub struct OnDemandState { + fetcher: Weak, + blockchain: Weak>, + block: Block::Hash, + cached_header: RwLock>, +} + +impl Backend { + /// Create new light backend. + pub fn new(blockchain: Arc>) -> Self { + Self { blockchain } + } + + /// Get shared blockchain reference. + pub fn blockchain(&self) -> &Arc> { + &self.blockchain + } +} + +impl ClientBackend for Backend where + Block: BlockT, + S: BlockchainStorage, + F: Fetcher, + H: Hasher, + C: NodeCodec, +{ + type BlockImportOperation = ImportOperation; + type Blockchain = Blockchain; + type State = OnDemandState; + + fn begin_operation(&self, _block: BlockId) -> ClientResult { + Ok(ImportOperation { + is_new_best: false, + header: None, + authorities: None, + _phantom: Default::default(), + }) + } + + fn commit_operation(&self, operation: Self::BlockImportOperation) -> ClientResult<()> { + let header = operation.header.expect("commit is called after set_block_data; set_block_data sets header; qed"); + self.blockchain.storage().import_header(operation.is_new_best, header, operation.authorities) + } + + fn blockchain(&self) -> &Blockchain { + &self.blockchain + } + + fn state_at(&self, block: BlockId) -> ClientResult { + let block_hash = match block { + BlockId::Hash(h) => Some(h), + BlockId::Number(n) => self.blockchain.hash(n).unwrap_or_default(), + }; + + Ok(OnDemandState { + fetcher: self.blockchain.fetcher(), + blockchain: Arc::downgrade(&self.blockchain), + block: block_hash.ok_or_else(|| ClientErrorKind::UnknownBlock(format!("{}", block)))?, + cached_header: RwLock::new(None), + }) + } + + fn revert(&self, _n: NumberFor) -> ClientResult> { + unimplemented!() + } +} + +impl RemoteBackend for Backend +where + Block: BlockT, + S: BlockchainStorage, + F: Fetcher, + H: Hasher, + C: NodeCodec, +{} + +impl BlockImportOperation for ImportOperation +where + Block: BlockT, + F: Fetcher, + S: BlockchainStorage, + H: Hasher, + C: NodeCodec, +{ + type State = OnDemandState; + + fn state(&self) -> ClientResult> { + // None means 'locally-stateless' backend + Ok(None) + } + + fn set_block_data( + &mut self, + header: Block::Header, + _body: Option>, + _justification: Option>, + is_new_best: bool + ) -> ClientResult<()> { + self.is_new_best = is_new_best; + self.header = Some(header); + Ok(()) + } + + fn update_authorities(&mut self, authorities: Vec) { + self.authorities = Some(authorities); + } + + fn update_storage(&mut self, _update: >::Transaction) -> ClientResult<()> { + // we're not storing anything locally => ignore changes + Ok(()) + } + + fn reset_storage, Vec)>>(&mut self, _iter: I) -> ClientResult<()> { + // we're not storing anything locally => ignore changes + Ok(()) + } +} + +impl StateBackend for OnDemandState + where + Block: BlockT, + S: BlockchainStorage, + F: Fetcher, + H: Hasher, + C: NodeCodec, +{ + type Error = ClientError; + type Transaction = (); + + fn storage(&self, key: &[u8]) -> ClientResult>> { + let mut header = self.cached_header.read().clone(); + if header.is_none() { + let cached_header = self.blockchain.upgrade() + .ok_or_else(|| ClientErrorKind::UnknownBlock(format!("{}", self.block)).into()) + .and_then(|blockchain| blockchain.expect_header(BlockId::Hash(self.block)))?; + header = Some(cached_header.clone()); + *self.cached_header.write() = Some(cached_header); + } + + self.fetcher.upgrade().ok_or(ClientErrorKind::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() + } + + fn for_keys_with_prefix(&self, _prefix: &[u8], _action: A) { + // whole state is not available on light node + } + + fn storage_root(&self, _delta: I) -> (H::Out, Self::Transaction) + where I: IntoIterator, Option>)> { + (H::Out::default(), ()) + } + + fn pairs(&self) -> Vec<(Vec, Vec)> { + // whole state is not available on light node + Vec::new() + } +} + +impl TryIntoStateTrieBackend for OnDemandState +where + Block: BlockT, + F: Fetcher, + H: Hasher, + C: NodeCodec, +{ + fn try_into_trie_backend(self) -> Option> { + None + } +} diff --git a/core/client/src/light/blockchain.rs b/core/client/src/light/blockchain.rs new file mode 100644 index 0000000000000..11f3070aa6d4d --- /dev/null +++ b/core/client/src/light/blockchain.rs @@ -0,0 +1,142 @@ +// Copyright 2017 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 . + +//! Light client blockchin backend. Only stores headers and justifications of recent +//! blocks. CHT roots are stored for headers of ancient blocks. + +use std::sync::Weak; +use futures::{Future, IntoFuture}; +use parking_lot::Mutex; + +use primitives::AuthorityId; +use runtime_primitives::{bft::Justification, generic::BlockId}; +use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero}; + +use blockchain::{Backend as BlockchainBackend, BlockStatus, Cache as BlockchainCache, + HeaderBackend as BlockchainHeaderBackend, Info as BlockchainInfo}; +use cht; +use error::{ErrorKind as ClientErrorKind, Result as ClientResult}; +use light::fetcher::{Fetcher, RemoteHeaderRequest}; + +/// Light client blockchain storage. +pub trait Storage: BlockchainHeaderBackend { + /// Store new header. + fn import_header( + &self, + is_new_best: bool, + header: Block::Header, + authorities: Option> + ) -> ClientResult<()>; + + /// Get CHT root for given block. Fails if the block is not pruned (not a part of any CHT). + fn cht_root(&self, cht_size: u64, block: NumberFor) -> ClientResult; + + /// Get storage cache. + fn cache(&self) -> Option<&BlockchainCache>; +} + +/// Light client blockchain. +pub struct Blockchain { + fetcher: Mutex>, + storage: S, +} + +impl Blockchain { + /// Create new light blockchain backed with given storage. + pub fn new(storage: S) -> Self { + Self { + fetcher: Mutex::new(Default::default()), + storage, + } + } + + /// Sets fetcher reference. + pub fn set_fetcher(&self, fetcher: Weak) { + *self.fetcher.lock() = fetcher; + } + + /// Get fetcher weak reference. + pub fn fetcher(&self) -> Weak { + self.fetcher.lock().clone() + } + + /// Get storage reference. + pub fn storage(&self) -> &S { + &self.storage + } +} + +impl BlockchainHeaderBackend for Blockchain where Block: BlockT, S: Storage, F: Fetcher { + fn header(&self, id: BlockId) -> ClientResult> { + match self.storage.header(id)? { + Some(header) => Ok(Some(header)), + None => { + let number = match id { + BlockId::Hash(hash) => match self.storage.number(hash)? { + Some(number) => number, + None => return Ok(None), + }, + BlockId::Number(number) => number, + }; + + // if the header is from future or genesis (we never prune genesis) => return + if number.is_zero() || self.storage.status(BlockId::Number(number))? != BlockStatus::InChain { + return Ok(None); + } + + self.fetcher().upgrade().ok_or(ClientErrorKind::NotAvailableOnLightClient)? + .remote_header(RemoteHeaderRequest { + cht_root: self.storage.cht_root(cht::SIZE, number)?, + block: number, + retry_count: None, + }) + .into_future().wait() + .map(Some) + } + } + } + + fn info(&self) -> ClientResult> { + self.storage.info() + } + + fn status(&self, id: BlockId) -> ClientResult { + self.storage.status(id) + } + + fn number(&self, hash: Block::Hash) -> ClientResult>> { + self.storage.number(hash) + } + + fn hash(&self, number: <::Header as HeaderT>::Number) -> ClientResult> { + self.storage.hash(number) + } +} + +impl BlockchainBackend for Blockchain where Block: BlockT, S: Storage, F: Fetcher { + fn body(&self, _id: BlockId) -> ClientResult>> { + // TODO [light]: fetch from remote node + Ok(None) + } + + fn justification(&self, _id: BlockId) -> ClientResult>> { + Ok(None) + } + + fn cache(&self) -> Option<&BlockchainCache> { + self.storage.cache() + } +} diff --git a/core/client/src/light/call_executor.rs b/core/client/src/light/call_executor.rs new file mode 100644 index 0000000000000..354a2dbe33e71 --- /dev/null +++ b/core/client/src/light/call_executor.rs @@ -0,0 +1,191 @@ +// Copyright 2017 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 . + +//! Light client call exector. Executes methods on remote full nodes, fetching +//! execution proof and checking it locally. + +use std::sync::Arc; +use futures::{IntoFuture, Future}; + +use runtime_primitives::generic::BlockId; +use runtime_primitives::traits::{Block as BlockT, Header as HeaderT}; +use state_machine::{Backend as StateBackend, CodeExecutor, OverlayedChanges, + execution_proof_check, ExecutionManager}; +use primitives::H256; +use patricia_trie::NodeCodec; +use hashdb::Hasher; +use rlp::Encodable; + +use blockchain::Backend as ChainBackend; +use call_executor::{CallExecutor, CallResult}; +use error::{Error as ClientError, ErrorKind as ClientErrorKind, Result as ClientResult}; +use light::fetcher::{Fetcher, RemoteCallRequest}; +use executor::RuntimeVersion; +use codec::Decode; +use heapsize::HeapSizeOf; +use std::marker::PhantomData; + +/// Call executor that executes methods on remote node, querying execution proof +/// and checking proof by re-executing locally. +pub struct RemoteCallExecutor { + blockchain: Arc, + fetcher: Arc, + _hasher: PhantomData, + _codec: PhantomData, +} + +impl Clone for RemoteCallExecutor { + fn clone(&self) -> Self { + RemoteCallExecutor { + blockchain: self.blockchain.clone(), + fetcher: self.fetcher.clone(), + _hasher: Default::default(), + _codec: Default::default(), + } + } +} + +impl RemoteCallExecutor { + /// Creates new instance of remote call executor. + pub fn new(blockchain: Arc, fetcher: Arc) -> Self { + RemoteCallExecutor { blockchain, fetcher, _hasher: PhantomData, _codec: PhantomData } + } +} + +impl CallExecutor for RemoteCallExecutor +where + Block: BlockT, + B: ChainBackend, + F: Fetcher, + H: Hasher, + H::Out: Ord + Encodable, + C: NodeCodec +{ + type Error = ClientError; + + fn call(&self, id: &BlockId, method: &str, call_data: &[u8]) -> ClientResult { + let block_hash = match *id { + BlockId::Hash(hash) => hash, + BlockId::Number(number) => self.blockchain.hash(number)? + .ok_or_else(|| ClientErrorKind::UnknownBlock(format!("{}", number)))?, + }; + let block_header = self.blockchain.expect_header(id.clone())?; + + 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 runtime_version(&self, id: &BlockId) -> ClientResult { + let call_result = self.call(id, "version", &[])?; + RuntimeVersion::decode(&mut call_result.return_data.as_slice()) + .ok_or_else(|| ClientErrorKind::VersionInvalid.into()) + } + + fn call_at_state< + S: StateBackend, + FF: FnOnce(Result, Self::Error>, Result, Self::Error>) -> Result, Self::Error> + >(&self, + _state: &S, + _changes: &mut OverlayedChanges, + _method: &str, + _call_data: &[u8], + _m: ExecutionManager + ) -> ClientResult<(Vec, S::Transaction)> { + Err(ClientErrorKind::NotAvailableOnLightClient.into()) + } + + fn prove_at_state>( + &self, + _state: S, + _changes: &mut OverlayedChanges, + _method: &str, + _call_data: &[u8] + ) -> ClientResult<(Vec, Vec>)> { + Err(ClientErrorKind::NotAvailableOnLightClient.into()) + } + + fn native_runtime_version(&self) -> Option { + None + } +} + +/// Check remote execution proof using given backend. +pub fn check_execution_proof( + executor: &E, + request: &RemoteCallRequest

, + remote_proof: Vec> +) -> ClientResult + where + Header: HeaderT, + E: CodeExecutor, + H: Hasher, + H::Out: Ord + Encodable + HeapSizeOf + From, + C: NodeCodec, +{ + let local_state_root = request.header.state_root(); + + let mut changes = OverlayedChanges::default(); + let (local_result, _) = execution_proof_check::( + H256::from_slice(local_state_root.as_ref()).into(), + remote_proof, + &mut changes, + executor, + &request.method, + &request.call_data)?; + + Ok(CallResult { return_data: local_result, changes }) +} + +#[cfg(test)] +mod tests { + use test_client; + use executor::NativeExecutionDispatch; + use super::*; + use primitives::RlpCodec; + + #[test] + fn execution_proof_is_generated_and_checked() { + // prepare remote client + let remote_client = test_client::new(); + let remote_block_id = BlockId::Number(0); + let remote_block_storage_root = remote_client.state_at(&remote_block_id) + .unwrap().storage_root(::std::iter::empty()).0; + + // 'fetch' execution proof from remote node + let remote_execution_proof = remote_client.execution_proof(&remote_block_id, "authorities", &[]).unwrap().1; + + // check remote execution proof locally + let local_executor = test_client::LocalExecutor::new(); + check_execution_proof::<_, _, _, RlpCodec>(&local_executor, &RemoteCallRequest { + block: test_client::runtime::Hash::default(), + header: test_client::runtime::Header { + state_root: remote_block_storage_root.into(), + parent_hash: Default::default(), + number: 0, + extrinsics_root: Default::default(), + digest: Default::default(), + }, + method: "authorities".into(), + call_data: vec![], + retry_count: None, + }, remote_execution_proof).unwrap(); + } +} diff --git a/core/client/src/light/fetcher.rs b/core/client/src/light/fetcher.rs new file mode 100644 index 0000000000000..02090a4bc6076 --- /dev/null +++ b/core/client/src/light/fetcher.rs @@ -0,0 +1,308 @@ +// Copyright 2017 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 . + +//! Light client data fetcher. Fetches requested data from remote full nodes. + +use futures::IntoFuture; + +use primitives::H256; +use hashdb::Hasher; +use patricia_trie::NodeCodec; +use rlp::Encodable; +use heapsize::HeapSizeOf; +use runtime_primitives::traits::{Block as BlockT, Header as HeaderT}; +use state_machine::{CodeExecutor, read_proof_check}; +use std::marker::PhantomData; + +use call_executor::CallResult; +use cht; +use error::{Error as ClientError, ErrorKind as ClientErrorKind, Result as ClientResult}; +use light::call_executor::check_execution_proof; + +/// Remote call request. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct RemoteCallRequest { + /// Call at state of given block. + pub block: Header::Hash, + /// Head of block at which call is perormed. + pub header: Header, + /// Method to call. + pub method: String, + /// Call data. + pub call_data: Vec, + /// Number of times to retry request. None means that default RETRY_COUNT is used. + pub retry_count: Option, +} + +/// Remote canonical header request. +#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] +pub struct RemoteHeaderRequest { + /// The root of CHT this block is included in. + pub cht_root: Header::Hash, + /// Number of the header to query. + pub block: Header::Number, + /// Number of times to retry request. None means that default RETRY_COUNT is used. + pub retry_count: Option, +} + +/// Remote storage read request. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct RemoteReadRequest { + /// Read at state of given block. + pub block: Header::Hash, + /// Head of block at which read is perormed. + pub header: Header, + /// Storage key to read. + pub key: Vec, + /// Number of times to retry request. None means that default RETRY_COUNT is used. + pub retry_count: Option, +} + +/// Light client data fetcher. Implementations of this trait must check if remote data +/// is correct (see FetchedDataChecker) and return already checked data. +pub trait Fetcher: Send + Sync { + /// Remote header future. + type RemoteHeaderResult: IntoFuture; + /// Remote storage read future. + type RemoteReadResult: IntoFuture>, Error=ClientError>; + /// Remote call result future. + type RemoteCallResult: IntoFuture; + + /// Fetch remote header. + fn remote_header(&self, request: RemoteHeaderRequest) -> Self::RemoteHeaderResult; + /// Fetch remote storage value. + fn remote_read(&self, request: RemoteReadRequest) -> Self::RemoteReadResult; + /// Fetch remote call result. + fn remote_call(&self, request: RemoteCallRequest) -> Self::RemoteCallResult; +} + +/// Light client remote data checker. +/// +/// Implementations of this trait should not use any blockchain data except that is +/// passed to its methods. +pub trait FetchChecker: Send + Sync { + /// Check remote header proof. + fn check_header_proof( + &self, + request: &RemoteHeaderRequest, + header: Option, + remote_proof: Vec> + ) -> ClientResult; + /// Check remote storage read proof. + fn check_read_proof( + &self, + request: &RemoteReadRequest, + remote_proof: Vec> + ) -> ClientResult>>; + /// Check remote method execution proof. + fn check_execution_proof( + &self, + request: &RemoteCallRequest, + remote_proof: Vec> + ) -> ClientResult; +} + +/// Remote data checker. +pub struct LightDataChecker { + executor: E, + _hasher: PhantomData, + _codec: PhantomData, +} + +impl LightDataChecker { + /// Create new light data checker. + pub fn new(executor: E) -> Self { + Self { + executor, _hasher: PhantomData, _codec: PhantomData + } + } +} + +impl FetchChecker for LightDataChecker + where + Block: BlockT, + Block::Hash: Into + From, + E: CodeExecutor, + H: Hasher, + C: NodeCodec + Sync + Send, + H::Out: Ord + Encodable + HeapSizeOf + From + From, +{ + fn check_header_proof( + &self, + request: &RemoteHeaderRequest, + remote_header: Option, + remote_proof: Vec> + ) -> ClientResult { + let remote_header = remote_header.ok_or_else(|| + ClientError::from(ClientErrorKind::InvalidHeaderProof))?; + let remote_header_hash = remote_header.hash(); + cht::check_proof::( + request.cht_root, + request.block, + remote_header_hash, + remote_proof) + .map(|_| remote_header) + } + + fn check_read_proof( + &self, + request: &RemoteReadRequest, + remote_proof: Vec> + ) -> ClientResult>> { + let local_state_root = request.header.state_root().clone(); + read_proof_check::(local_state_root.into(), remote_proof, &request.key).map_err(Into::into) + } + + fn check_execution_proof( + &self, + request: &RemoteCallRequest, + remote_proof: Vec> + ) -> ClientResult { + check_execution_proof::<_, _, H, C>(&self.executor, request, remote_proof) + } +} + +#[cfg(test)] +pub mod tests { + use futures::future::{ok, err, FutureResult}; + use parking_lot::Mutex; + use call_executor::CallResult; + use executor::{self, NativeExecutionDispatch}; + use error::Error as ClientError; + use test_client::{self, TestClient, runtime::{Hash, Block, Header}}; + use test_client::client::BlockOrigin; + use in_mem::{Blockchain as InMemoryBlockchain}; + use light::fetcher::{Fetcher, FetchChecker, LightDataChecker, + RemoteCallRequest, RemoteHeaderRequest}; + use primitives::{Blake2Hasher, RlpCodec}; + use runtime_primitives::generic::BlockId; + use state_machine::Backend; + use super::*; + + pub type OkCallFetcher = Mutex; + + impl Fetcher for OkCallFetcher { + type RemoteHeaderResult = FutureResult; + type RemoteReadResult = FutureResult>, ClientError>; + type RemoteCallResult = FutureResult; + + fn remote_header(&self, _request: RemoteHeaderRequest
) -> Self::RemoteHeaderResult { + err("Not implemented on test node".into()) + } + + fn remote_read(&self, _request: RemoteReadRequest
) -> Self::RemoteReadResult { + err("Not implemented on test node".into()) + } + + fn remote_call(&self, _request: RemoteCallRequest
) -> Self::RemoteCallResult { + ok((*self.lock()).clone()) + } + } + + fn prepare_for_read_proof_check() -> ( + LightDataChecker, Blake2Hasher, RlpCodec>, + Header, Vec>, usize) + { + // prepare remote client + let remote_client = test_client::new(); + let remote_block_id = BlockId::Number(0); + let remote_block_hash = remote_client.block_hash(0).unwrap().unwrap(); + let mut remote_block_header = remote_client.header(&remote_block_id).unwrap().unwrap(); + remote_block_header.state_root = remote_client.state_at(&remote_block_id).unwrap().storage_root(::std::iter::empty()).0.into(); + + // 'fetch' read proof from remote node + let authorities_len = remote_client.authorities_at(&remote_block_id).unwrap().len(); + let remote_read_proof = remote_client.read_proof(&remote_block_id, b":auth:len").unwrap(); + + // check remote read proof locally + let local_storage = InMemoryBlockchain::::new(); + local_storage.insert(remote_block_hash, remote_block_header.clone(), None, None, true); + let local_executor = test_client::LocalExecutor::new(); + let local_checker = LightDataChecker::new(local_executor); + (local_checker, remote_block_header, remote_read_proof, authorities_len) + } + + fn prepare_for_header_proof_check(insert_cht: bool) -> ( + LightDataChecker, Blake2Hasher, RlpCodec>, + Hash, Header, Vec>) + { + // prepare remote client + let remote_client = test_client::new(); + let mut local_headers_hashes = Vec::new(); + for i in 0..4 { + let builder = remote_client.new_block().unwrap(); + remote_client.justify_and_import(BlockOrigin::Own, builder.bake().unwrap()).unwrap(); + local_headers_hashes.push(remote_client.block_hash(i + 1).unwrap()); + } + + // 'fetch' header proof from remote node + let remote_block_id = BlockId::Number(1); + let (remote_block_header, remote_header_proof) = remote_client.header_proof_with_cht_size(&remote_block_id, 4).unwrap(); + + // check remote read proof locally + let local_storage = InMemoryBlockchain::::new(); + let local_cht_root = cht::compute_root::(4, 0, local_headers_hashes.into_iter()).unwrap(); + if insert_cht { + local_storage.insert_cht_root(1, local_cht_root); + } + let local_executor = test_client::LocalExecutor::new(); + let local_checker = LightDataChecker::new(local_executor); + (local_checker, local_cht_root, remote_block_header, remote_header_proof) + } + + #[test] + fn storage_read_proof_is_generated_and_checked() { + let (local_checker, remote_block_header, remote_read_proof, authorities_len) = prepare_for_read_proof_check(); + assert_eq!((&local_checker as &FetchChecker).check_read_proof(&RemoteReadRequest::
{ + block: remote_block_header.hash(), + header: remote_block_header, + key: b":auth:len".to_vec(), + retry_count: None, + }, remote_read_proof).unwrap().unwrap()[0], authorities_len as u8); + } + + #[test] + fn header_proof_is_generated_and_checked() { + let (local_checker, local_cht_root, remote_block_header, remote_header_proof) = prepare_for_header_proof_check(true); + assert_eq!((&local_checker as &FetchChecker).check_header_proof(&RemoteHeaderRequest::
{ + cht_root: local_cht_root, + block: 1, + retry_count: None, + }, Some(remote_block_header.clone()), remote_header_proof).unwrap(), remote_block_header); + } + + #[test] + fn check_header_proof_fails_if_cht_root_is_invalid() { + let (local_checker, _, mut remote_block_header, remote_header_proof) = prepare_for_header_proof_check(true); + remote_block_header.number = 100; + assert!((&local_checker as &FetchChecker).check_header_proof(&RemoteHeaderRequest::
{ + cht_root: Default::default(), + block: 1, + retry_count: None, + }, Some(remote_block_header.clone()), remote_header_proof).is_err()); + } + + #[test] + fn check_header_proof_fails_if_invalid_header_provided() { + let (local_checker, local_cht_root, mut remote_block_header, remote_header_proof) = prepare_for_header_proof_check(true); + remote_block_header.number = 100; + assert!((&local_checker as &FetchChecker).check_header_proof(&RemoteHeaderRequest::
{ + cht_root: local_cht_root, + block: 1, + retry_count: None, + }, Some(remote_block_header.clone()), remote_header_proof).is_err()); + } +} diff --git a/core/client/src/light/mod.rs b/core/client/src/light/mod.rs new file mode 100644 index 0000000000000..d16814ae2fca5 --- /dev/null +++ b/core/client/src/light/mod.rs @@ -0,0 +1,77 @@ +// Copyright 2017 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 . + +//! Light client components. + +pub mod backend; +pub mod blockchain; +pub mod call_executor; +pub mod fetcher; + +use std::sync::Arc; + +use primitives::{Blake2Hasher, RlpCodec}; +use runtime_primitives::BuildStorage; +use runtime_primitives::traits::Block as BlockT; +use state_machine::{CodeExecutor, ExecutionStrategy}; + +use client::Client; +use error::Result as ClientResult; +use light::backend::Backend; +use light::blockchain::{Blockchain, Storage as BlockchainStorage}; +use light::call_executor::RemoteCallExecutor; +use light::fetcher::{Fetcher, LightDataChecker}; +use hashdb::Hasher; +use patricia_trie::NodeCodec; + +/// Create an instance of light client blockchain backend. +pub fn new_light_blockchain, F>(storage: S) -> Arc> { + Arc::new(Blockchain::new(storage)) +} + +/// Create an instance of light client backend. +pub fn new_light_backend, F: Fetcher>(blockchain: Arc>, fetcher: Arc) -> Arc> { + blockchain.set_fetcher(Arc::downgrade(&fetcher)); + Arc::new(Backend::new(blockchain)) +} + +/// Create an instance of light client. +pub fn new_light( + backend: Arc>, + fetcher: Arc, + genesis_storage: GS, +) -> ClientResult, RemoteCallExecutor, F, Blake2Hasher, RlpCodec>, B>> + where + B: BlockT, + S: BlockchainStorage, + F: Fetcher, + GS: BuildStorage, +{ + let executor = RemoteCallExecutor::new(backend.blockchain().clone(), fetcher); + Client::new(backend, executor, genesis_storage, ExecutionStrategy::NativeWhenPossible) +} + +/// Create an instance of fetch data checker. +pub fn new_fetch_checker( + executor: E, +) -> LightDataChecker + where + E: CodeExecutor, + H: Hasher, + C: NodeCodec, +{ + LightDataChecker::new(executor) +} diff --git a/core/client/src/notifications.rs b/core/client/src/notifications.rs new file mode 100644 index 0000000000000..390513d37a8f1 --- /dev/null +++ b/core/client/src/notifications.rs @@ -0,0 +1,289 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Storage notifications + +use std::{ + collections::{HashSet, HashMap}, + sync::Arc, +}; + +use fnv::{FnvHashSet, FnvHashMap}; +use futures::sync::mpsc; +use primitives::storage::{StorageKey, StorageData}; +use runtime_primitives::traits::Block as BlockT; + +/// Storage change set +#[derive(Debug)] +pub struct StorageChangeSet { + changes: Arc)>>, + filter: Option>, +} + +impl StorageChangeSet { + /// Convert the change set into iterator over storage items. + pub fn iter<'a>(&'a self) -> impl Iterator)> + 'a { + self.changes + .iter() + .filter(move |&(key, _)| match self.filter { + Some(ref filter) => filter.contains(key), + None => true, + }) + } +} + +/// Type that implements `futures::Stream` of storage change events. +pub type StorageEventStream = mpsc::UnboundedReceiver<(H, StorageChangeSet)>; + +type SubscriberId = u64; + +/// Manages storage listeners. +#[derive(Debug)] +pub struct StorageNotifications { + next_id: SubscriberId, + wildcard_listeners: FnvHashSet, + listeners: HashMap>, + sinks: FnvHashMap, + Option>, + )>, +} + +impl Default for StorageNotifications { + fn default() -> Self { + StorageNotifications { + next_id: Default::default(), + wildcard_listeners: Default::default(), + listeners: Default::default(), + sinks: Default::default(), + } + } +} + +impl StorageNotifications { + /// Trigger notification to all listeners. + /// + /// Note the changes are going to be filtered by listener's filter key. + /// In fact no event might be sent if clients are not interested in the changes. + pub fn trigger(&mut self, hash: &Block::Hash, changeset: impl Iterator, Option>)>) { + let has_wildcard = !self.wildcard_listeners.is_empty(); + + // early exit if no listeners + if !has_wildcard && self.listeners.is_empty() { + return; + } + + let mut subscribers = self.wildcard_listeners.clone(); + let mut changes = Vec::new(); + + // Collect subscribers and changes + for (k, v) in changeset { + let k = StorageKey(k); + let listeners = self.listeners.get(&k); + + if let Some(ref listeners) = listeners { + subscribers.extend(listeners.iter()); + } + + if has_wildcard || listeners.is_some() { + changes.push((k, v.map(StorageData))); + } + } + + // Don't send empty notifications + if changes.is_empty() { + return; + } + + let changes = Arc::new(changes); + // Trigger the events + for subscriber in subscribers { + let should_remove = { + let &(ref sink, ref filter) = self.sinks.get(&subscriber) + .expect("subscribers returned from self.listeners are always in self.sinks; qed"); + sink.unbounded_send((hash.clone(), StorageChangeSet { + changes: changes.clone(), + filter: filter.clone(), + })).is_err() + }; + + if should_remove { + self.remove_subscriber(subscriber); + } + } + } + + fn remove_subscriber(&mut self, subscriber: SubscriberId) { + if let Some((_, filters)) = self.sinks.remove(&subscriber) { + match filters { + None => { + self.wildcard_listeners.remove(&subscriber); + }, + Some(filters) => { + for key in filters { + let remove_key = match self.listeners.get_mut(&key) { + Some(ref mut set) => { + set.remove(&subscriber); + set.is_empty() + }, + None => false, + }; + + if remove_key { + self.listeners.remove(&key); + } + } + }, + } + } + } + + /// Start listening for particular storage keys. + pub fn listen(&mut self, filter_keys: Option<&[StorageKey]>) -> StorageEventStream { + self.next_id += 1; + + // add subscriber for every key + let keys = match filter_keys { + None => { + self.wildcard_listeners.insert(self.next_id); + None + }, + Some(keys) => Some(keys.iter().map(|key| { + self.listeners + .entry(key.clone()) + .or_insert_with(Default::default) + .insert(self.next_id); + key.clone() + }).collect()) + }; + + // insert sink + let (tx, rx) = mpsc::unbounded(); + self.sinks.insert(self.next_id, (tx, keys)); + rx + } +} + +#[cfg(test)] +mod tests { + use runtime_primitives::testing::{H256 as Hash, Block as RawBlock}; + use super::*; + use futures::Stream; + + + #[cfg(test)] + impl From)>> for StorageChangeSet { + fn from(changes: Vec<(StorageKey, Option)>) -> Self { + StorageChangeSet { + changes: Arc::new(changes), + filter: None, + } + } + } + + #[cfg(test)] + impl PartialEq for StorageChangeSet { + fn eq(&self, other: &Self) -> bool { + self.iter().eq(other.iter()) + } + } + + type Block = RawBlock; + + #[test] + fn triggering_change_should_notify_wildcard_listeners() { + // given + let mut notifications = StorageNotifications::::default(); + let mut recv = notifications.listen(None).wait(); + + // when + let changeset = vec![ + (vec![2], Some(vec![3])), + (vec![3], None), + ]; + notifications.trigger(&1.into(), changeset.into_iter()); + + // then + assert_eq!(recv.next().unwrap(), Ok((1.into(), vec![ + (StorageKey(vec![2]), Some(StorageData(vec![3]))), + (StorageKey(vec![3]), None), + ].into()))); + } + + #[test] + fn should_only_notify_interested_listeners() { + // given + let mut notifications = StorageNotifications::::default(); + let mut recv1 = notifications.listen(Some(&[StorageKey(vec![1])])).wait(); + let mut recv2 = notifications.listen(Some(&[StorageKey(vec![2])])).wait(); + + // when + let changeset = vec![ + (vec![2], Some(vec![3])), + (vec![1], None), + ]; + notifications.trigger(&1.into(), changeset.into_iter()); + + // then + assert_eq!(recv1.next().unwrap(), Ok((1.into(), vec![ + (StorageKey(vec![1]), None), + ].into()))); + assert_eq!(recv2.next().unwrap(), Ok((1.into(), vec![ + (StorageKey(vec![2]), Some(StorageData(vec![3]))), + ].into()))); + } + + #[test] + fn should_cleanup_subscribers_if_dropped() { + // given + let mut notifications = StorageNotifications::::default(); + { + let _recv1 = notifications.listen(Some(&[StorageKey(vec![1])])).wait(); + let _recv2 = notifications.listen(Some(&[StorageKey(vec![2])])).wait(); + let _recv3 = notifications.listen(None).wait(); + assert_eq!(notifications.listeners.len(), 2); + assert_eq!(notifications.wildcard_listeners.len(), 1); + } + + // when + let changeset = vec![ + (vec![2], Some(vec![3])), + (vec![1], None), + ]; + notifications.trigger(&1.into(), changeset.into_iter()); + + // then + assert_eq!(notifications.listeners.len(), 0); + assert_eq!(notifications.wildcard_listeners.len(), 0); + } + + #[test] + fn should_not_send_empty_notifications() { + // given + let mut recv = { + let mut notifications = StorageNotifications::::default(); + let recv = notifications.listen(None).wait(); + + // when + let changeset = vec![]; + notifications.trigger(&1.into(), changeset.into_iter()); + recv + }; + + // then + assert_eq!(recv.next(), None); + } +} diff --git a/core/codec/Cargo.toml b/core/codec/Cargo.toml new file mode 100644 index 0000000000000..a228686e90b1d --- /dev/null +++ b/core/codec/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "substrate-codec" +description = "Serialization and deserialization codec for runtime values" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +arrayvec = { version = "0.4", default_features = false } + +[features] +default = ["std"] +std = [] diff --git a/core/codec/README.adoc b/core/codec/README.adoc new file mode 100644 index 0000000000000..12d3953789a0a --- /dev/null +++ b/core/codec/README.adoc @@ -0,0 +1,13 @@ + += Codec + +.Summary +[source, toml] +---- +include::Cargo.toml[lines=2..5] +---- + +.Description +---- +include::src/lib.rs[tag=description] +---- diff --git a/core/codec/derive/Cargo.toml b/core/codec/derive/Cargo.toml new file mode 100644 index 0000000000000..d5ccd9fdd57aa --- /dev/null +++ b/core/codec/derive/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "substrate-codec-derive" +description = "Serialization and deserialization derive macro" +version = "0.1.0" +authors = ["Parity Technologies "] + +[lib] +proc-macro = true + +[dependencies] +syn = "0.14" +quote = "0.6" +proc-macro2 = "0.4" + +[dev-dependencies] +substrate-codec = { path = "../" } + +[features] +default = ["std"] +std = [] diff --git a/core/codec/derive/src/decode.rs b/core/codec/derive/src/decode.rs new file mode 100644 index 0000000000000..0d33a7c350de1 --- /dev/null +++ b/core/codec/derive/src/decode.rs @@ -0,0 +1,111 @@ +// Copyright 2018 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 proc_macro2::{Span, TokenStream, Ident}; +use syn::{ + Data, Fields, + spanned::Spanned, +}; + +pub fn quote(data: &Data, type_name: &Ident, input: &TokenStream) -> TokenStream { + let call_site = Span::call_site(); + match *data { + Data::Struct(ref data) => match data.fields { + Fields::Named(_) | Fields::Unnamed(_) => create_instance( + call_site, + quote! { #type_name }, + input, + &data.fields, + ), + Fields::Unit => { + quote_spanned! {call_site => + drop(#input); + Some(#type_name) + } + }, + }, + Data::Enum(ref data) => { + assert!(data.variants.len() < 256, "Currently only enums with at most 256 variants are encodable."); + + let recurse = data.variants.iter().enumerate().map(|(i, v)| { + let name = &v.ident; + let index = super::index(v, i); + + let create = create_instance( + call_site, + quote! { #type_name :: #name }, + input, + &v.fields, + ); + + quote_spanned! { v.span() => + x if x == #index as u8 => { + #create + }, + } + }); + + quote! { + match #input.read_byte()? { + #( #recurse )* + _ => None, + } + + } + + }, + Data::Union(_) => panic!("Union types are not supported."), + } +} + +fn create_instance(call_site: Span, name: TokenStream, input: &TokenStream, fields: &Fields) -> TokenStream { + match *fields { + Fields::Named(ref fields) => { + let recurse = fields.named.iter().map(|f| { + let name = &f.ident; + let field = quote_spanned!(call_site => #name); + + quote_spanned! { f.span() => + #field: ::codec::Decode::decode(#input)? + } + }); + + quote_spanned! {call_site => + Some(#name { + #( #recurse, )* + }) + } + }, + Fields::Unnamed(ref fields) => { + let recurse = fields.unnamed.iter().map(|f| { + quote_spanned! { f.span() => + ::codec::Decode::decode(#input)? + } + }); + + quote_spanned! {call_site => + Some(#name ( + #( #recurse, )* + )) + } + }, + Fields::Unit => { + quote_spanned! {call_site => + Some(#name) + } + }, + } +} diff --git a/core/codec/derive/src/encode.rs b/core/codec/derive/src/encode.rs new file mode 100644 index 0000000000000..c378c9af51016 --- /dev/null +++ b/core/codec/derive/src/encode.rs @@ -0,0 +1,153 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +#[cfg(not(feature = "std"))] +use core::str::from_utf8; +#[cfg(feature = "std")] +use std::str::from_utf8; + +use proc_macro2::{Span, TokenStream}; +use syn::{ + Data, Field, Fields, Ident, Index, + punctuated::Punctuated, + spanned::Spanned, + token::Comma, +}; + +type FieldsList = Punctuated; + +fn encode_fields( + dest: &TokenStream, + fields: &FieldsList, + field_name: F, +) -> TokenStream where + F: Fn(usize, &Option) -> TokenStream, +{ + let recurse = fields.iter().enumerate().map(|(i, f)| { + let field = field_name(i, &f.ident); + + quote_spanned! { f.span() => + #dest.push(#field); + } + }); + + quote! { + #( #recurse )* + } +} + +pub fn quote(data: &Data, type_name: &Ident, self_: &TokenStream, dest: &TokenStream) -> TokenStream { + let call_site = Span::call_site(); + match *data { + Data::Struct(ref data) => match data.fields { + Fields::Named(ref fields) => encode_fields( + dest, + &fields.named, + |_, name| quote_spanned!(call_site => &#self_.#name), + ), + Fields::Unnamed(ref fields) => encode_fields( + dest, + &fields.unnamed, + |i, _| { + let index = Index { index: i as u32, span: call_site }; + quote_spanned!(call_site => &#self_.#index) + }, + ), + Fields::Unit => quote_spanned! { call_site => + drop(#dest); + }, + }, + Data::Enum(ref data) => { + assert!(data.variants.len() < 256, "Currently only enums with at most 256 variants are encodable."); + + let recurse = data.variants.iter().enumerate().map(|(i, f)| { + let name = &f.ident; + let index = super::index(f, i); + + match f.fields { + Fields::Named(ref fields) => { + let field_name = |_, ident: &Option| quote_spanned!(call_site => #ident); + let names = fields.named + .iter() + .enumerate() + .map(|(i, f)| field_name(i, &f.ident)); + + let encode_fields = encode_fields( + dest, + &fields.named, + |a, b| field_name(a, b), + ); + + quote_spanned! { f.span() => + #type_name :: #name { #( ref #names, )* } => { + #dest.push_byte(#index as u8); + #encode_fields + } + } + }, + Fields::Unnamed(ref fields) => { + let field_name = |i, _: &Option| { + let data = stringify(i as u8); + let ident = from_utf8(&data).expect("We never go beyond ASCII"); + let ident = Ident::new(ident, call_site); + quote_spanned!(call_site => #ident) + }; + let names = fields.unnamed + .iter() + .enumerate() + .map(|(i, f)| field_name(i, &f.ident)); + + let encode_fields = encode_fields( + dest, + &fields.unnamed, + |a, b| field_name(a, b), + ); + + quote_spanned! { f.span() => + #type_name :: #name ( #( ref #names, )* ) => { + #dest.push_byte(#index as u8); + #encode_fields + } + } + }, + Fields::Unit => { + quote_spanned! { f.span() => + #type_name :: #name => { + #dest.push_byte(#index as u8); + } + } + }, + } + }); + + quote! { + match *#self_ { + #( #recurse )*, + } + } + }, + Data::Union(_) => panic!("Union types are not supported."), + } +} +pub fn stringify(id: u8) -> [u8; 2] { + const CHARS: &[u8] = b"abcdefghijklmnopqrstuvwxyz"; + let len = CHARS.len() as u8; + let symbol = |id: u8| CHARS[(id % len) as usize]; + let a = symbol(id); + let b = symbol(id / len); + + [a, b] +} diff --git a/core/codec/derive/src/lib.rs b/core/codec/derive/src/lib.rs new file mode 100644 index 0000000000000..1bf3aebe7522a --- /dev/null +++ b/core/codec/derive/src/lib.rs @@ -0,0 +1,126 @@ +// Copyright 2018 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 . + +//! Derives serialization and deserialization codec for complex structs for simple marshalling. + +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate proc_macro; +extern crate proc_macro2; + +#[macro_use] +extern crate syn; + +#[macro_use] +extern crate quote; + +use proc_macro::TokenStream; +use syn::{DeriveInput, Generics, GenericParam, Ident}; + +mod decode; +mod encode; + +const ENCODE_ERR: &str = "derive(Encode) failed"; + +#[proc_macro_derive(Encode, attributes(codec))] +pub fn encode_derive(input: TokenStream) -> TokenStream { + let input: DeriveInput = syn::parse(input).expect(ENCODE_ERR); + let name = &input.ident; + + let generics = add_trait_bounds(input.generics, parse_quote!(::codec::Encode)); + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + let self_ = quote!(self); + let dest_ = quote!(dest); + let encoding = encode::quote(&input.data, name, &self_, &dest_); + + let expanded = quote! { + impl #impl_generics ::codec::Encode for #name #ty_generics #where_clause { + fn encode_to(&#self_, #dest_: &mut EncOut) { + #encoding + } + } + }; + + expanded.into() +} + +#[proc_macro_derive(Decode, attributes(codec))] +pub fn decode_derive(input: TokenStream) -> TokenStream { + let input: DeriveInput = syn::parse(input).expect(ENCODE_ERR); + let name = &input.ident; + + let generics = add_trait_bounds(input.generics, parse_quote!(::codec::Decode)); + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + let input_ = quote!(input); + let decoding = decode::quote(&input.data, name, &input_); + + let expanded = quote! { + impl #impl_generics ::codec::Decode for #name #ty_generics #where_clause { + fn decode(#input_: &mut DecIn) -> Option { + #decoding + } + } + }; + + expanded.into() +} + +fn add_trait_bounds(mut generics: Generics, bounds: syn::TypeParamBound) -> Generics { + for param in &mut generics.params { + if let GenericParam::Type(ref mut type_param) = *param { + type_param.bounds.push(bounds.clone()); + } + } + generics +} + +fn index(v: &syn::Variant, i: usize) -> proc_macro2::TokenStream { + // look for an index in attributes + let index = v.attrs.iter().filter_map(|attr| { + let pair = attr.path.segments.first()?; + let seg = pair.value(); + + if seg.ident == Ident::new("codec", seg.ident.span()) { + assert_eq!(attr.path.segments.len(), 1); + + let meta = attr.interpret_meta(); + if let Some(syn::Meta::List(ref l)) = meta { + if let syn::NestedMeta::Meta(syn::Meta::NameValue(ref nv)) = l.nested.last().unwrap().value() { + assert_eq!(nv.ident, Ident::new("index", nv.ident.span())); + if let syn::Lit::Str(ref s) = nv.lit { + let byte: u8 = s.value().parse().expect("Numeric index expected."); + return Some(byte) + } + panic!("Invalid syntax for `codec` attribute: Expected string literal.") + } + } + panic!("Invalid syntax for `codec` attribute: Expected `name = value` pair.") + } else { + None + } + }).next(); + + // then fallback to discriminant or just index + index.map(|i| quote! { #i }) + .unwrap_or_else(|| v.discriminant + .as_ref() + .map(|&(_, ref expr)| quote! { #expr }) + .unwrap_or_else(|| quote! { #i }) + ) +} + diff --git a/core/codec/derive/tests/mod.rs b/core/codec/derive/tests/mod.rs new file mode 100644 index 0000000000000..6b3260f8f64e2 --- /dev/null +++ b/core/codec/derive/tests/mod.rs @@ -0,0 +1,151 @@ +// Copyright 2018 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 . + +extern crate substrate_codec as codec; + +#[macro_use] +extern crate substrate_codec_derive; + +use codec::{Encode, Decode}; + +#[derive(Debug, PartialEq, Encode, Decode)] +struct Unit; + +#[derive(Debug, PartialEq, Encode, Decode)] +struct Indexed(u32, u64); + +#[derive(Debug, PartialEq, Encode, Decode)] +struct Struct { + pub a: A, + pub b: B, + pub c: C, +} + +type TestType = Struct>; + +impl Struct { + fn new(a: A, b: B, c: C) -> Self { + Self { a, b, c } + } +} + +#[derive(Debug, PartialEq, Encode, Decode)] +enum EnumType { + #[codec(index = "15")] + A, + B(u32, u64), + C { + a: u32, + b: u64, + }, +} + +#[derive(Debug, PartialEq, Encode, Decode)] +enum EnumWithDiscriminant { + A = 1, + B = 15, + C = 255, +} + +#[test] +fn should_work_for_simple_enum() { + let a = EnumType::A; + let b = EnumType::B(1, 2); + let c = EnumType::C { a: 1, b: 2 }; + + a.using_encoded(|ref slice| { + assert_eq!(slice, &b"\x0f"); + }); + b.using_encoded(|ref slice| { + assert_eq!(slice, &b"\x01\x01\0\0\0\x02\0\0\0\0\0\0\0"); + }); + c.using_encoded(|ref slice| { + assert_eq!(slice, &b"\x02\x01\0\0\0\x02\0\0\0\0\0\0\0"); + }); + + let mut da: &[u8] = b"\x0f"; + assert_eq!(EnumType::decode(&mut da), Some(a)); + let mut db: &[u8] = b"\x01\x01\0\0\0\x02\0\0\0\0\0\0\0"; + assert_eq!(EnumType::decode(&mut db), Some(b)); + let mut dc: &[u8] = b"\x02\x01\0\0\0\x02\0\0\0\0\0\0\0"; + assert_eq!(EnumType::decode(&mut dc), Some(c)); + let mut dz: &[u8] = &[0]; + assert_eq!(EnumType::decode(&mut dz), None); +} + +#[test] +fn should_work_for_enum_with_discriminant() { + EnumWithDiscriminant::A.using_encoded(|ref slice| { + assert_eq!(slice, &[1]); + }); + EnumWithDiscriminant::B.using_encoded(|ref slice| { + assert_eq!(slice, &[15]); + }); + EnumWithDiscriminant::C.using_encoded(|ref slice| { + assert_eq!(slice, &[255]); + }); + + let mut da: &[u8] = &[1]; + assert_eq!(EnumWithDiscriminant::decode(&mut da), Some(EnumWithDiscriminant::A)); + let mut db: &[u8] = &[15]; + assert_eq!(EnumWithDiscriminant::decode(&mut db), Some(EnumWithDiscriminant::B)); + let mut dc: &[u8] = &[255]; + assert_eq!(EnumWithDiscriminant::decode(&mut dc), Some(EnumWithDiscriminant::C)); + let mut dz: &[u8] = &[2]; + assert_eq!(EnumWithDiscriminant::decode(&mut dz), None); +} + +#[test] +fn should_derive_encode() { + let v = TestType::new(15, 9, b"Hello world".to_vec()); + + v.using_encoded(|ref slice| { + assert_eq!(slice, &b"\x0f\0\0\0\x09\0\0\0\0\0\0\0\x0b\0\0\0Hello world") + }); +} + +#[test] +fn should_derive_decode() { + let slice = b"\x0f\0\0\0\x09\0\0\0\0\0\0\0\x0b\0\0\0Hello world".to_vec(); + + let v = TestType::decode(&mut &*slice); + + assert_eq!(v, Some(TestType::new(15, 9, b"Hello world".to_vec()))); +} + +#[test] +fn should_work_for_unit() { + let v = Unit; + + v.using_encoded(|ref slice| { + assert_eq!(slice, &[]); + }); + + let mut a: &[u8] = &[]; + assert_eq!(Unit::decode(&mut a), Some(Unit)); +} + +#[test] +fn should_work_for_indexed() { + let v = Indexed(1, 2); + + v.using_encoded(|ref slice| { + assert_eq!(slice, &b"\x01\0\0\0\x02\0\0\0\0\0\0\0") + }); + + let mut v: &[u8] = b"\x01\0\0\0\x02\0\0\0\0\0\0\0"; + assert_eq!(Indexed::decode(&mut v), Some(Indexed(1, 2))); +} diff --git a/core/codec/src/codec.rs b/core/codec/src/codec.rs new file mode 100644 index 0000000000000..c92790f097a88 --- /dev/null +++ b/core/codec/src/codec.rs @@ -0,0 +1,540 @@ +// Copyright 2017 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 . + +//! Serialisation. + +use alloc::vec::Vec; +use alloc::boxed::Box; +use core::{mem, slice}; +use arrayvec::ArrayVec; + +/// Trait that allows reading of data into a slice. +pub trait Input { + /// Read into the provided input slice. Returns the number of bytes read. + fn read(&mut self, into: &mut [u8]) -> usize; + + /// Read a single byte from the input. + fn read_byte(&mut self) -> Option { + let mut buf = [0u8]; + match self.read(&mut buf[..]) { + 0 => None, + 1 => Some(buf[0]), + _ => unreachable!(), + } + } +} + +#[cfg(not(feature = "std"))] +impl<'a> Input for &'a [u8] { + fn read(&mut self, into: &mut [u8]) -> usize { + let len = ::core::cmp::min(into.len(), self.len()); + into[..len].copy_from_slice(&self[..len]); + *self = &self[len..]; + len + } +} + +#[cfg(feature = "std")] +impl Input for R { + fn read(&mut self, into: &mut [u8]) -> usize { + match (self as &mut ::std::io::Read).read_exact(into) { + Ok(()) => into.len(), + Err(_) => 0, + } + } +} + +/// Trait that allows writing of data. +pub trait Output: Sized { + /// Write to the output. + fn write(&mut self, bytes: &[u8]); + + fn push_byte(&mut self, byte: u8) { + self.write(&[byte]); + } + + fn push(&mut self, value: &V) { + value.encode_to(self); + } +} + +#[cfg(not(feature = "std"))] +impl Output for Vec { + fn write(&mut self, bytes: &[u8]) { + self.extend(bytes); + } +} + +#[cfg(feature = "std")] +impl Output for W { + fn write(&mut self, bytes: &[u8]) { + (self as &mut ::std::io::Write).write_all(bytes).expect("Codec outputs are infallible"); + } +} + +/// Trait that allows zero-copy write of value-references to slices in LE format. +/// Implementations should override `using_encoded` for value types and `encode_to` for allocating types. +pub trait Encode { + /// Convert self to a slice and append it to the destination. + fn encode_to(&self, dest: &mut T) { + self.using_encoded(|buf| dest.write(buf)); + } + + /// Convert self to an owned vector. + fn encode(&self) -> Vec { + let mut r = Vec::new(); + self.encode_to(&mut r); + r + } + + /// Convert self to a slice and then invoke the given closure with it. + fn using_encoded R>(&self, f: F) -> R { + f(&self.encode()) + } +} + +/// Trait that allows zero-copy read of value-references from slices in LE format. +pub trait Decode: Sized { + /// Attempt to deserialise the value from input. + fn decode(value: &mut I) -> Option; +} + +/// Trait that allows zero-copy read/write of value-references to/from slices in LE format. +pub trait Codec: Decode + Encode {} + +impl Codec for S {} + +impl Encode for Result { + fn encode_to(&self, dest: &mut W) { + match *self { + Ok(ref t) => { + dest.push_byte(0); + t.encode_to(dest); + } + Err(ref e) => { + dest.push_byte(1); + e.encode_to(dest); + } + } + } +} + +impl Decode for Result { + fn decode(input: &mut I) -> Option { + match input.read_byte()? { + 0 => Some(Ok(T::decode(input)?)), + 1 => Some(Err(E::decode(input)?)), + _ => None, + } + } +} + +/// Shim type because we can't do a specialised implementation for `Option` directly. +pub struct OptionBool(pub Option); + +impl Encode for OptionBool { + fn using_encoded R>(&self, f: F) -> R { + f(&[match *self { + OptionBool(None) => 0u8, + OptionBool(Some(true)) => 1u8, + OptionBool(Some(false)) => 2u8, + }]) + } +} + +impl Decode for OptionBool { + fn decode(input: &mut I) -> Option { + match input.read_byte()? { + 0 => Some(OptionBool(None)), + 1 => Some(OptionBool(Some(true))), + 2 => Some(OptionBool(Some(false))), + _ => None, + } + } +} + +impl Encode for Option { + fn encode_to(&self, dest: &mut W) { + match *self { + Some(ref t) => { + dest.push_byte(1); + t.encode_to(dest); + } + None => dest.push_byte(0), + } + } +} + +impl Decode for Option { + fn decode(input: &mut I) -> Option { + match input.read_byte()? { + 0 => Some(None), + 1 => Some(Some(T::decode(input)?)), + _ => None, + } + } +} + +macro_rules! impl_array { + ( $( $n:expr )* ) => { $( + impl Encode for [T; $n] { + fn encode_to(&self, dest: &mut W) { + for item in self.iter() { + item.encode_to(dest); + } + } + } + + impl Decode for [T; $n] { + fn decode(input: &mut I) -> Option { + let mut r = ArrayVec::new(); + for _ in 0..$n { + r.push(T::decode(input)?); + } + r.into_inner().ok() + } + } + )* } +} + +impl_array!(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 + 40 48 56 64 72 96 128 160 192 224 256); + +impl Encode for Box { + fn encode_to(&self, dest: &mut W) { + self.as_ref().encode_to(dest) + } +} + +impl Decode for Box { + fn decode(input: &mut I) -> Option { + Some(Box::new(T::decode(input)?)) + } +} + +impl Encode for [u8] { + fn encode_to(&self, dest: &mut W) { + let len = self.len(); + assert!(len <= u32::max_value() as usize, "Attempted to serialize a collection with too many elements."); + (len as u32).encode_to(dest); + dest.write(self) + } +} + +impl Encode for Vec { + fn encode_to(&self, dest: &mut W) { + self.as_slice().encode_to(dest) + } +} + +impl Decode for Vec { + fn decode(input: &mut I) -> Option { + u32::decode(input).and_then(move |len| { + let len = len as usize; + let mut vec = vec![0; len]; + if input.read(&mut vec[..len]) != len { + None + } else { + Some(vec) + } + }) + } +} + +impl<'a> Encode for &'a str { + fn encode_to(&self, dest: &mut W) { + self.as_bytes().encode_to(dest) + } +} + +#[cfg(feature = "std")] +impl<'a > Encode for ::std::borrow::Cow<'a, str> { + fn encode_to(&self, dest: &mut W) { + self.as_bytes().encode_to(dest) + } +} + +#[cfg(feature = "std")] +impl<'a> Decode for ::std::borrow::Cow<'a, str> { + fn decode(input: &mut I) -> Option { + Some(::std::borrow::Cow::Owned(String::from_utf8_lossy(&Vec::decode(input)?).into())) + } +} + +#[cfg(feature = "std")] +impl Encode for String { + fn encode_to(&self, dest: &mut W) { + self.as_bytes().encode_to(dest) + } +} + +#[cfg(feature = "std")] +impl Decode for String { + fn decode(input: &mut I) -> Option { + Some(Self::from_utf8_lossy(&Vec::decode(input)?).into()) + } +} + +impl Encode for [T] { + fn encode_to(&self, dest: &mut W) { + let len = self.len(); + assert!(len <= u32::max_value() as usize, "Attempted to serialize a collection with too many elements."); + (len as u32).encode_to(dest); + for item in self { + item.encode_to(dest); + } + } +} + +impl Encode for Vec { + fn encode_to(&self, dest: &mut W) { + self.as_slice().encode_to(dest) + } +} + +impl Decode for Vec { + fn decode(input: &mut I) -> Option { + u32::decode(input).and_then(move |len| { + let mut r = Vec::with_capacity(len as usize); + for _ in 0..len { + r.push(T::decode(input)?); + } + Some(r) + }) + } +} + +impl Encode for () { + fn encode_to(&self, _dest: &mut T) { + } + + fn using_encoded R>(&self, f: F) -> R { + f(&[]) + } + + fn encode(&self) -> Vec { + Vec::new() + } +} + +impl<'a, T: 'a + Encode + ?Sized> Encode for &'a T { + fn encode_to(&self, dest: &mut D) { + (&**self).encode_to(dest) + } + + fn using_encoded R>(&self, f: F) -> R { + (&**self).using_encoded(f) + } + + fn encode(&self) -> Vec { + (&**self).encode() + } +} + +impl Decode for () { + fn decode(_: &mut I) -> Option<()> { + Some(()) + } +} + +macro_rules! tuple_impl { + ($one:ident,) => { + impl<$one: Encode> Encode for ($one,) { + fn encode_to(&self, dest: &mut T) { + self.0.encode_to(dest); + } + } + + impl<$one: Decode> Decode for ($one,) { + fn decode(input: &mut I) -> Option { + match $one::decode(input) { + None => None, + Some($one) => Some(($one,)), + } + } + } + }; + ($first:ident, $($rest:ident,)+) => { + impl<$first: Encode, $($rest: Encode),+> + Encode for + ($first, $($rest),+) { + fn encode_to(&self, dest: &mut T) { + let ( + ref $first, + $(ref $rest),+ + ) = *self; + + $first.encode_to(dest); + $($rest.encode_to(dest);)+ + } + } + + impl<$first: Decode, $($rest: Decode),+> + Decode for + ($first, $($rest),+) { + fn decode(input: &mut INPUT) -> Option { + Some(( + match $first::decode(input) { + Some(x) => x, + None => return None, + }, + $(match $rest::decode(input) { + Some(x) => x, + None => return None, + },)+ + )) + } + } + + tuple_impl!($($rest,)+); + } +} + +#[allow(non_snake_case)] +mod inner_tuple_impl { + use super::{Input, Output, Decode, Encode}; + tuple_impl!(A, B, C, D, E, F, G, H, I, J, K,); +} + +/// Trait to allow conversion to a know endian representation when sensitive. +/// Types implementing this trait must have a size > 0. +// note: the copy bound and static lifetimes are necessary for safety of `Codec` blanket +// implementation. +trait EndianSensitive: Copy + 'static { + fn to_le(self) -> Self { self } + fn to_be(self) -> Self { self } + fn from_le(self) -> Self { self } + fn from_be(self) -> Self { self } + fn as_be_then T>(&self, f: F) -> T { f(&self) } + fn as_le_then T>(&self, f: F) -> T { f(&self) } +} + +macro_rules! impl_endians { + ( $( $t:ty ),* ) => { $( + impl EndianSensitive for $t { + fn to_le(self) -> Self { <$t>::to_le(self) } + fn to_be(self) -> Self { <$t>::to_be(self) } + fn from_le(self) -> Self { <$t>::from_le(self) } + fn from_be(self) -> Self { <$t>::from_be(self) } + fn as_be_then T>(&self, f: F) -> T { let d = self.to_be(); f(&d) } + fn as_le_then T>(&self, f: F) -> T { let d = self.to_le(); f(&d) } + } + + impl Encode for $t { + fn using_encoded R>(&self, f: F) -> R { + self.as_le_then(|le| { + let size = mem::size_of::<$t>(); + let value_slice = unsafe { + let ptr = le as *const _ as *const u8; + if size != 0 { + slice::from_raw_parts(ptr, size) + } else { + &[] + } + }; + + f(value_slice) + }) + } + } + + impl Decode for $t { + fn decode(input: &mut I) -> Option { + let size = mem::size_of::<$t>(); + assert!(size > 0, "EndianSensitive can never be implemented for a zero-sized type."); + let mut val: $t = unsafe { mem::zeroed() }; + + unsafe { + let raw: &mut [u8] = slice::from_raw_parts_mut( + &mut val as *mut $t as *mut u8, + size + ); + if input.read(raw) != size { return None } + } + Some(val.from_le()) + } + } + )* } +} +macro_rules! impl_non_endians { + ( $( $t:ty ),* ) => { $( + impl EndianSensitive for $t {} + + impl Encode for $t { + fn using_encoded R>(&self, f: F) -> R { + self.as_le_then(|le| { + let size = mem::size_of::<$t>(); + let value_slice = unsafe { + let ptr = le as *const _ as *const u8; + if size != 0 { + slice::from_raw_parts(ptr, size) + } else { + &[] + } + }; + + f(value_slice) + }) + } + } + + impl Decode for $t { + fn decode(input: &mut I) -> Option { + let size = mem::size_of::<$t>(); + assert!(size > 0, "EndianSensitive can never be implemented for a zero-sized type."); + let mut val: $t = unsafe { mem::zeroed() }; + + unsafe { + let raw: &mut [u8] = slice::from_raw_parts_mut( + &mut val as *mut $t as *mut u8, + size + ); + if input.read(raw) != size { return None } + } + Some(val.from_le()) + } + } + )* } +} + +impl_endians!(u16, u32, u64, u128, usize, i16, i32, i64, i128, isize); +impl_non_endians!(i8, [u8; 1], [u8; 2], [u8; 3], [u8; 4], [u8; 5], [u8; 6], [u8; 7], [u8; 8], + [u8; 10], [u8; 12], [u8; 14], [u8; 16], [u8; 20], [u8; 24], [u8; 28], [u8; 32], [u8; 40], + [u8; 48], [u8; 56], [u8; 64], [u8; 80], [u8; 96], [u8; 112], [u8; 128], bool); + + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn vec_is_slicable() { + let v = b"Hello world".to_vec(); + v.using_encoded(|ref slice| + assert_eq!(slice, &b"\x0b\0\0\0Hello world") + ); + } + + #[test] + fn encode_borrowed_tuple() { + let x = vec![1u8, 2, 3, 4]; + let y = 128i64; + + let encoded = (&x, &y).encode(); + + assert_eq!((x, y), Decode::decode(&mut &encoded[..]).unwrap()); + } +} diff --git a/core/codec/src/joiner.rs b/core/codec/src/joiner.rs new file mode 100644 index 0000000000000..81105b060ce63 --- /dev/null +++ b/core/codec/src/joiner.rs @@ -0,0 +1,33 @@ +// Copyright 2017 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 . + +//! Trait + +use core::iter::Extend; +use super::Codec; + +/// Trait to allow itself to be serialised into a value which can be extended +/// by bytes. +pub trait Joiner { + fn and(self, value: &V) -> Self; +} + +impl Joiner for T where T: for<'a> Extend<&'a u8> { + fn and(mut self, value: &V) -> Self { + value.using_encoded(|s| self.extend(s)); + self + } +} diff --git a/core/codec/src/keyedvec.rs b/core/codec/src/keyedvec.rs new file mode 100644 index 0000000000000..37298674a21af --- /dev/null +++ b/core/codec/src/keyedvec.rs @@ -0,0 +1,36 @@ +// Copyright 2017 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 . + +//! Serialiser and prepender. + +use Codec; +use core::iter::Extend; +use alloc::vec::Vec; + +/// Trait to allow itself to be serialised and prepended by a given slice. +pub trait KeyedVec { + fn to_keyed_vec(&self, prepend_key: &[u8]) -> Vec; +} + +impl KeyedVec for T { + fn to_keyed_vec(&self, prepend_key: &[u8]) -> Vec { + self.using_encoded(|slice| { + let mut r = prepend_key.to_vec(); + r.extend(slice); + r + }) + } +} diff --git a/core/codec/src/lib.rs b/core/codec/src/lib.rs new file mode 100644 index 0000000000000..c1c29ccaa0abc --- /dev/null +++ b/core/codec/src/lib.rs @@ -0,0 +1,45 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +// tag::description[] +//! Implements a serialization and deserialization codec for simple marshalling. +// end::description[] + +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(not(feature = "std"), feature(alloc))] + +#[cfg(not(feature = "std"))] +#[macro_use] +extern crate alloc; + +#[cfg(feature = "std")] +extern crate core; + +extern crate arrayvec; + +#[cfg(feature = "std")] +pub mod alloc { + pub use std::boxed; + pub use std::vec; +} + +mod codec; +mod joiner; +mod keyedvec; + +pub use self::codec::{Input, Output, Encode, Decode, Codec}; +pub use self::joiner::Joiner; +pub use self::keyedvec::KeyedVec; diff --git a/core/environmental/Cargo.toml b/core/environmental/Cargo.toml new file mode 100644 index 0000000000000..971dd38b676b5 --- /dev/null +++ b/core/environmental/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "environmental" +version = "0.1.0" +authors = ["Parity Technologies "] + +[features] +default = ["std"] +std = [] diff --git a/core/environmental/README.adoc b/core/environmental/README.adoc new file mode 100644 index 0000000000000..b12a0f9f5fad6 --- /dev/null +++ b/core/environmental/README.adoc @@ -0,0 +1,13 @@ + += Environmental + +.Summary +[source, toml] +---- +include::Cargo.toml[lines=2..5] +---- + +.Description +---- +include::src/lib.rs[tag=description] +---- diff --git a/core/environmental/src/lib.rs b/core/environmental/src/lib.rs new file mode 100644 index 0000000000000..ea7bbfa4f0ac3 --- /dev/null +++ b/core/environmental/src/lib.rs @@ -0,0 +1,383 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +// tag::description[] +//! Safe global references to stack variables. +//! +//! Set up a global reference with environmental! macro giving it a name and type. +//! Use the `using` function scoped under its name to name a reference and call a function that +//! takes no parameters yet can access said reference through the similarly placed `with` function. +//! +//! # Examples +//! +//! ``` +//! #[macro_use] extern crate environmental; +//! // create a place for the global reference to exist. +//! environmental!(counter: u32); +//! fn stuff() { +//! // do some stuff, accessing the named reference as desired. +//! counter::with(|i| *i += 1); +//! } +//! fn main() { +//! // declare a stack variable of the same type as our global declaration. +//! let mut counter_value = 41u32; +//! // call stuff, setting up our `counter` environment as a reference to our counter_value var. +//! counter::using(&mut counter_value, stuff); +//! println!("The answer is {:?}", counter_value); // will print 42! +//! stuff(); // safe! doesn't do anything. +//! } +//! ``` +// end::description[] + +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(not(feature = "std"), feature(const_fn))] + +#[cfg(feature = "std")] +include!("../with_std.rs"); + +#[cfg(not(feature = "std"))] +include!("../without_std.rs"); + +#[doc(hidden)] +pub fn using R>( + global: &'static imp::LocalKey>>, + protected: &mut T, + f: F +) -> R { + // store the `protected` reference as a pointer so we can provide it to logic running within + // `f`. + // while we record this pointer (while it's non-zero) we guarantee: + // - it will only be used once at any time (no reentrancy); + // - that no other thread will use it; and + // - that we do not use the original mutating reference while the pointer. + // exists. + global.with(|r| { + let original = { + let mut global = r.borrow_mut(); + imp::replace(&mut *global, Some(protected as _)) + }; + + // even if `f` panics the original will be replaced. + struct ReplaceOriginal<'a, T: 'a + ?Sized> { + original: Option<*mut T>, + global: &'a imp::RefCell>, + } + + impl<'a, T: 'a + ?Sized> Drop for ReplaceOriginal<'a, T> { + fn drop(&mut self) { + *self.global.borrow_mut() = self.original.take(); + } + } + + let _guard = ReplaceOriginal { + original, + global: r, + }; + + f() + }) +} + +#[doc(hidden)] +pub fn with R>( + global: &'static imp::LocalKey>>, + mutator: F, +) -> Option { + global.with(|r| unsafe { + let ptr = r.borrow_mut(); + match *ptr { + Some(ptr) => { + // safe because it's only non-zero when it's being called from using, which + // is holding on to the underlying reference (and not using it itself) safely. + Some(mutator(&mut *ptr)) + } + None => None, + } + }) +} + +/// Declare a new global reference module whose underlying value does not contain references. +/// +/// Will create a module of a given name that contains two functions: +/// +/// * `pub fn using R>(protected: &mut $t, f: F) -> R` +/// This executes `f`, returning its value. During the call, the module's reference is set to +/// be equal to `protected`. +/// * `pub fn with R>(f: F) -> Option` +/// This executes `f`, returning `Some` of its value if called from code that is being executed +/// as part of a `using` call. If not, it returns `None`. `f` is provided with one argument: the +/// same reference as provided to the most recent `using` call. +/// +/// # Examples +/// +/// Initializing the global context with a given value. +/// +/// ```rust +/// #[macro_use] extern crate environmental; +/// environmental!(counter: u32); +/// fn main() { +/// let mut counter_value = 41u32; +/// counter::using(&mut counter_value, || { +/// let odd = counter::with(|value| +/// if *value % 2 == 1 { +/// *value += 1; true +/// } else { +/// *value -= 3; false +/// }).unwrap(); // safe because we're inside a counter::using +/// println!("counter was {}", match odd { true => "odd", _ => "even" }); +/// }); +/// +/// println!("The answer is {:?}", counter_value); // 42 +/// } +/// ``` +/// +/// Roughly the same, but with a trait object: +/// +/// ```rust +/// #[macro_use] extern crate environmental; +/// +/// trait Increment { fn increment(&mut self); } +/// +/// impl Increment for i32 { +/// fn increment(&mut self) { *self += 1 } +/// } +/// +/// environmental!(val: Increment + 'static); +/// +/// fn main() { +/// let mut local = 0i32; +/// val::using(&mut local, || { +/// val::with(|v| for _ in 0..5 { v.increment() }); +/// }); +/// +/// assert_eq!(local, 5); +/// } +/// ``` +#[macro_export] +macro_rules! environmental { + ($name:ident : $t:ty) => { + #[allow(non_camel_case_types)] + struct $name { __private_field: () } + + thread_local_impl!(static GLOBAL: ::std::cell::RefCell> + = ::std::cell::RefCell::new(None)); + + impl $name { + #[allow(unused_imports)] + + pub fn using R>( + protected: &mut $t, + f: F + ) -> R { + $crate::using(&GLOBAL, protected, f) + } + + pub fn with R>( + f: F + ) -> Option { + $crate::with(&GLOBAL, |x| f(x)) + } + } + }; + ($name:ident : trait @$t:ident [$($args:ty,)*]) => { + #[allow(non_camel_case_types, dead_code)] + struct $name { __private_field: () } + + thread_local_impl!(static GLOBAL: $crate::imp::RefCell + 'static)>> + = $crate::imp::RefCell::new(None)); + + impl $name { + #[allow(unused_imports)] + + pub fn using R>( + protected: &mut $t<$($args),*>, + f: F + ) -> R { + let lifetime_extended = unsafe { + $crate::imp::transmute::<&mut $t<$($args),*>, &mut ($t<$($args),*> + 'static)>(protected) + }; + $crate::using(&GLOBAL, lifetime_extended, f) + } + + pub fn with FnOnce(&'a mut ($t<$($args),*> + 'a)) -> R>( + f: F + ) -> Option { + $crate::with(&GLOBAL, |x| f(x)) + } + } + }; + ($name:ident<$traittype:ident> : trait $t:ident <$concretetype:ty>) => { + #[allow(non_camel_case_types, dead_code)] + struct $name { _private_field: $crate::imp::PhantomData } + + thread_local_impl!(static GLOBAL: $crate::imp::RefCell + 'static)>> + = $crate::imp::RefCell::new(None)); + + impl $name { + #[allow(unused_imports)] + pub fn using R>( + protected: &mut $t, + f: F + ) -> R { + let lifetime_extended = unsafe { + $crate::imp::transmute::<&mut $t, &mut ($t<$concretetype> + 'static)>(protected) + }; + $crate::using(&GLOBAL, lifetime_extended, f) + } + + pub fn with FnOnce(&'a mut ($t<$concretetype> + 'a)) -> R>( + f: F + ) -> Option { + $crate::with(&GLOBAL, |x| f(x)) + } + } + }; + ($name:ident : trait $t:ident <>) => { environmental! { $name : trait @$t [] } }; + ($name:ident : trait $t:ident < $($args:ty),* $(,)* >) => { environmental! { $name : trait @$t [$($args,)*] } }; + ($name:ident : trait $t:ident) => { environmental! { $name : trait @$t [] } }; +} + +#[cfg(test)] +mod tests { + + #[test] + fn simple_works() { + environmental!(counter: u32); + + fn stuff() { counter::with(|value| *value += 1); }; + + // declare a stack variable of the same type as our global declaration. + let mut local = 41u32; + + // call stuff, setting up our `counter` environment as a reference to our local counter var. + counter::using(&mut local, stuff); + assert_eq!(local, 42); + stuff(); // safe! doesn't do anything. + assert_eq!(local, 42); + } + + #[test] + fn overwrite_with_lesser_lifetime() { + environmental!(items: Vec); + + let mut local_items = vec![1, 2, 3]; + items::using(&mut local_items, || { + let dies_at_end = vec![4, 5, 6]; + items::with(|items| *items = dies_at_end); + }); + + assert_eq!(local_items, vec![4, 5, 6]); + } + + #[test] + fn declare_with_trait_object() { + trait Foo { + fn get(&self) -> i32; + fn set(&mut self, x: i32); + } + + impl Foo for i32 { + fn get(&self) -> i32 { *self } + fn set(&mut self, x: i32) { *self = x } + } + + environmental!(foo: Foo + 'static); + + fn stuff() { + foo::with(|value| { + let new_val = value.get() + 1; + value.set(new_val); + }); + } + + let mut local = 41i32; + foo::using(&mut local, stuff); + + assert_eq!(local, 42); + + stuff(); // doesn't do anything. + + assert_eq!(local, 42); + } + + #[test] + fn unwind_recursive() { + use std::panic; + + environmental!(items: Vec); + + let panicked = panic::catch_unwind(|| { + let mut local_outer = vec![1, 2, 3]; + + items::using(&mut local_outer, || { + let mut local_inner = vec![4, 5, 6]; + items::using(&mut local_inner, || { + panic!("are you unsafe?"); + }) + }); + }).is_err(); + + assert!(panicked); + + let mut was_cleared = true; + items::with(|_items| was_cleared = false); + + assert!(was_cleared); + } + + #[test] + fn use_non_static_trait() { + trait Sum { fn sum(&self) -> usize; } + impl<'a> Sum for &'a [usize] { + fn sum(&self) -> usize { + self.iter().fold(0, |a, c| a + c) + } + } + + environmental!(sum: trait Sum); + let numbers = vec![1, 2, 3, 4, 5]; + let mut numbers = &numbers[..]; + let got_sum = sum::using(&mut numbers, || { + sum::with(|x| x.sum()) + }).unwrap(); + + assert_eq!(got_sum, 15); + } + + #[test] + fn use_generic_trait() { + trait Plus { fn plus42() -> usize; } + struct ConcretePlus; + impl Plus for ConcretePlus { + fn plus42() -> usize { 42 } + } + trait Multiplier { fn mul_and_add(&self) -> usize; } + impl<'a, P: Plus> Multiplier

for &'a [usize] { + fn mul_and_add(&self) -> usize { + self.iter().fold(1, |a, c| a * c) + P::plus42() + } + } + + let numbers = vec![1, 2, 3]; + let mut numbers = &numbers[..]; + let out = foo::::using(&mut numbers, || { + foo::::with(|x| x.mul_and_add() ) + }).unwrap(); + + assert_eq!(out, 6 + 42); + environmental!(foo: trait Multiplier); + } +} diff --git a/core/environmental/with_std.rs b/core/environmental/with_std.rs new file mode 100644 index 0000000000000..63ec5a71af317 --- /dev/null +++ b/core/environmental/with_std.rs @@ -0,0 +1,32 @@ +// Copyright 2018 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 . + +#[doc(hidden)] +pub mod imp { + pub use std::cell::RefCell; + pub use std::thread::LocalKey; + pub use std::mem::transmute; + pub use std::mem::replace; + pub use std::marker::PhantomData; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! thread_local_impl { + ($(#[$attr:meta])* static $name:ident: $t:ty = $init:expr) => ( + thread_local!($(#[$attr])* static $name: $t = $init); + ); +} diff --git a/core/environmental/without_std.rs b/core/environmental/without_std.rs new file mode 100644 index 0000000000000..0576419d1d3f5 --- /dev/null +++ b/core/environmental/without_std.rs @@ -0,0 +1,70 @@ +// Copyright 2018 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 . + +#[doc(hidden)] +pub mod imp { + pub use core::cell::RefCell; + pub use core::mem::transmute; + pub use core::mem::replace; + pub use core::marker::PhantomData; + + // This code is a simplified version of [`LocalKey`] and it's wasm32 specialization: [`statik::Key`]. + // [`LocalKey`]: https://github.com/alexcrichton/rust/blob/98931165a23a1c2860d99759385f45d6807c8982/src/libstd/thread/local.rs#L89 + // [`statik::Key`]: https://github.com/alexcrichton/rust/blob/98931165a23a1c2860d99759385f45d6807c8982/src/libstd/thread/local.rs#L310-L312 + + pub struct LocalKey { + pub init: fn() -> T, + pub inner: RefCell>, + } + + // This is safe as long there is no threads in wasm32. + unsafe impl ::core::marker::Sync for LocalKey { } + + impl LocalKey { + pub const fn new(init: fn() -> T) -> LocalKey { + LocalKey { + init, + inner: RefCell::new(None), + } + } + + pub fn with(&'static self, f: F) -> R + where F: FnOnce(&T) -> R + { + if self.inner.borrow().is_none() { + let v = (self.init)(); + *self.inner.borrow_mut() = Some(v); + } + // This code can't panic because: + // 1. `inner` can be borrowed mutably only once at the initialization time. + // 2. After the initialization `inner` is always `Some`. + f(&*self.inner.borrow().as_ref().unwrap()) + } + } +} + +#[doc(hidden)] +#[macro_export] +macro_rules! thread_local_impl { + ($(#[$attr:meta])* static $name:ident: $t:ty = $init:expr) => ( + $(#[$attr])* + static $name: $crate::imp::LocalKey<$t> = { + fn __init() -> $t { $init } + + $crate::imp::LocalKey::new(__init) + }; + ); +} diff --git a/core/executor/Cargo.toml b/core/executor/Cargo.toml new file mode 100644 index 0000000000000..4c30827dc1b7b --- /dev/null +++ b/core/executor/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "substrate-executor" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +error-chain = "0.12" +substrate-codec = { path = "../codec" } +substrate-runtime-io = { path = "../runtime-io" } +substrate-primitives = { path = "../primitives" } +substrate-serializer = { path = "../serializer" } +substrate-state-machine = { path = "../state-machine" } +substrate-runtime-version = { path = "../../runtime/version" } + +serde = "1.0" +serde_derive = "1.0" +wasmi = "0.4" +byteorder = "1.1" +triehash = "0.2" +twox-hash = "1.1.0" +lazy_static = "1.0" +parking_lot = "*" +log = "0.3" +hashdb = "0.2.1" + +[dev-dependencies] +assert_matches = "1.1" +wabt = "0.4" +hex-literal = "0.1.0" + +[features] +default = [] +wasm-extern-trace = [] diff --git a/core/executor/README.adoc b/core/executor/README.adoc new file mode 100644 index 0000000000000..6a0ee23565a70 --- /dev/null +++ b/core/executor/README.adoc @@ -0,0 +1,13 @@ + += Executor + +.Summary +[source, toml] +---- +include::Cargo.toml[lines=2..5] +---- + +.Description +---- +include::src/lib.rs[tag=description] +---- diff --git a/core/executor/src/error.rs b/core/executor/src/error.rs new file mode 100644 index 0000000000000..1729ed2bafa11 --- /dev/null +++ b/core/executor/src/error.rs @@ -0,0 +1,81 @@ +// Copyright 2017 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 . + +//! Rust executor possible errors. + +use state_machine; +use serializer; +use wasmi; + +error_chain! { + foreign_links { + InvalidData(serializer::Error) #[doc = "Unserializable Data"]; + Trap(wasmi::Trap) #[doc = "Trap occured during execution"]; + Wasmi(wasmi::Error) #[doc = "Wasmi loading/instantiating error"]; + } + + errors { + /// Method is not found + MethodNotFound(t: String) { + description("method not found"), + display("Method not found: '{}'", t), + } + + /// Code is invalid (expected single byte) + InvalidCode(c: Vec) { + description("invalid code"), + display("Invalid Code: {:?}", c), + } + + /// Could not get runtime version. + VersionInvalid { + description("Runtime version error"), + display("On-chain runtime does not specify version"), + } + + /// Externalities have failed. + Externalities { + description("externalities failure"), + display("Externalities error"), + } + + /// Invalid index. + InvalidIndex { + description("index given was not in range"), + display("Invalid index provided"), + } + + /// Invalid return type. + InvalidReturn { + description("u64 was not returned"), + display("Invalid type returned (should be u64)"), + } + + /// Runtime failed. + Runtime { + description("runtime failure"), + display("Runtime error"), + } + + /// Runtime failed. + InvalidMemoryReference { + description("invalid memory reference"), + display("Invalid memory reference"), + } + } +} + +impl state_machine::Error for Error {} diff --git a/core/executor/src/lib.rs b/core/executor/src/lib.rs new file mode 100644 index 0000000000000..7b9779a9b6467 --- /dev/null +++ b/core/executor/src/lib.rs @@ -0,0 +1,93 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +// tag::description[] +//! Temporary crate for contracts implementations. +//! +//! This will be replaced with WASM contracts stored on-chain. +//! ** NOTE *** +//! This is entirely deprecated with the idea of a single-module Wasm module for state transition. +//! The dispatch table should be replaced with the specific functions needed: +//! - execute_block(bytes) +//! - init_block(PrevBlock?) -> InProgressBlock +//! - add_transaction(InProgressBlock) -> InProgressBlock +//! I leave it as is for now as it might be removed before this is ever done. +// end::description[] + +#![warn(missing_docs)] +#![recursion_limit="128"] + +extern crate substrate_codec as codec; +extern crate substrate_runtime_io as runtime_io; +extern crate substrate_primitives as primitives; +extern crate substrate_serializer as serializer; +extern crate substrate_state_machine as state_machine; +extern crate substrate_runtime_version as runtime_version; + +extern crate serde; +extern crate wasmi; +extern crate byteorder; +extern crate triehash; +extern crate parking_lot; +extern crate twox_hash; +extern crate hashdb; + +#[macro_use] extern crate log; + +#[macro_use] +extern crate lazy_static; + +#[macro_use] +extern crate error_chain; + +#[cfg(test)] +extern crate assert_matches; + +#[cfg(test)] +extern crate wabt; + +#[cfg(test)] +#[macro_use] +extern crate hex_literal; + +#[macro_use] +mod wasm_utils; +mod wasm_executor; +#[macro_use] +mod native_executor; +mod sandbox; + +pub mod error; +pub use wasm_executor::WasmExecutor; +pub use native_executor::{with_native_environment, NativeExecutor, NativeExecutionDispatch}; +pub use state_machine::Externalities; +pub use runtime_version::RuntimeVersion; +pub use codec::Codec; +use primitives::Blake2Hasher; + +/// Provides runtime information. +pub trait RuntimeInfo { + /// Native runtime information if any. + const NATIVE_VERSION: Option; + + /// Extract RuntimeVersion of given :code block + fn runtime_version> ( + &self, + ext: &mut E, + heap_pages: usize, + code: &[u8] + ) -> Option; +} diff --git a/core/executor/src/native_executor.rs b/core/executor/src/native_executor.rs new file mode 100644 index 0000000000000..bc928364a607a --- /dev/null +++ b/core/executor/src/native_executor.rs @@ -0,0 +1,224 @@ +// Copyright 2017 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 error::{Error, ErrorKind, Result}; +use state_machine::{CodeExecutor, Externalities}; +use wasm_executor::WasmExecutor; +use wasmi::Module as WasmModule; +use runtime_version::RuntimeVersion; +use std::collections::HashMap; +use codec::Decode; +use primitives::hashing::blake2_256; +use parking_lot::{Mutex, MutexGuard}; +use RuntimeInfo; +use primitives::Blake2Hasher; + +// For the internal Runtime Cache: +// Is it compatible enough to run this natively or do we need to fall back on the WasmModule + +enum RuntimePreproc { + InvalidCode, + ValidCode(WasmModule, Option), +} + +type CacheType = HashMap<[u8; 32], RuntimePreproc>; + +lazy_static! { + static ref RUNTIMES_CACHE: Mutex = Mutex::new(HashMap::new()); +} + +// helper function to generate low-over-head caching_keys +// it is asserted that part of the audit process that any potential on-chain code change +// will have done is to ensure that the two-x hash is different to that of any other +// :code value from the same chain +fn gen_cache_key(code: &[u8]) -> [u8; 32] { + blake2_256(code) +} + +/// fetch a runtime version from the cache or if there is no cached version yet, create +/// the runtime version entry for `code`, determines whether `Compatibility::IsCompatible` +/// can be used by comparing returned RuntimeVersion to `ref_version` +fn fetch_cached_runtime_version<'a, E: Externalities>( + wasm_executor: &WasmExecutor, + cache: &'a mut MutexGuard, + ext: &mut E, + heap_pages: usize, + code: &[u8] +) -> Result<(&'a WasmModule, &'a Option)> { + let maybe_runtime_preproc = cache.entry(gen_cache_key(code)) + .or_insert_with(|| match WasmModule::from_buffer(code) { + Ok(module) => { + let version = wasm_executor.call_in_wasm_module(ext, heap_pages, &module, "version", &[]) + .ok() + .and_then(|v| RuntimeVersion::decode(&mut v.as_slice())); + RuntimePreproc::ValidCode(module, version) + } + Err(e) => { + trace!(target: "executor", "Invalid code presented to executor ({:?})", e); + RuntimePreproc::InvalidCode + } + }); + match maybe_runtime_preproc { + RuntimePreproc::InvalidCode => Err(ErrorKind::InvalidCode(code.into()).into()), + RuntimePreproc::ValidCode(m, v) => Ok((m, v)), + } +} + +fn safe_call(f: F) -> Result + where F: ::std::panic::UnwindSafe + FnOnce() -> U +{ + // Substrate uses custom panic hook that terminates process on panic. Disable it for the native call. + let hook = ::std::panic::take_hook(); + let result = ::std::panic::catch_unwind(f).map_err(|_| ErrorKind::Runtime.into()); + ::std::panic::set_hook(hook); + result +} + +/// Set up the externalities and safe calling environment to execute calls to a native runtime. +/// +/// If the inner closure panics, it will be caught and return an error. +pub fn with_native_environment(ext: &mut Externalities, f: F) -> Result +where F: ::std::panic::UnwindSafe + FnOnce() -> U +{ + ::runtime_io::with_externalities(ext, move || safe_call(f)) +} + +/// Delegate for dispatching a CodeExecutor call to native code. +pub trait NativeExecutionDispatch: Send + Sync { + /// Get the wasm code that the native dispatch will be equivalent to. + fn native_equivalent() -> &'static [u8]; + + /// Dispatch a method and input data to be executed natively. Returns `Some` result or `None` + /// if the `method` is unknown. Panics if there's an unrecoverable error. + // fn dispatch(ext: &mut Externalities, method: &str, data: &[u8]) -> Result>; + fn dispatch(ext: &mut Externalities, method: &str, data: &[u8]) -> Result>; + + /// Get native runtime version. + const VERSION: RuntimeVersion; + + /// Construct corresponding `NativeExecutor` + fn new() -> NativeExecutor where Self: Sized { + NativeExecutor::new() + } +} + +/// A generic `CodeExecutor` implementation that uses a delegate to determine wasm code equivalence +/// and dispatch to native code when possible, falling back on `WasmExecutor` when not. +#[derive(Debug)] +pub struct NativeExecutor { + /// Dummy field to avoid the compiler complaining about us not using `D`. + _dummy: ::std::marker::PhantomData, + /// The fallback executor in case native isn't available. + fallback: WasmExecutor, +} + +impl NativeExecutor { + /// Create new instance. + pub fn new() -> Self { + NativeExecutor { + _dummy: Default::default(), + fallback: WasmExecutor::new(), + } + } +} + +impl Clone for NativeExecutor { + fn clone(&self) -> Self { + NativeExecutor { + _dummy: Default::default(), + fallback: self.fallback.clone(), + } + } +} + +impl RuntimeInfo for NativeExecutor { + const NATIVE_VERSION: Option = Some(D::VERSION); + + fn runtime_version>( + &self, + ext: &mut E, + heap_pages: usize, + code: &[u8], + ) -> Option { + fetch_cached_runtime_version(&self.fallback, &mut RUNTIMES_CACHE.lock(), ext, heap_pages, code).ok()?.1.clone() + } +} + +impl CodeExecutor for NativeExecutor { + type Error = Error; + + fn call>( + &self, + ext: &mut E, + heap_pages: usize, + code: &[u8], + method: &str, + data: &[u8], + use_native: bool, + ) -> (Result>, bool) { + let mut c = RUNTIMES_CACHE.lock(); + let (module, onchain_version) = match fetch_cached_runtime_version(&self.fallback, &mut c, ext, heap_pages, code) { + Ok((module, onchain_version)) => (module, onchain_version), + Err(_) => return (Err(ErrorKind::InvalidCode(code.into()).into()), false), + }; + match (use_native, onchain_version.as_ref().map_or(false, |v| v.can_call_with(&D::VERSION))) { + (_, false) => { + trace!(target: "executor", "Request for native execution failed (native: {}, chain: {})", D::VERSION, onchain_version.as_ref().map_or_else(||"".into(), |v| format!("{}", v))); + (self.fallback.call_in_wasm_module(ext, heap_pages, module, method, data), false) + } + (false, _) => { + (self.fallback.call_in_wasm_module(ext, heap_pages, module, method, data), false) + } + _ => { + trace!(target: "executor", "Request for native execution succeeded (native: {}, chain: {})", D::VERSION, onchain_version.as_ref().map_or_else(||"".into(), |v| format!("{}", v))); + (D::dispatch(ext, method, data), true) + } + } + } +} + +#[macro_export] +macro_rules! native_executor_instance { + (pub $name:ident, $dispatcher:path, $version:path, $code:expr) => { + pub struct $name; + native_executor_instance!(IMPL $name, $dispatcher, $version, $code); + }; + ($name:ident, $dispatcher:path, $version:path, $code:expr) => { + /// A unit struct which implements `NativeExecutionDispatch` feeding in the hard-coded runtime. + struct $name; + native_executor_instance!(IMPL $name, $dispatcher, $version, $code); + }; + (IMPL $name:ident, $dispatcher:path, $version:path, $code:expr) => { + // TODO: this is not so great – I think I should go back to have dispatch take a type param and modify this macro to accept a type param and then pass it in from the test-client instead + use primitives::Blake2Hasher as _Blake2Hasher; + impl $crate::NativeExecutionDispatch for $name { + const VERSION: $crate::RuntimeVersion = $version; + fn native_equivalent() -> &'static [u8] { + // WARNING!!! This assumes that the runtime was built *before* the main project. Until we + // get a proper build script, this must be strictly adhered to or things will go wrong. + $code + } + fn dispatch(ext: &mut $crate::Externalities<_Blake2Hasher>, method: &str, data: &[u8]) -> $crate::error::Result> { + $crate::with_native_environment(ext, move || $dispatcher(method, data))? + .ok_or_else(|| $crate::error::ErrorKind::MethodNotFound(method.to_owned()).into()) + } + + fn new() -> $crate::NativeExecutor<$name> { + $crate::NativeExecutor::new() + } + } + } +} diff --git a/core/executor/src/sandbox.rs b/core/executor/src/sandbox.rs new file mode 100644 index 0000000000000..7bedde70dc367 --- /dev/null +++ b/core/executor/src/sandbox.rs @@ -0,0 +1,676 @@ +// Copyright 2018 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 . + +#![warn(missing_docs)] + +//! This module implements sandboxing support in the runtime. + +use std::collections::HashMap; +use std::rc::Rc; +use codec::{Decode, Encode}; +use primitives::sandbox as sandbox_primitives; +use wasm_utils::UserError; +use wasmi; +use wasmi::memory_units::Pages; +use wasmi::{ + Externals, FuncRef, ImportResolver, MemoryInstance, MemoryRef, Module, ModuleInstance, + ModuleRef, RuntimeArgs, RuntimeValue, Trap, TrapKind +}; + +/// Index of a function inside the supervisor. +/// +/// This is a typically an index in the default table of the supervisor, however +/// the exact meaning of this index is depends on the implementation of dispatch function. +#[derive(Copy, Clone, Debug, PartialEq)] +struct SupervisorFuncIndex(usize); + +/// Index of a function within guest index space. +/// +/// This index is supposed to be used with as index for `Externals`. +#[derive(Copy, Clone, Debug, PartialEq)] +struct GuestFuncIndex(usize); + +/// This struct holds a mapping from guest index space to supervisor. +struct GuestToSupervisorFunctionMapping { + funcs: Vec, +} + +impl GuestToSupervisorFunctionMapping { + fn new() -> GuestToSupervisorFunctionMapping { + GuestToSupervisorFunctionMapping { funcs: Vec::new() } + } + + fn define(&mut self, supervisor_func: SupervisorFuncIndex) -> GuestFuncIndex { + let idx = self.funcs.len(); + self.funcs.push(supervisor_func); + GuestFuncIndex(idx) + } + + fn func_by_guest_index(&self, guest_func_idx: GuestFuncIndex) -> Option { + self.funcs.get(guest_func_idx.0).cloned() + } +} + +struct Imports { + func_map: HashMap<(Vec, Vec), GuestFuncIndex>, + memories_map: HashMap<(Vec, Vec), MemoryRef>, +} + +impl ImportResolver for Imports { + fn resolve_func( + &self, + module_name: &str, + field_name: &str, + signature: &::wasmi::Signature, + ) -> Result { + let key = ( + module_name.as_bytes().to_owned(), + field_name.as_bytes().to_owned(), + ); + let idx = *self.func_map.get(&key).ok_or_else(|| { + ::wasmi::Error::Instantiation(format!( + "Export {}:{} not found", + module_name, field_name + )) + })?; + Ok(::wasmi::FuncInstance::alloc_host(signature.clone(), idx.0)) + } + + fn resolve_memory( + &self, + module_name: &str, + field_name: &str, + _memory_type: &::wasmi::MemoryDescriptor, + ) -> Result { + let key = ( + module_name.as_bytes().to_vec(), + field_name.as_bytes().to_vec(), + ); + let mem = self.memories_map + .get(&key) + .ok_or_else(|| { + ::wasmi::Error::Instantiation(format!( + "Export {}:{} not found", + module_name, field_name + )) + })? + .clone(); + Ok(mem) + } + + fn resolve_global( + &self, + module_name: &str, + field_name: &str, + _global_type: &::wasmi::GlobalDescriptor, + ) -> Result<::wasmi::GlobalRef, ::wasmi::Error> { + Err(::wasmi::Error::Instantiation(format!( + "Export {}:{} not found", + module_name, field_name + ))) + } + + fn resolve_table( + &self, + module_name: &str, + field_name: &str, + _table_type: &::wasmi::TableDescriptor, + ) -> Result<::wasmi::TableRef, ::wasmi::Error> { + Err(::wasmi::Error::Instantiation(format!( + "Export {}:{} not found", + module_name, field_name + ))) + } +} + +/// This trait encapsulates sandboxing capabilities. +/// +/// Note that this functions are only called in the `supervisor` context. +pub trait SandboxCapabilities { + /// Returns a reference to an associated sandbox `Store`. + fn store(&self) -> &Store; + + /// Returns a mutable reference to an associated sandbox `Store`. + fn store_mut(&mut self) -> &mut Store; + + /// Allocate space of the specified length in the supervisor memory. + /// + /// Returns pointer to the allocated block. + fn allocate(&mut self, len: u32) -> u32; + + /// Deallocate space specified by the pointer that was previously returned by [`allocate`]. + /// + /// [`allocate`]: #tymethod.allocate + fn deallocate(&mut self, ptr: u32); + + /// Write `data` into the supervisor memory at offset specified by `ptr`. + /// + /// # Errors + /// + /// Returns `Err` if `ptr + data.len()` is out of bounds. + fn write_memory(&mut self, ptr: u32, data: &[u8]) -> Result<(), UserError>; + + /// Read `len` bytes from the supervisor memory. + /// + /// # Errors + /// + /// Returns `Err` if `ptr + len` is out of bounds. + fn read_memory(&self, ptr: u32, len: u32) -> Result, UserError>; +} + +/// Implementation of [`Externals`] that allows execution of guest module with +/// [externals][`Externals`] that might refer functions defined by supervisor. +/// +/// [`Externals`]: ../../wasmi/trait.Externals.html +pub struct GuestExternals<'a, FE: SandboxCapabilities + Externals + 'a> { + supervisor_externals: &'a mut FE, + sandbox_instance: &'a SandboxInstance, + state: u32, +} + +fn trap() -> Trap { + TrapKind::Host(Box::new(UserError("Sandbox error"))).into() +} + +fn deserialize_result(serialized_result: &[u8]) -> Result, Trap> { + use self::sandbox_primitives::{HostError, ReturnValue}; + let result_val = Result::::decode(&mut &serialized_result[..]) + .ok_or_else(|| trap())?; + + match result_val { + Ok(return_value) => Ok(match return_value { + ReturnValue::Unit => None, + ReturnValue::Value(typed_value) => Some(RuntimeValue::from(typed_value)), + }), + Err(HostError) => Err(trap()), + } +} + +impl<'a, FE: SandboxCapabilities + Externals + 'a> Externals for GuestExternals<'a, FE> { + fn invoke_index( + &mut self, + index: usize, + args: RuntimeArgs, + ) -> Result, Trap> { + // Make `index` typesafe again. + let index = GuestFuncIndex(index); + + let dispatch_thunk = self.sandbox_instance.dispatch_thunk.clone(); + let func_idx = self.sandbox_instance + .guest_to_supervisor_mapping + .func_by_guest_index(index) + .expect( + "`invoke_index` is called with indexes registered via `FuncInstance::alloc_host`; + `FuncInstance::alloc_host` is called with indexes that was obtained from `guest_to_supervisor_mapping`; + `func_by_guest_index` called with `index` can't return `None`; + qed" + ); + + // Serialize arguments into a byte vector. + let invoke_args_data: Vec = args.as_ref() + .iter() + .cloned() + .map(sandbox_primitives::TypedValue::from) + .collect::>() + .encode(); + + let state = self.state; + + // Move serialized arguments inside the memory and invoke dispatch thunk and + // then free allocated memory. + let invoke_args_ptr = self.supervisor_externals + .allocate(invoke_args_data.len() as u32); + self.supervisor_externals + .write_memory(invoke_args_ptr, &invoke_args_data)?; + let result = ::wasmi::FuncInstance::invoke( + &dispatch_thunk, + &[ + RuntimeValue::I32(invoke_args_ptr as i32), + RuntimeValue::I32(invoke_args_data.len() as i32), + RuntimeValue::I32(state as i32), + RuntimeValue::I32(func_idx.0 as i32), + ], + self.supervisor_externals, + ); + self.supervisor_externals.deallocate(invoke_args_ptr); + + // dispatch_thunk returns pointer to serialized arguments. + let (serialized_result_val_ptr, serialized_result_val_len) = match result { + // Unpack pointer and len of the serialized result data. + Ok(Some(RuntimeValue::I64(v))) => { + // Cast to u64 to use zero-extension. + let v = v as u64; + let ptr = (v as u64 >> 32) as u32; + let len = (v & 0xFFFFFFFF) as u32; + (ptr, len) + } + _ => return Err(trap()), + }; + + let serialized_result_val = self.supervisor_externals + .read_memory(serialized_result_val_ptr, serialized_result_val_len)?; + self.supervisor_externals + .deallocate(serialized_result_val_ptr); + + // We do not have to check the signature here, because it's automatically + // checked by wasmi. + + deserialize_result(&serialized_result_val) + } +} + +fn with_guest_externals( + supervisor_externals: &mut FE, + sandbox_instance: &SandboxInstance, + state: u32, + f: F, +) -> R +where + FE: SandboxCapabilities + Externals, + F: FnOnce(&mut GuestExternals) -> R, +{ + let mut guest_externals = GuestExternals { + supervisor_externals, + sandbox_instance, + state, + }; + f(&mut guest_externals) +} + +/// Sandboxed instance of a wasm module. +/// +/// It's primary purpose is to [`invoke`] exported functions on it. +/// +/// All imports of this instance are specified at the creation time and +/// imports are implemented by the supervisor. +/// +/// Hence, in order to invoke an exported function on a sandboxed module instance, +/// it's required to provide supervisor externals: it will be used to execute +/// code in the supervisor context. +/// +/// [`invoke`]: #method.invoke +pub struct SandboxInstance { + instance: ModuleRef, + dispatch_thunk: FuncRef, + guest_to_supervisor_mapping: GuestToSupervisorFunctionMapping, +} + +impl SandboxInstance { + /// Invoke an exported function by a name. + /// + /// `supervisor_externals` is required to execute the implementations + /// of the syscalls that published to a sandboxed module instance. + /// + /// The `state` parameter can be used to provide custom data for + /// these syscall implementations. + pub fn invoke( + &self, + export_name: &str, + args: &[RuntimeValue], + supervisor_externals: &mut FE, + state: u32, + ) -> Result, wasmi::Error> { + with_guest_externals( + supervisor_externals, + self, + state, + |guest_externals| { + self.instance + .invoke_export(export_name, args, guest_externals) + }, + ) + } +} + +fn decode_environment_definition( + raw_env_def: &[u8], + memories: &[Option], +) -> Result<(Imports, GuestToSupervisorFunctionMapping), UserError> { + let env_def = sandbox_primitives::EnvironmentDefinition::decode(&mut &raw_env_def[..]).ok_or_else(|| UserError("Sandbox error"))?; + + let mut func_map = HashMap::new(); + let mut memories_map = HashMap::new(); + let mut guest_to_supervisor_mapping = GuestToSupervisorFunctionMapping::new(); + + for entry in &env_def.entries { + let module = entry.module_name.clone(); + let field = entry.field_name.clone(); + + match entry.entity { + sandbox_primitives::ExternEntity::Function(func_idx) => { + let externals_idx = + guest_to_supervisor_mapping.define(SupervisorFuncIndex(func_idx as usize)); + func_map.insert((module, field), externals_idx); + } + sandbox_primitives::ExternEntity::Memory(memory_idx) => { + let memory_ref = memories + .get(memory_idx as usize) + .cloned() + .ok_or_else(|| UserError("Sandbox error"))? + .ok_or_else(|| UserError("Sandbox error"))?; + memories_map.insert((module, field), memory_ref); + } + } + } + + Ok(( + Imports { + func_map, + memories_map, + }, + guest_to_supervisor_mapping, + )) +} + +/// Instantiate a guest module and return it's index in the store. +/// +/// The guest module's code is specified in `wasm`. Environment that will be available to +/// guest module is specified in `raw_env_def` (serialized version of [`EnvironmentDefinition`]). +/// `dispatch_thunk` is used as function that handle calls from guests. +/// +/// # Errors +/// +/// Returns `Err` if any of the following conditions happens: +/// +/// - `raw_env_def` can't be deserialized as a [`EnvironmentDefinition`]. +/// - Module in `wasm` is invalid or couldn't be instantiated. +/// +/// [`EnvironmentDefinition`]: ../../sandbox/struct.EnvironmentDefinition.html +pub fn instantiate( + supervisor_externals: &mut FE, + dispatch_thunk: FuncRef, + wasm: &[u8], + raw_env_def: &[u8], + state: u32, +) -> Result { + let (imports, guest_to_supervisor_mapping) = + decode_environment_definition(raw_env_def, &supervisor_externals.store().memories)?; + + let module = Module::from_buffer(wasm).map_err(|_| UserError("Sandbox error"))?; + let instance = ModuleInstance::new(&module, &imports).map_err(|_| UserError("Sandbox error"))?; + + let sandbox_instance = Rc::new(SandboxInstance { + // In general, it's not a very good idea to use `.not_started_instance()` for anything + // but for extracting memory and tables. But in this particular case, we are extracting + // for the purpose of running `start` function which should be ok. + instance: instance.not_started_instance().clone(), + dispatch_thunk, + guest_to_supervisor_mapping, + }); + + with_guest_externals( + supervisor_externals, + &sandbox_instance, + state, + |guest_externals| { + instance + .run_start(guest_externals) + .map_err(|_| UserError("Sandbox error")) + }, + )?; + + let instance_idx = supervisor_externals + .store_mut() + .register_sandbox_instance(sandbox_instance); + + Ok(instance_idx) +} + +/// This struct keeps track of all sandboxed components. +pub struct Store { + // Memories and instances are `Some` untill torndown. + instances: Vec>>, + memories: Vec>, +} + +impl Store { + /// Create a new empty sandbox store. + pub fn new() -> Store { + Store { + instances: Vec::new(), + memories: Vec::new(), + } + } + + /// Create a new memory instance and return it's index. + /// + /// # Errors + /// + /// Returns `Err` if the memory couldn't be created. + /// Typically happens if `initial` is more than `maximum`. + pub fn new_memory(&mut self, initial: u32, maximum: u32) -> Result { + let maximum = match maximum { + sandbox_primitives::MEM_UNLIMITED => None, + specified_limit => Some(Pages(specified_limit as usize)), + }; + + let mem = + MemoryInstance::alloc(Pages(initial as usize), maximum).map_err(|_| UserError("Sandbox error"))?; + let mem_idx = self.memories.len(); + self.memories.push(Some(mem)); + Ok(mem_idx as u32) + } + + /// Returns `SandboxInstance` by `instance_idx`. + /// + /// # Errors + /// + /// Returns `Err` If `instance_idx` isn't a valid index of an instance or + /// instance is already torndown. + pub fn instance(&self, instance_idx: u32) -> Result, UserError> { + self.instances + .get(instance_idx as usize) + .cloned() + .ok_or_else(|| UserError("Sandbox error"))? + .ok_or_else(|| UserError("Sandbox error")) + } + + /// Returns reference to a memory instance by `memory_idx`. + /// + /// # Errors + /// + /// Returns `Err` If `memory_idx` isn't a valid index of an memory or + /// memory is already torndown. + pub fn memory(&self, memory_idx: u32) -> Result { + self.memories + .get(memory_idx as usize) + .cloned() + .ok_or_else(|| UserError("Sandbox error"))? + .ok_or_else(|| UserError("Sandbox error")) + } + + /// Teardown the memory at the specified index. + /// + /// # Errors + /// + /// Returns `Err` if `memory_idx` isn't a valid index of an memory. + pub fn memory_teardown(&mut self, memory_idx: u32) -> Result<(), UserError> { + if memory_idx as usize >= self.memories.len() { + return Err(UserError("Sandbox error")); + } + self.memories[memory_idx as usize] = None; + Ok(()) + } + + /// Teardown the instance at the specified index. + pub fn instance_teardown(&mut self, instance_idx: u32) -> Result<(), UserError> { + if instance_idx as usize >= self.instances.len() { + return Err(UserError("Sandbox error")); + } + self.instances[instance_idx as usize] = None; + Ok(()) + } + + fn register_sandbox_instance(&mut self, sandbox_instance: Rc) -> u32 { + let instance_idx = self.instances.len(); + self.instances.push(Some(sandbox_instance)); + instance_idx as u32 + } +} + +#[cfg(test)] +mod tests { + use wasm_executor::WasmExecutor; + use state_machine::TestExternalities; + use wabt; + + #[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 code = wabt::wat2wasm(r#" + (module + (import "env" "assert" (func $assert (param i32))) + (import "env" "inc_counter" (func $inc_counter (param i32) (result i32))) + (func (export "call") + (drop + (call $inc_counter (i32.const 5)) + ) + + (call $inc_counter (i32.const 3)) + ;; current counter value is on the stack + + ;; check whether current == 8 + i32.const 8 + i32.eq + + call $assert + ) + ) + "#).unwrap(); + + assert_eq!( + WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_sandbox", &code).unwrap(), + vec![1], + ); + } + + #[test] + fn sandbox_trap() { + let mut ext = TestExternalities::default(); + let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + + let code = wabt::wat2wasm(r#" + (module + (import "env" "assert" (func $assert (param i32))) + (func (export "call") + i32.const 0 + call $assert + ) + ) + "#).unwrap(); + + assert_eq!( + WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_sandbox", &code).unwrap(), + vec![0], + ); + } + + #[test] + fn start_called() { + let mut ext = TestExternalities::default(); + let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + + let code = wabt::wat2wasm(r#" + (module + (import "env" "assert" (func $assert (param i32))) + (import "env" "inc_counter" (func $inc_counter (param i32) (result i32))) + + ;; Start function + (start $start) + (func $start + ;; Increment counter by 1 + (drop + (call $inc_counter (i32.const 1)) + ) + ) + + (func (export "call") + ;; Increment counter by 1. The current value is placed on the stack. + (call $inc_counter (i32.const 1)) + + ;; Counter is incremented twice by 1, once there and once in `start` func. + ;; So check the returned value is equal to 2. + i32.const 2 + i32.eq + call $assert + ) + ) + "#).unwrap(); + + assert_eq!( + WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_sandbox", &code).unwrap(), + vec![1], + ); + } + + #[test] + fn invoke_args() { + let mut ext = TestExternalities::default(); + let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + + let code = wabt::wat2wasm(r#" + (module + (import "env" "assert" (func $assert (param i32))) + + (func (export "call") (param $x i32) (param $y i64) + ;; assert that $x = 0x12345678 + (call $assert + (i32.eq + (get_local $x) + (i32.const 0x12345678) + ) + ) + + (call $assert + (i64.eq + (get_local $y) + (i64.const 0x1234567887654321) + ) + ) + ) + ) + "#).unwrap(); + + assert_eq!( + WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_sandbox_args", &code).unwrap(), + vec![1], + ); + } + + #[test] + fn return_val() { + let mut ext = TestExternalities::default(); + let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + + let code = wabt::wat2wasm(r#" + (module + (func (export "call") (param $x i32) (result i32) + (i32.add + (get_local $x) + (i32.const 1) + ) + ) + ) + "#).unwrap(); + + assert_eq!( + WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_sandbox_return_val", &code).unwrap(), + vec![1], + ); + } +} diff --git a/core/executor/src/wasm_executor.rs b/core/executor/src/wasm_executor.rs new file mode 100644 index 0000000000000..18280c797df65 --- /dev/null +++ b/core/executor/src/wasm_executor.rs @@ -0,0 +1,718 @@ +// Copyright 2017 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 . + +//! Rust implementation of Substrate contracts. + +use std::cmp::Ordering; +use std::collections::HashMap; + +use wasmi::{ + Module, ModuleInstance, MemoryInstance, MemoryRef, TableRef, ImportsBuilder +}; +use wasmi::RuntimeValue::{I32, I64}; +use wasmi::memory_units::{Pages, Bytes}; +use state_machine::Externalities; +use error::{Error, ErrorKind, Result}; +use wasm_utils::UserError; +use primitives::{blake2_256, twox_128, twox_256, ed25519, Blake2Hasher, hexdisplay::HexDisplay}; +use primitives::sandbox as sandbox_primitives; +use triehash::ordered_trie_root; +use sandbox; + + +struct Heap { + end: u32, +} + +impl Heap { + /// Construct new `Heap` struct with a given number of pages. + /// + /// Returns `Err` if the heap couldn't allocate required + /// number of pages. + /// + /// This could mean that wasm binary specifies memory + /// limit and we are trying to allocate beyond that limit. + fn new(memory: &MemoryRef, pages: usize) -> Result { + let prev_page_count = memory.initial(); + memory.grow(Pages(pages)).map_err(|_| Error::from(ErrorKind::Runtime))?; + Ok(Heap { + end: Bytes::from(prev_page_count).0 as u32, + }) + } + + fn allocate(&mut self, size: u32) -> u32 { + let r = self.end; + self.end += size; + r + } + + fn deallocate(&mut self, _offset: u32) { + } +} + +#[cfg(feature="wasm-extern-trace")] +macro_rules! debug_trace { + ( $( $x:tt )* ) => ( trace!( $( $x )* ) ) +} +#[cfg(not(feature="wasm-extern-trace"))] +macro_rules! debug_trace { + ( $( $x:tt )* ) => () +} + +struct FunctionExecutor<'e, E: Externalities + 'e> { + sandbox_store: sandbox::Store, + heap: Heap, + memory: MemoryRef, + table: Option, + ext: &'e mut E, + hash_lookup: HashMap, Vec>, +} + +impl<'e, E: Externalities> FunctionExecutor<'e, E> { + fn new(m: MemoryRef, heap_pages: usize, t: Option, e: &'e mut E) -> Result { + Ok(FunctionExecutor { + sandbox_store: sandbox::Store::new(), + heap: Heap::new(&m, heap_pages)?, + memory: m, + table: t, + ext: e, + hash_lookup: HashMap::new(), + }) + } +} + +impl<'e, E: Externalities> sandbox::SandboxCapabilities for FunctionExecutor<'e, E> { + fn store(&self) -> &sandbox::Store { + &self.sandbox_store + } + fn store_mut(&mut self) -> &mut sandbox::Store { + &mut self.sandbox_store + } + fn allocate(&mut self, len: u32) -> u32 { + self.heap.allocate(len) + } + fn deallocate(&mut self, ptr: u32) { + self.heap.deallocate(ptr) + } + fn write_memory(&mut self, ptr: u32, data: &[u8]) -> ::std::result::Result<(), UserError> { + self.memory.set(ptr, data).map_err(|_| UserError("Invalid attempt to write_memory")) + } + fn read_memory(&self, ptr: u32, len: u32) -> ::std::result::Result, UserError> { + self.memory.get(ptr, len as usize).map_err(|_| UserError("Invalid attempt to write_memory")) + } +} + +trait WritePrimitive { + fn write_primitive(&self, offset: u32, t: T) -> ::std::result::Result<(), UserError>; +} + +impl WritePrimitive for MemoryInstance { + fn write_primitive(&self, offset: u32, t: u32) -> ::std::result::Result<(), UserError> { + use byteorder::{LittleEndian, ByteOrder}; + let mut r = [0u8; 4]; + LittleEndian::write_u32(&mut r, t); + self.set(offset, &r).map_err(|_| UserError("Invalid attempt to write_primitive")) + } +} + +trait ReadPrimitive { + fn read_primitive(&self, offset: u32) -> ::std::result::Result; +} + +impl ReadPrimitive for MemoryInstance { + fn read_primitive(&self, offset: u32) -> ::std::result::Result { + use byteorder::{LittleEndian, ByteOrder}; + Ok(LittleEndian::read_u32(&self.get(offset, 4).map_err(|_| UserError("Invalid attempt to read_primitive"))?)) + } +} + +// TODO: this macro does not support `where` clauses and that seems somewhat tricky to add +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) { + if let Ok(message) = String::from_utf8(utf8) { + println!("{}", message); + } + } + Ok(()) + }, + ext_print_hex(data: *const u8, len: u32) => { + if let Ok(hex) = this.memory.get(data, len as usize) { + println!("{}", HexDisplay::from(&hex)); + } + Ok(()) + }, + ext_print_num(number: u64) => { + println!("{}", number); + Ok(()) + }, + ext_memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32 => { + let sl1 = this.memory.get(s1, n as usize).map_err(|_| UserError("Invalid attempt to read from memory in first arg of ext_memcmp"))?; + let sl2 = this.memory.get(s2, n as usize).map_err(|_| UserError("Invalid attempt to read from memory in second arg of ext_memcmp"))?; + Ok(match sl1.cmp(&sl2) { + Ordering::Greater => 1, + Ordering::Less => -1, + Ordering::Equal => 0, + }) + }, + ext_memcpy(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => { + this.memory.copy_nonoverlapping(src as usize, dest as usize, count as usize) + .map_err(|_| UserError("Invalid attempt to copy_nonoverlapping in ext_memcpy"))?; + debug_trace!(target: "runtime-io", "memcpy {} from {}, {} bytes", dest, src, count); + Ok(dest) + }, + ext_memmove(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => { + this.memory.copy(src as usize, dest as usize, count as usize) + .map_err(|_| UserError("Invalid attempt to copy in ext_memmove"))?; + debug_trace!(target: "runtime-io", "memmove {} from {}, {} bytes", dest, src, count); + Ok(dest) + }, + ext_memset(dest: *mut u8, val: u32, count: usize) -> *mut u8 => { + debug_trace!(target: "runtime-io", "memset {} with {}, {} bytes", dest, val, count); + this.memory.clear(dest as usize, val as u8, count as usize) + .map_err(|_| UserError("Invalid attempt to clear in ext_memset"))?; + Ok(dest) + }, + ext_malloc(size: usize) -> *mut u8 => { + let r = this.heap.allocate(size); + debug_trace!(target: "runtime-io", "malloc {} bytes at {}", size, r); + Ok(r) + }, + ext_free(addr: *mut u8) => { + this.heap.deallocate(addr); + debug_trace!(target: "runtime-io", "free {}", addr); + Ok(()) + }, + ext_set_storage(key_data: *const u8, key_len: u32, value_data: *const u8, value_len: u32) => { + let key = this.memory.get(key_data, key_len as usize).map_err(|_| UserError("Invalid attempt to determine key in ext_set_storage"))?; + let value = this.memory.get(value_data, value_len as usize).map_err(|_| UserError("Invalid attempt to determine value in ext_set_storage"))?; + if let Some(_preimage) = this.hash_lookup.get(&key) { + debug_trace!(target: "wasm-trace", "*** Setting storage: %{} -> {} [k={}]", ::primitives::hexdisplay::ascii_format(&_preimage), HexDisplay::from(&value), HexDisplay::from(&key)); + } else { + debug_trace!(target: "wasm-trace", "*** Setting storage: {} -> {} [k={}]", ::primitives::hexdisplay::ascii_format(&key), HexDisplay::from(&value), HexDisplay::from(&key)); + } + this.ext.set_storage(key, value); + Ok(()) + }, + ext_clear_storage(key_data: *const u8, key_len: u32) => { + let key = this.memory.get(key_data, key_len as usize).map_err(|_| UserError("Invalid attempt to determine key in ext_clear_storage"))?; + debug_trace!(target: "wasm-trace", "*** Clearing storage: {} [k={}]", + if let Some(_preimage) = this.hash_lookup.get(&key) { + format!("%{}", ::primitives::hexdisplay::ascii_format(&_preimage)) + } else { + format!(" {}", ::primitives::hexdisplay::ascii_format(&key)) + }, HexDisplay::from(&key)); + this.ext.clear_storage(&key); + Ok(()) + }, + ext_exists_storage(key_data: *const u8, key_len: u32) -> u32 => { + let key = this.memory.get(key_data, key_len as usize).map_err(|_| UserError("Invalid attempt to determine key in ext_exists_storage"))?; + Ok(if this.ext.exists_storage(&key) { 1 } else { 0 }) + }, + ext_clear_prefix(prefix_data: *const u8, prefix_len: u32) => { + let prefix = this.memory.get(prefix_data, prefix_len as usize).map_err(|_| UserError("Invalid attempt to determine prefix in ext_clear_prefix"))?; + this.ext.clear_prefix(&prefix); + Ok(()) + }, + // return 0 and place u32::max_value() into written_out if no value exists for the key. + ext_get_allocated_storage(key_data: *const u8, key_len: u32, written_out: *mut u32) -> *mut u8 => { + let key = this.memory.get(key_data, key_len as usize).map_err(|_| UserError("Invalid attempt to determine key in ext_get_allocated_storage"))?; + let maybe_value = this.ext.storage(&key); + + debug_trace!(target: "wasm-trace", "*** Getting storage: {} == {} [k={}]", + if let Some(_preimage) = this.hash_lookup.get(&key) { + format!("%{}", ::primitives::hexdisplay::ascii_format(&_preimage)) + } else { + format!(" {}", ::primitives::hexdisplay::ascii_format(&key)) + }, + if let Some(ref b) = maybe_value { + format!("{}", HexDisplay::from(b)) + } else { + "".to_owned() + }, + HexDisplay::from(&key) + ); + + if let Some(value) = maybe_value { + let offset = this.heap.allocate(value.len() as u32) as u32; + this.memory.set(offset, &value).map_err(|_| UserError("Invalid attempt to set memory in ext_get_allocated_storage"))?; + this.memory.write_primitive(written_out, value.len() as u32) + .map_err(|_| UserError("Invalid attempt to write written_out in ext_get_allocated_storage"))?; + Ok(offset) + } else { + this.memory.write_primitive(written_out, u32::max_value()) + .map_err(|_| UserError("Invalid attempt to write failed written_out in ext_get_allocated_storage"))?; + Ok(0) + } + }, + // return u32::max_value() if no value exists for the key. + ext_get_storage_into(key_data: *const u8, key_len: u32, value_data: *mut u8, value_len: u32, value_offset: u32) -> u32 => { + let key = this.memory.get(key_data, key_len as usize).map_err(|_| UserError("Invalid attempt to get key in ext_get_storage_into"))?; + let maybe_value = this.ext.storage(&key); + debug_trace!(target: "wasm-trace", "*** Getting storage: {} == {} [k={}]", + if let Some(_preimage) = this.hash_lookup.get(&key) { + format!("%{}", ::primitives::hexdisplay::ascii_format(&_preimage)) + } else { + format!(" {}", ::primitives::hexdisplay::ascii_format(&key)) + }, + if let Some(ref b) = maybe_value { + format!("{}", HexDisplay::from(b)) + } else { + "".to_owned() + }, + HexDisplay::from(&key) + ); + + if let Some(value) = maybe_value { + let value = &value[value_offset as usize..]; + let written = ::std::cmp::min(value_len as usize, value.len()); + this.memory.set(value_data, &value[..written]).map_err(|_| UserError("Invalid attempt to set value in ext_get_storage_into"))?; + Ok(written as u32) + } else { + Ok(u32::max_value()) + } + }, + ext_storage_root(result: *mut u8) => { + let r = this.ext.storage_root(); + this.memory.set(result, r.as_ref()).map_err(|_| UserError("Invalid attempt to set memory in ext_storage_root"))?; + Ok(()) + }, + ext_blake2_256_enumerated_trie_root(values_data: *const u8, lens_data: *const u32, lens_len: u32, result: *mut u8) => { + let values = (0..lens_len) + .map(|i| this.memory.read_primitive(lens_data + i * 4)) + .collect::<::std::result::Result, UserError>>()? + .into_iter() + .scan(0u32, |acc, v| { let o = *acc; *acc += v; Some((o, v)) }) + .map(|(offset, len)| + this.memory.get(values_data + offset, len as usize) + .map_err(|_| UserError("Invalid attempt to get memory in ext_blake2_256_enumerated_trie_root")) + ) + .collect::<::std::result::Result, UserError>>()?; + let r = ordered_trie_root::(values.into_iter()); + this.memory.set(result, &r[..]).map_err(|_| UserError("Invalid attempt to set memory in ext_blake2_256_enumerated_trie_root"))?; + Ok(()) + }, + ext_chain_id() -> u64 => { + Ok(this.ext.chain_id()) + }, + ext_twox_128(data: *const u8, len: u32, out: *mut u8) => { + let result = if len == 0 { + let hashed = twox_128(&[0u8; 0]); + debug_trace!(target: "xxhash", "XXhash: '' -> {}", HexDisplay::from(&hashed)); + this.hash_lookup.insert(hashed.to_vec(), vec![]); + hashed + } else { + let key = this.memory.get(data, len as usize).map_err(|_| UserError("Invalid attempt to get key in ext_twox_128"))?; + let hashed_key = twox_128(&key); + debug_trace!(target: "xxhash", "XXhash: {} -> {}", + if let Ok(_skey) = ::std::str::from_utf8(&key) { + _skey.to_owned() + } else { + format!("{}", HexDisplay::from(&key)) + }, + HexDisplay::from(&hashed_key) + ); + this.hash_lookup.insert(hashed_key.to_vec(), key); + hashed_key + }; + + this.memory.set(out, &result).map_err(|_| UserError("Invalid attempt to set result in ext_twox_128"))?; + Ok(()) + }, + ext_twox_256(data: *const u8, len: u32, out: *mut u8) => { + let result = if len == 0 { + twox_256(&[0u8; 0]) + } else { + twox_256(&this.memory.get(data, len as usize).map_err(|_| UserError("Invalid attempt to get data in ext_twox_256"))?) + }; + this.memory.set(out, &result).map_err(|_| UserError("Invalid attempt to set result in ext_twox_256"))?; + Ok(()) + }, + ext_blake2_256(data: *const u8, len: u32, out: *mut u8) => { + let result = if len == 0 { + blake2_256(&[0u8; 0]) + } else { + blake2_256(&this.memory.get(data, len as usize).map_err(|_| UserError("Invalid attempt to get data in ext_blake2_256"))?) + }; + this.memory.set(out, &result).map_err(|_| UserError("Invalid attempt to set result in ext_blake2_256"))?; + Ok(()) + }, + ext_ed25519_verify(msg_data: *const u8, msg_len: u32, sig_data: *const u8, pubkey_data: *const u8) -> u32 => { + let mut sig = [0u8; 64]; + this.memory.get_into(sig_data, &mut sig[..]).map_err(|_| UserError("Invalid attempt to get signature in ext_ed25519_verify"))?; + let mut pubkey = [0u8; 32]; + this.memory.get_into(pubkey_data, &mut pubkey[..]).map_err(|_| UserError("Invalid attempt to get pubkey in ext_ed25519_verify"))?; + let msg = this.memory.get(msg_data, msg_len as usize).map_err(|_| UserError("Invalid attempt to get message in ext_ed25519_verify"))?; + + Ok(if ed25519::verify(&sig, &msg, &pubkey) { + 0 + } else { + 5 + }) + }, + ext_sandbox_instantiate(dispatch_thunk_idx: usize, wasm_ptr: *const u8, wasm_len: usize, imports_ptr: *const u8, imports_len: usize, state: usize) -> u32 => { + let wasm = this.memory.get(wasm_ptr, wasm_len as usize).map_err(|_| UserError("Sandbox error"))?; + let raw_env_def = this.memory.get(imports_ptr, imports_len as usize).map_err(|_| UserError("Sandbox error"))?; + + // Extract a dispatch thunk from instance's table by the specified index. + let dispatch_thunk = { + let table = this.table.as_ref().ok_or_else(|| UserError("Sandbox error"))?; + table.get(dispatch_thunk_idx) + .map_err(|_| UserError("Sandbox error"))? + .ok_or_else(|| UserError("Sandbox error"))? + .clone() + }; + + let instance_idx = sandbox::instantiate(this, dispatch_thunk, &wasm, &raw_env_def, state)?; + + Ok(instance_idx as u32) + }, + ext_sandbox_instance_teardown(instance_idx: u32) => { + this.sandbox_store.instance_teardown(instance_idx)?; + Ok(()) + }, + ext_sandbox_invoke(instance_idx: u32, export_ptr: *const u8, export_len: usize, args_ptr: *const u8, args_len: usize, return_val_ptr: *const u8, return_val_len: usize, state: usize) -> u32 => { + use codec::{Decode, Encode}; + + trace!(target: "runtime-sandbox", "invoke, instance_idx={}", instance_idx); + let export = this.memory.get(export_ptr, export_len as usize) + .map_err(|_| UserError("Sandbox error")) + .and_then(|b| + String::from_utf8(b) + .map_err(|_| UserError("Sandbox error")) + )?; + + // Deserialize arguments and convert them into wasmi types. + let serialized_args = this.memory.get(args_ptr, args_len as usize) + .map_err(|_| UserError("Sandbox error"))?; + let args = Vec::::decode(&mut &serialized_args[..]) + .ok_or_else(|| UserError("Sandbox error"))? + .into_iter() + .map(Into::into) + .collect::>(); + + let instance = this.sandbox_store.instance(instance_idx)?; + let result = instance.invoke(&export, &args, this, state); + + match result { + Ok(None) => Ok(sandbox_primitives::ERR_OK), + Ok(Some(val)) => { + // Serialize return value and write it back into the memory. + sandbox_primitives::ReturnValue::Value(val.into()).using_encoded(|val| { + if val.len() > return_val_len as usize { + Err(UserError("Sandbox error"))?; + } + this.memory + .set(return_val_ptr, val) + .map_err(|_| UserError("Sandbox error"))?; + Ok(sandbox_primitives::ERR_OK) + }) + } + Err(_) => Ok(sandbox_primitives::ERR_EXECUTION), + } + }, + ext_sandbox_memory_new(initial: u32, maximum: u32) -> u32 => { + let mem_idx = this.sandbox_store.new_memory(initial, maximum)?; + Ok(mem_idx) + }, + ext_sandbox_memory_get(memory_idx: u32, offset: u32, buf_ptr: *mut u8, buf_len: usize) -> u32 => { + let dst_memory = this.sandbox_store.memory(memory_idx)?; + + let data: Vec = match dst_memory.get(offset, buf_len as usize) { + Ok(data) => data, + Err(_) => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS), + }; + match this.memory.set(buf_ptr, &data) { + Err(_) => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS), + _ => {}, + } + + Ok(sandbox_primitives::ERR_OK) + }, + ext_sandbox_memory_set(memory_idx: u32, offset: u32, val_ptr: *const u8, val_len: usize) -> u32 => { + let dst_memory = this.sandbox_store.memory(memory_idx)?; + + let data = match this.memory.get(offset, val_len as usize) { + Ok(data) => data, + Err(_) => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS), + }; + match dst_memory.set(val_ptr, &data) { + Err(_) => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS), + _ => {}, + } + + Ok(sandbox_primitives::ERR_OK) + }, + ext_sandbox_memory_teardown(memory_idx: u32) => { + this.sandbox_store.memory_teardown(memory_idx)?; + Ok(()) + }, + => <'e, E: Externalities + 'e> +); + +/// Wasm rust executor for contracts. +/// +/// Executes the provided code in a sandboxed wasm runtime. +#[derive(Debug, Clone)] +pub struct WasmExecutor { +} + +impl WasmExecutor { + + /// Create a new instance. + pub fn new() -> Self { + WasmExecutor{} + } + + /// Call a given method in the given code. + /// This should be used for tests only. + pub fn call>( + &self, + ext: &mut E, + heap_pages: usize, + code: &[u8], + method: &str, + data: &[u8], + ) -> Result> { + let module = ::wasmi::Module::from_buffer(code).expect("all modules compiled with rustc are valid wasm code; qed"); + self.call_in_wasm_module(ext, heap_pages, &module, method, data) + } + + /// Call a given method in the given wasm-module runtime. + pub fn call_in_wasm_module>( + &self, + ext: &mut E, + heap_pages: usize, + module: &Module, + method: &str, + data: &[u8], + ) -> Result> { + // start module instantiation. Don't run 'start' function yet. + let intermediate_instance = ModuleInstance::new( + module, + &ImportsBuilder::new() + .with_resolver("env", FunctionExecutor::::resolver()) + )?; + + // extract a reference to a linear memory, optional reference to a table + // and then initialize FunctionExecutor. + let memory = intermediate_instance + .not_started_instance() + .export_by_name("memory") + // TODO: with code coming from the blockchain it isn't strictly been compiled with rustc anymore. + // these assumptions are probably not true anymore + .expect("all modules compiled with rustc should have an export named 'memory'; qed") + .as_memory() + .expect("in module generated by rustc export named 'memory' should be a memory; qed") + .clone(); + let table: Option = intermediate_instance + .not_started_instance() + .export_by_name("__indirect_function_table") + .and_then(|e| e.as_table().cloned()); + + let mut fec = FunctionExecutor::new(memory.clone(), heap_pages, table, ext)?; + + // finish instantiation by running 'start' function (if any). + let instance = intermediate_instance.run_start(&mut fec)?; + + let size = data.len() as u32; + let offset = fec.heap.allocate(size); + memory.set(offset, &data)?; + + let result = instance.invoke_export( + method, + &[ + I32(offset as i32), + I32(size as i32) + ], + &mut fec + ); + + let returned = match result { + Ok(x) => x, + Err(e) => { + trace!(target: "wasm-executor", "Failed to execute code with {} pages", heap_pages); + return Err(e.into()) + }, + }; + + if let Some(I64(r)) = returned { + let offset = r as u32; + let length = (r >> 32) as u32 as usize; + memory.get(offset, length) + .map_err(|_| ErrorKind::Runtime.into()) + } else { + Err(ErrorKind::InvalidReturn.into()) + } + } +} + + +#[cfg(test)] +mod tests { + use super::*; + use codec::Encode; + use state_machine::TestExternalities; + + // TODO: move into own crate. + macro_rules! map { + ($( $name:expr => $value:expr ),*) => ( + vec![ $( ( $name, $value ) ),* ].into_iter().collect() + ) + } + + #[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 output = WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_empty_return", &[]).unwrap(); + assert_eq!(output, vec![0u8; 0]); + } + + #[test] + fn panicking_should_work() { + let mut ext = TestExternalities::default(); + let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + + let output = WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_panic", &[]); + assert!(output.is_err()); + + let output = WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_conditional_panic", &[2]); + assert!(output.is_err()); + } + + #[test] + fn storage_should_work() { + let mut ext = TestExternalities::default(); + ext.set_storage(b"foo".to_vec(), b"bar".to_vec()); + let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + + let output = WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_data_in", b"Hello world").unwrap(); + + assert_eq!(output, b"all ok!".to_vec()); + + let expected : TestExternalities<_> = map![ + b"input".to_vec() => b"Hello world".to_vec(), + b"foo".to_vec() => b"bar".to_vec(), + b"baz".to_vec() => b"bar".to_vec() + ]; + assert_eq!(expected, ext); + } + + #[test] + fn clear_prefix_should_work() { + let mut ext = TestExternalities::default(); + ext.set_storage(b"aaa".to_vec(), b"1".to_vec()); + ext.set_storage(b"aab".to_vec(), b"2".to_vec()); + ext.set_storage(b"aba".to_vec(), b"3".to_vec()); + ext.set_storage(b"abb".to_vec(), b"4".to_vec()); + ext.set_storage(b"bbb".to_vec(), b"5".to_vec()); + let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + + // This will clear all entries which prefix is "ab". + let output = WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_clear_prefix", b"ab").unwrap(); + + assert_eq!(output, b"all ok!".to_vec()); + + let expected: TestExternalities<_> = map![ + b"aaa".to_vec() => b"1".to_vec(), + b"aab".to_vec() => b"2".to_vec(), + b"bbb".to_vec() => b"5".to_vec() + ]; + assert_eq!(expected, ext); + } + + #[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"); + assert_eq!( + WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_blake2_256", &[]).unwrap(), + blake2_256(&b""[..]).encode() + ); + assert_eq!( + WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_blake2_256", b"Hello world!").unwrap(), + blake2_256(&b"Hello world!"[..]).encode() + ); + } + + #[test] + fn twox_256_should_work() { + let mut ext = TestExternalities::default(); + let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + assert_eq!( + WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_twox_256", &[]).unwrap(), + hex!("99e9d85137db46ef4bbea33613baafd56f963c64b1f3685a4eb4abd67ff6203a") + ); + assert_eq!( + WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_twox_256", b"Hello world!").unwrap(), + hex!("b27dfd7f223f177f2a13647b533599af0c07f68bda23d96d059da2b451a35a74") + ); + } + + #[test] + fn twox_128_should_work() { + let mut ext = TestExternalities::default(); + let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + assert_eq!( + WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_twox_128", &[]).unwrap(), + hex!("99e9d85137db46ef4bbea33613baafd5") + ); + assert_eq!( + WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_twox_128", b"Hello world!").unwrap(), + hex!("b27dfd7f223f177f2a13647b533599af") + ); + } + + #[test] + fn ed25519_verify_should_work() { + let mut ext = TestExternalities::default(); + let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + let key = ed25519::Pair::from_seed(&blake2_256(b"test")); + let sig = key.sign(b"all ok!"); + let mut calldata = vec![]; + calldata.extend_from_slice(key.public().as_ref()); + calldata.extend_from_slice(sig.as_ref()); + + assert_eq!( + WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_ed25519_verify", &calldata).unwrap(), + vec![1] + ); + + let other_sig = key.sign(b"all is not ok!"); + let mut calldata = vec![]; + calldata.extend_from_slice(key.public().as_ref()); + calldata.extend_from_slice(other_sig.as_ref()); + + assert_eq!( + WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_ed25519_verify", &calldata).unwrap(), + vec![0] + ); + } + + #[test] + fn 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"); + 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()]).0.encode() + ); + } + + +} diff --git a/core/executor/src/wasm_utils.rs b/core/executor/src/wasm_utils.rs new file mode 100644 index 0000000000000..5459266ba46c0 --- /dev/null +++ b/core/executor/src/wasm_utils.rs @@ -0,0 +1,199 @@ +// Copyright 2017 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 . + +//! Rust implementation of Substrate contracts. + +use wasmi::{ValueType, RuntimeValue, HostError}; +use wasmi::nan_preserving_float::{F32, F64}; +use std::fmt; + +#[derive(Debug)] +pub struct UserError(pub &'static str); +impl fmt::Display for UserError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "UserError: {}", self.0) + } +} +impl HostError for UserError { +} + +pub trait ConvertibleToWasm { const VALUE_TYPE: ValueType; type NativeType; fn to_runtime_value(self) -> RuntimeValue; } +impl ConvertibleToWasm for i32 { type NativeType = i32; const VALUE_TYPE: ValueType = ValueType::I32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self) } } +impl ConvertibleToWasm for u32 { type NativeType = u32; const VALUE_TYPE: ValueType = ValueType::I32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self as i32) } } +impl ConvertibleToWasm for i64 { type NativeType = i64; const VALUE_TYPE: ValueType = ValueType::I64; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I64(self) } } +impl ConvertibleToWasm for u64 { type NativeType = u64; const VALUE_TYPE: ValueType = ValueType::I64; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I64(self as i64) } } +impl ConvertibleToWasm for F32 { type NativeType = F32; const VALUE_TYPE: ValueType = ValueType::F32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::F32(self) } } +impl ConvertibleToWasm for F64 { type NativeType = F64; const VALUE_TYPE: ValueType = ValueType::F64; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::F64(self) } } +impl ConvertibleToWasm for isize { type NativeType = i32; const VALUE_TYPE: ValueType = ValueType::I32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self as i32) } } +impl ConvertibleToWasm for usize { type NativeType = u32; const VALUE_TYPE: ValueType = ValueType::I32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self as u32 as i32) } } +impl ConvertibleToWasm for *const T { type NativeType = u32; const VALUE_TYPE: ValueType = ValueType::I32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self as isize as i32) } } +impl ConvertibleToWasm for *mut T { type NativeType = u32; const VALUE_TYPE: ValueType = ValueType::I32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self as isize as i32) } } + +#[macro_export] +macro_rules! convert_args { + () => ([]); + ( $( $t:ty ),* ) => ( [ $( { use $crate::wasm_utils::ConvertibleToWasm; <$t>::VALUE_TYPE }, )* ] ); +} + +#[macro_export] +macro_rules! gen_signature { + ( ( $( $params: ty ),* ) ) => ( + { + $crate::wasmi::Signature::new(&convert_args!($($params),*)[..], None) + } + ); + + ( ( $( $params: ty ),* ) -> $returns: ty ) => ( + { + $crate::wasmi::Signature::new(&convert_args!($($params),*)[..], Some({ + use $crate::wasm_utils::ConvertibleToWasm; <$returns>::VALUE_TYPE + })) + } + ); +} + +macro_rules! resolve_fn { + (@iter $index:expr, $sig_var:ident, $name_var:ident) => (); + (@iter $index:expr, $sig_var:ident, $name_var:ident $name:ident ( $( $params:ty ),* ) $( -> $returns:ty )* => $($tail:tt)* ) => ( + if $name_var == stringify!($name) { + let signature = gen_signature!( ( $( $params ),* ) $( -> $returns )* ); + if $sig_var != &signature { + return Err($crate::wasmi::Error::Instantiation( + format!("Export {} has different signature {:?}", $name_var, $sig_var), + )); + } + return Ok($crate::wasmi::FuncInstance::alloc_host(signature, $index)); + } + resolve_fn!(@iter $index + 1, $sig_var, $name_var $($tail)*) + ); + + ($sig_var:ident, $name_var:ident, $($tail:tt)* ) => ( + resolve_fn!(@iter 0, $sig_var, $name_var $($tail)*); + ); +} + +#[macro_export] +macro_rules! unmarshall_args { + ( $body:tt, $objectname:ident, $args_iter:ident, $( $names:ident : $params:ty ),*) => ({ + $( + let $names : <$params as $crate::wasm_utils::ConvertibleToWasm>::NativeType = + $args_iter.next() + .and_then(|rt_val| rt_val.try_into()) + .expect( + "`$args_iter` comes from an argument of Externals::invoke_index; + args to an external call always matches the signature of the external; + external signatures are built with count and types and in order defined by `$params`; + here, we iterating on `$params`; + qed; + " + ); + )* + $body + }) +} + +/// Since we can't specify the type of closure directly at binding site: +/// +/// ```rust,ignore +/// let f: FnOnce() -> Result<::NativeType, _> = || { /* ... */ }; +/// ``` +/// +/// we use this function to constrain the type of the closure. +#[inline(always)] +pub fn constrain_closure(f: F) -> F +where + F: FnOnce() -> Result +{ + f +} + +#[macro_export] +macro_rules! marshall { + ( $args_iter:ident, $objectname:ident, ( $( $names:ident : $params:ty ),* ) -> $returns:ty => $body:tt ) => ({ + let body = $crate::wasm_utils::constrain_closure::< + <$returns as $crate::wasm_utils::ConvertibleToWasm>::NativeType, _ + >(|| { + unmarshall_args!($body, $objectname, $args_iter, $( $names : $params ),*) + }); + let r = body()?; + return Ok(Some({ use $crate::wasm_utils::ConvertibleToWasm; r.to_runtime_value() })) + }); + ( $args_iter:ident, $objectname:ident, ( $( $names:ident : $params:ty ),* ) => $body:tt ) => ({ + let body = $crate::wasm_utils::constrain_closure::<(), _>(|| { + unmarshall_args!($body, $objectname, $args_iter, $( $names : $params ),*) + }); + body()?; + return Ok(None) + }) +} + +macro_rules! dispatch_fn { + ( @iter $index:expr, $index_ident:ident, $objectname:ident, $args_iter:ident) => { + // `$index` comes from an argument of Externals::invoke_index; + // externals are always invoked with index given by resolve_fn! at resolve time; + // For each next function resolve_fn! gives new index, starting from 0; + // Both dispatch_fn! and resolve_fn! are called with the same list of functions; + // qed; + panic!("fn with index {} is undefined", $index); + }; + + ( @iter $index:expr, $index_ident:ident, $objectname:ident, $args_iter:ident, $name:ident ( $( $names:ident : $params:ty ),* ) $( -> $returns:ty )* => $body:tt $($tail:tt)*) => ( + if $index_ident == $index { + { marshall!($args_iter, $objectname, ( $( $names : $params ),* ) $( -> $returns )* => $body) } + } + dispatch_fn!( @iter $index + 1, $index_ident, $objectname, $args_iter $($tail)*) + ); + + ( $index_ident:ident, $objectname:ident, $args_iter:ident, $($tail:tt)* ) => ( + dispatch_fn!( @iter 0, $index_ident, $objectname, $args_iter, $($tail)*); + ); +} + +#[macro_export] +macro_rules! impl_function_executor { + ( $objectname:ident : $structname:ty, + $( $name:ident ( $( $names:ident : $params:ty ),* ) $( -> $returns:ty )* => $body:tt , )* + => $($pre:tt)+ ) => ( + impl $( $pre ) + $structname { + #[allow(unused)] + fn resolver() -> &'static $crate::wasmi::ModuleImportResolver { + struct Resolver; + impl $crate::wasmi::ModuleImportResolver for Resolver { + fn resolve_func(&self, name: &str, signature: &$crate::wasmi::Signature) -> ::std::result::Result<$crate::wasmi::FuncRef, $crate::wasmi::Error> { + resolve_fn!(signature, name, $( $name( $( $params ),* ) $( -> $returns )* => )*); + + Err($crate::wasmi::Error::Instantiation( + format!("Export {} not found", name), + )) + } + } + &Resolver + } + } + + impl $( $pre ) + $crate::wasmi::Externals for $structname { + fn invoke_index( + &mut self, + index: usize, + args: $crate::wasmi::RuntimeArgs, + ) -> ::std::result::Result, $crate::wasmi::Trap> { + let $objectname = self; + let mut args = args.as_ref().iter(); + dispatch_fn!(index, $objectname, args, $( $name( $( $names : $params ),* ) $( -> $returns )* => $body ),*); + } + } + ); +} diff --git a/core/executor/wasm/Cargo.lock b/core/executor/wasm/Cargo.lock new file mode 100644 index 0000000000000..ed26df817ed7b --- /dev/null +++ b/core/executor/wasm/Cargo.lock @@ -0,0 +1,216 @@ +[[package]] +name = "arrayvec" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "byteorder" +version = "1.2.1" +source = "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 = "fixed-hash" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "hashdb" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "nodrop" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "plain_hasher" +version = "0.2.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)", +] + +[[package]] +name = "proc-macro2" +version = "0.4.9" +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 = "pwasm-alloc" +version = "0.1.0" +dependencies = [ + "pwasm-libc 0.1.0", + "rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pwasm-libc" +version = "0.1.0" + +[[package]] +name = "quote" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "runtime-test" +version = "0.1.0" +dependencies = [ + "substrate-primitives 0.1.0", + "substrate-runtime-io 0.1.0", + "substrate-runtime-sandbox 0.1.0", +] + +[[package]] +name = "rustc-hex" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc_version" +version = "0.2.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 = "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-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "substrate-codec" +version = "0.1.0" +dependencies = [ + "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "substrate-codec-derive" +version = "0.1.0" +dependencies = [ + "proc-macro2 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.14.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "substrate-primitives" +version = "0.1.0" +dependencies = [ + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "fixed-hash 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hashdb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "plain_hasher 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hex 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-codec-derive 0.1.0", + "substrate-runtime-std 0.1.0", + "uint 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "substrate-runtime-io" +version = "0.1.0" +dependencies = [ + "hashdb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-std 0.1.0", +] + +[[package]] +name = "substrate-runtime-sandbox" +version = "0.1.0" +dependencies = [ + "rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-io 0.1.0", + "substrate-runtime-std 0.1.0", +] + +[[package]] +name = "substrate-runtime-std" +version = "0.1.0" +dependencies = [ + "pwasm-alloc 0.1.0", + "pwasm-libc 0.1.0", + "rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syn" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.5 (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 = "uint" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hex 2.0.0 (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.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" +"checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23" +"checksum crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" +"checksum fixed-hash 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0d5ec8112f00ea8a483e04748a85522184418fd1cf02890b626d8fc28683f7de" +"checksum hashdb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f1c71fc577cde89b3345d5f2880fecaf462a32e96c619f431279bdaf1ba5ddb1" +"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" +"checksum plain_hasher 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "95fa6386b1d34aaf0adb9b7dd2885dbe7c34190e6263785e5a7ec2b19044a90f" +"checksum proc-macro2 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "cccdc7557a98fe98453030f077df7f3a042052fae465bb61d2c2c41435cfd9b6" +"checksum quote 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3372dc35766b36a99ce2352bd1b6ea0137c38d215cc0c8780bf6de6df7842ba9" +"checksum rustc-hex 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d2b03280c2813907a030785570c577fb27d3deec8da4c18566751ade94de0ace" +"checksum rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9743a7670d88d5d52950408ecdb7c71d8986251ab604d4689dd2ca25c9bca69" +"checksum semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537" +"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +"checksum serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "db99f3919e20faa51bb2996057f5031d8685019b5a06139b1ce761da671b8526" +"checksum syn 0.14.7 (registry+https://github.com/rust-lang/crates.io-index)" = "e2e13df71f29f9440b50261a5882c86eac334f1badb3134ec26f0de2f1418e44" +"checksum uint 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "754ba11732b9161b94c41798e5197e5e75388d012f760c42adb5000353e98646" +"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" diff --git a/core/executor/wasm/Cargo.toml b/core/executor/wasm/Cargo.toml new file mode 100644 index 0000000000000..44e0dafe5d995 --- /dev/null +++ b/core/executor/wasm/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "runtime-test" +version = "0.1.0" +authors = ["Parity Technologies "] + +[lib] +crate-type = ["cdylib"] + +[dependencies] +substrate-runtime-io = { path = "../../runtime-io", version = "0.1", default_features = false } +substrate-runtime-sandbox = { path = "../../runtime-sandbox", version = "0.1", default_features = false } +substrate-primitives = { path = "../../primitives", default_features = false } + +[profile.release] +panic = "abort" +lto = true + +[workspace] +members = [] diff --git a/core/executor/wasm/build.sh b/core/executor/wasm/build.sh new file mode 100755 index 0000000000000..ab71864481782 --- /dev/null +++ b/core/executor/wasm/build.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -e + +cargo +nightly 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/executor/wasm/src/lib.rs b/core/executor/wasm/src/lib.rs new file mode 100644 index 0000000000000..3046fd1f166ca --- /dev/null +++ b/core/executor/wasm/src/lib.rs @@ -0,0 +1,122 @@ +#![no_std] +#![feature(panic_handler)] +#![cfg_attr(feature = "strict", deny(warnings))] + +#![feature(alloc)] +extern crate alloc; +use alloc::vec::Vec; + +#[macro_use] +extern crate substrate_runtime_io as runtime_io; +extern crate substrate_runtime_sandbox as sandbox; +extern crate substrate_primitives; + +use runtime_io::{ + set_storage, storage, clear_prefix, print, blake2_256, + twox_128, twox_256, ed25519_verify, enumerated_trie_root +}; + +impl_stubs!( + test_data_in NO_DECODE => |input| { + print("set_storage"); + set_storage(b"input", input); + + print("storage"); + let foo = storage(b"foo").unwrap(); + + print("set_storage"); + set_storage(b"baz", &foo); + + print("finished!"); + b"all ok!".to_vec() + }, + test_clear_prefix NO_DECODE => |input| { + clear_prefix(input); + b"all ok!".to_vec() + }, + test_empty_return NO_DECODE => |_| Vec::new(), + test_panic NO_DECODE => |_| panic!("test panic"), + test_conditional_panic NO_DECODE => |input: &[u8]| { + if input.len() > 0 { + panic!("test panic") + } + input.to_vec() + }, + test_blake2_256 NO_DECODE => |input| blake2_256(input).to_vec(), + test_twox_256 NO_DECODE => |input| twox_256(input).to_vec(), + test_twox_128 NO_DECODE => |input| twox_128(input).to_vec(), + test_ed25519_verify NO_DECODE => |input: &[u8]| { + let mut pubkey = [0; 32]; + let mut sig = [0; 64]; + + pubkey.copy_from_slice(&input[0..32]); + sig.copy_from_slice(&input[32..96]); + + let msg = b"all ok!"; + [ed25519_verify(&sig, &msg[..], &pubkey) as u8].to_vec() + }, + test_enumerated_trie_root NO_DECODE => |_| { + enumerated_trie_root::(&[&b"zero"[..], &b"one"[..], &b"two"[..]]).to_vec() + }, + test_sandbox NO_DECODE => |code: &[u8]| { + let ok = execute_sandboxed(code, &[]).is_ok(); + [ok as u8].to_vec() + }, + test_sandbox_args NO_DECODE => |code: &[u8]| { + let ok = execute_sandboxed( + code, + &[ + sandbox::TypedValue::I32(0x12345678), + sandbox::TypedValue::I64(0x1234567887654321), + ] + ).is_ok(); + [ok as u8].to_vec() + }, + test_sandbox_return_val NO_DECODE => |code: &[u8]| { + let result = execute_sandboxed( + code, + &[ + sandbox::TypedValue::I32(0x1336), + ] + ); + let ok = if let Ok(sandbox::ReturnValue::Value(sandbox::TypedValue::I32(0x1337))) = result { true } else { false }; + [ok as u8].to_vec() + } +); + +fn execute_sandboxed(code: &[u8], args: &[sandbox::TypedValue]) -> Result { + struct State { + counter: u32, + } + + fn env_assert(_e: &mut State, args: &[sandbox::TypedValue]) -> Result { + if args.len() != 1 { + return Err(sandbox::HostError); + } + let condition = args[0].as_i32().ok_or_else(|| sandbox::HostError)?; + if condition != 0 { + Ok(sandbox::ReturnValue::Unit) + } else { + Err(sandbox::HostError) + } + } + fn env_inc_counter(e: &mut State, args: &[sandbox::TypedValue]) -> Result { + if args.len() != 1 { + return Err(sandbox::HostError); + } + let inc_by = args[0].as_i32().ok_or_else(|| sandbox::HostError)?; + e.counter += inc_by as u32; + Ok(sandbox::ReturnValue::Value(sandbox::TypedValue::I32(e.counter as i32))) + } + + let mut state = State { counter: 0 }; + + let mut env_builder = sandbox::EnvironmentDefinitionBuilder::new(); + env_builder.add_host_func("env", "assert", env_assert); + env_builder.add_host_func("env", "inc_counter", env_inc_counter); + + let mut instance = sandbox::Instance::new(code, &env_builder, &mut state)?; + let result = instance.invoke(b"call", args, &mut state); + + result.map_err(|_| sandbox::HostError) +} diff --git a/core/extrinsic-pool/Cargo.toml b/core/extrinsic-pool/Cargo.toml new file mode 100644 index 0000000000000..8fc4f49186556 --- /dev/null +++ b/core/extrinsic-pool/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "substrate-extrinsic-pool" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +serde = "1.0" +serde_derive = "1.0" +error-chain = "0.12" +futures = "0.1" +log = "0.3" +parking_lot = "0.4" +transaction-pool = "1.13.2" +substrate-runtime-primitives = { path = "../../runtime/primitives" } + +[dev-dependencies] +substrate-test-client = { path = "../test-client" } +substrate-keyring = { path = "../keyring" } +substrate-codec = { path = "../codec" } diff --git a/core/extrinsic-pool/README.adoc b/core/extrinsic-pool/README.adoc new file mode 100644 index 0000000000000..e128d64c9bf8b --- /dev/null +++ b/core/extrinsic-pool/README.adoc @@ -0,0 +1,13 @@ + += extrinsic-pool + +.Summary +[source, toml] +---- +include::Cargo.toml[lines=2..5] +---- + +.Description +---- +include::src/lib.rs[tag=description] +---- diff --git a/core/extrinsic-pool/src/error.rs b/core/extrinsic-pool/src/error.rs new file mode 100644 index 0000000000000..047041d18d560 --- /dev/null +++ b/core/extrinsic-pool/src/error.rs @@ -0,0 +1,33 @@ +// Copyright 2018 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 . + +//! External Error trait for extrinsic pool. + +use txpool; + +/// Extrinsic pool error. +pub trait IntoPoolError: ::std::error::Error + Send + Sized { + /// Try to extract original `txpool::Error` + /// + /// This implementation is optional and used only to + /// provide more descriptive error messages for end users + /// of RPC API. + fn into_pool_error(self) -> Result { Err(self) } +} + +impl IntoPoolError for txpool::Error { + fn into_pool_error(self) -> Result { Ok(self) } +} diff --git a/core/extrinsic-pool/src/lib.rs b/core/extrinsic-pool/src/lib.rs new file mode 100644 index 0000000000000..bcd2b03c80e00 --- /dev/null +++ b/core/extrinsic-pool/src/lib.rs @@ -0,0 +1,49 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +// tag::description[] +//! Generic extrinsic pool. +// end::description[] + +#![warn(missing_docs)] +#![warn(unused_extern_crates)] + +extern crate futures; +extern crate parking_lot; +extern crate substrate_runtime_primitives as runtime_primitives; + +#[macro_use] +extern crate log; +extern crate serde; +#[macro_use] +extern crate serde_derive; +extern crate transaction_pool as txpool; +#[cfg(test)] extern crate substrate_test_client as test_client; +#[cfg(test)] extern crate substrate_keyring as keyring; +#[cfg(test)] extern crate substrate_codec as codec; + +pub mod watcher; +mod error; +mod listener; +mod pool; +mod rotator; + +pub use listener::Listener; +pub use pool::{Pool, ChainApi, EventStream, Verified, VerifiedFor, ExtrinsicFor, ExHash, AllExtrinsics}; +pub use txpool::scoring; +pub use txpool::{Error, ErrorKind}; +pub use error::IntoPoolError; +pub use txpool::{Options, Status, LightStatus, VerifiedTransaction, Readiness, Transaction}; diff --git a/core/extrinsic-pool/src/listener.rs b/core/extrinsic-pool/src/listener.rs new file mode 100644 index 0000000000000..8badb331a5b64 --- /dev/null +++ b/core/extrinsic-pool/src/listener.rs @@ -0,0 +1,95 @@ +// Copyright 2018 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, + fmt, + collections::HashMap, +}; +use txpool; + +use watcher; + +/// Extrinsic pool default listener. +#[derive(Default)] +pub struct Listener { + watchers: HashMap> +} + +impl Listener { + /// Creates a new watcher for given verified extrinsic. + /// + /// The watcher can be used to subscribe to lifecycle events of that extrinsic. + pub fn create_watcher>(&mut self, xt: Arc) -> watcher::Watcher { + let sender = self.watchers.entry(*xt.hash()).or_insert_with(watcher::Sender::default); + sender.new_watcher() + } + + /// Notify the listeners about extrinsic broadcast. + pub fn broadcasted(&mut self, hash: &H, peers: Vec) { + self.fire(hash, |watcher| watcher.broadcast(peers)); + } + + fn fire(&mut self, hash: &H, fun: F) where F: FnOnce(&mut watcher::Sender) { + let clean = if let Some(h) = self.watchers.get_mut(hash) { + fun(h); + h.is_done() + } else { + false + }; + + if clean { + self.watchers.remove(hash); + } + } +} + +impl txpool::Listener for Listener where + H: ::std::hash::Hash + Eq + Copy + fmt::Debug + fmt::LowerHex + Default, + T: txpool::VerifiedTransaction, +{ + fn added(&mut self, tx: &Arc, old: Option<&Arc>) { + if let Some(old) = old { + let hash = tx.hash(); + self.fire(old.hash(), |watcher| watcher.usurped(*hash)); + } + } + + fn dropped(&mut self, tx: &Arc, by: Option<&T>) { + self.fire(tx.hash(), |watcher| match by { + Some(t) => watcher.usurped(*t.hash()), + None => watcher.dropped(), + }) + } + + fn rejected(&mut self, tx: &Arc, reason: &txpool::ErrorKind) { + warn!(target: "extrinsic-pool", "Extrinsic rejected ({}): {:?}", reason, tx); + } + + fn invalid(&mut self, tx: &Arc) { + warn!(target: "extrinsic-pool", "Extrinsic invalid: {:?}", tx); + } + + fn canceled(&mut self, tx: &Arc) { + debug!(target: "extrinsic-pool", "Extrinsic canceled: {:?}", tx); + } + + fn culled(&mut self, tx: &Arc) { + // TODO [ToDr] latest block number? + let header_hash = Default::default(); + self.fire(tx.hash(), |watcher| watcher.finalised(header_hash)) + } +} diff --git a/core/extrinsic-pool/src/pool.rs b/core/extrinsic-pool/src/pool.rs new file mode 100644 index 0000000000000..642171b376015 --- /dev/null +++ b/core/extrinsic-pool/src/pool.rs @@ -0,0 +1,606 @@ +// Copyright 2018 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::{ + collections::{BTreeMap, HashMap}, + fmt, + sync::Arc, + time, +}; +use futures::sync::mpsc; +use parking_lot::{Mutex, RwLock}; +use serde::{Serialize, de::DeserializeOwned}; +use txpool::{self, Scoring, Readiness}; + +use error::IntoPoolError; +use listener::Listener; +use rotator::PoolRotator; +use watcher::Watcher; + +use runtime_primitives::{generic::BlockId, traits::Block as BlockT}; + +/// Modification notification event stream type; +pub type EventStream = mpsc::UnboundedReceiver<()>; + +/// Extrinsic hash type for a pool. +pub type ExHash = ::Hash; +/// Extrinsic type for a pool. +pub type ExtrinsicFor = <::Block as BlockT>::Extrinsic; +/// Verified extrinsic data for `ChainApi`. +pub type VerifiedFor = Verified, ::VEx>; +/// A collection of all extrinsics. +pub type AllExtrinsics = BTreeMap<<::VEx as txpool::VerifiedTransaction>::Sender, Vec>>; + +/// Verified extrinsic struct. Wraps original extrinsic and verification info. +#[derive(Debug)] +pub struct Verified { + /// Original extrinsic. + pub original: Ex, + /// Verification data. + pub verified: VEx, + /// Pool deadline, after it's reached we remove the extrinsic from the pool. + pub valid_till: time::Instant, +} + +impl txpool::VerifiedTransaction for Verified +where + Ex: fmt::Debug, + VEx: txpool::VerifiedTransaction, +{ + type Hash = ::Hash; + type Sender = ::Sender; + + fn hash(&self) -> &Self::Hash { + self.verified.hash() + } + + fn sender(&self) -> &Self::Sender { + self.verified.sender() + } + + fn mem_usage(&self) -> usize { + // TODO: add `original` mem usage. + self.verified.mem_usage() + } +} + +/// Concrete extrinsic validation and query logic. +pub trait ChainApi: Send + Sync { + /// Block type. + type Block: BlockT; + /// Extrinsic hash type. + type Hash: ::std::hash::Hash + Eq + Copy + fmt::Debug + fmt::LowerHex + Serialize + DeserializeOwned + ::std::str::FromStr + Send + Sync + Default + 'static; + /// Extrinsic sender type. + type Sender: ::std::hash::Hash + fmt::Debug + Serialize + DeserializeOwned + Eq + Clone + Send + Sync + Ord + Default; + /// Unchecked extrinsic type. + /// Verified extrinsic type. + type VEx: txpool::VerifiedTransaction + Send + Sync + Clone; + /// Readiness evaluator + type Ready; + /// Error type. + type Error: From + IntoPoolError; + /// Score type. + type Score: ::std::cmp::Ord + Clone + Default + fmt::Debug + Send + Send + Sync + fmt::LowerHex; + /// Custom scoring update event type. + type Event: ::std::fmt::Debug; + /// Verify extrinsic at given block. + fn verify_transaction(&self, at: &BlockId, uxt: &ExtrinsicFor) -> Result; + + /// Create new readiness evaluator. + fn ready(&self) -> Self::Ready; + + /// Check readiness for verified extrinsic at given block. + fn is_ready(&self, at: &BlockId, context: &mut Self::Ready, xt: &VerifiedFor) -> Readiness; + + /// Decides on ordering of `T`s from a particular sender. + fn compare(old: &VerifiedFor, other: &VerifiedFor) -> ::std::cmp::Ordering; + + /// Decides how to deal with two transactions from a sender that seem to occupy the same slot in the queue. + fn choose(old: &VerifiedFor, new: &VerifiedFor) -> txpool::scoring::Choice; + + /// Updates the transaction scores given a list of transactions and a change to previous scoring. + /// NOTE: you can safely assume that both slices have the same length. + /// (i.e. score at index `i` represents transaction at the same index) + fn update_scores(xts: &[txpool::Transaction>], scores: &mut [Self::Score], change: txpool::scoring::Change); + + /// Decides if `new` should push out `old` transaction from the pool. + /// + /// NOTE returning `InsertNew` here can lead to some transactions being accepted above pool limits. + fn should_replace(old: &VerifiedFor, new: &VerifiedFor) -> txpool::scoring::Choice; +} + +pub struct Ready<'a, 'b, B: 'a + ChainApi> { + api: &'a B, + at: &'b BlockId, + context: B::Ready, + rotator: &'a PoolRotator, + now: time::Instant, +} + +impl<'a, 'b, B: ChainApi> txpool::Ready> for Ready<'a, 'b, B> { + fn is_ready(&mut self, xt: &VerifiedFor) -> Readiness { + if self.rotator.ban_if_stale(&self.now, xt) { + debug!(target: "extrinsic-pool", "[{:?}] Banning as stale.", txpool::VerifiedTransaction::hash(xt)); + return Readiness::Stale; + } + + self.api.is_ready(self.at, &mut self.context, xt) + } +} + +pub struct ScoringAdapter(::std::marker::PhantomData); + +impl ::std::fmt::Debug for ScoringAdapter { + fn fmt(&self, _f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + Ok(()) + } +} + +impl Scoring> for ScoringAdapter { + type Score = ::Score; + type Event = ::Event; + + fn compare(&self, old: &VerifiedFor, other: &VerifiedFor) -> ::std::cmp::Ordering { + T::compare(old, other) + } + + fn choose(&self, old: &VerifiedFor, new: &VerifiedFor) -> txpool::scoring::Choice { + T::choose(old, new) + } + + fn update_scores(&self, xts: &[txpool::Transaction>], scores: &mut [Self::Score], change: txpool::scoring::Change) { + T::update_scores(xts, scores, change) + } + + fn should_replace(&self, old: &VerifiedFor, new: &VerifiedFor) -> txpool::scoring::Choice { + T::should_replace(old, new) + } +} + +/// Maximum time the transaction will be kept in the pool. +/// +/// Transactions that don't get included within the limit are removed from the pool. +const POOL_TIME: time::Duration = time::Duration::from_secs(60 * 5); + +/// Extrinsics pool. +pub struct Pool { + api: B, + pool: RwLock, + ScoringAdapter, + Listener, + >>, + import_notification_sinks: Mutex>>, + rotator: PoolRotator, +} + +impl Pool { + /// Create a new transaction pool. + pub fn new(options: txpool::Options, api: B) -> Self { + Pool { + pool: RwLock::new(txpool::Pool::new(Listener::default(), ScoringAdapter::(Default::default()), options)), + import_notification_sinks: Default::default(), + api, + rotator: Default::default(), + } + } + + /// Imports a pre-verified extrinsic to the pool. + pub fn import(&self, xt: VerifiedFor) -> Result>, B::Error> { + let result = self.pool.write().import(xt)?; + + self.import_notification_sinks.lock() + .retain(|sink| sink.unbounded_send(()).is_ok()); + + Ok(result) + } + + /// Return an event stream of transactions imported to the pool. + pub fn import_notification_stream(&self) -> EventStream { + let (sink, stream) = mpsc::unbounded(); + self.import_notification_sinks.lock().push(sink); + stream + } + + /// Invoked when extrinsics are broadcasted. + pub fn on_broadcasted(&self, propagated: HashMap>) { + for (hash, peers) in propagated.into_iter() { + self.pool.write().listener_mut().broadcasted(&hash, peers); + } + } + + /// Imports a bunch of unverified extrinsics to the pool + pub fn submit_at(&self, at: &BlockId, xts: T) -> Result>>, B::Error> where + T: IntoIterator> + { + xts + .into_iter() + .map(|xt| { + match self.api.verify_transaction(at, &xt) { + Ok(ref verified) if self.rotator.is_banned(txpool::VerifiedTransaction::hash(verified)) => { + return (Err(txpool::Error::from("Temporarily Banned".to_owned()).into()), xt) + }, + result => (result, xt), + } + }) + .map(|(v, xt)| { + let xt = Verified { + original: xt, + verified: v?, + valid_till: time::Instant::now() + POOL_TIME, + }; + Ok(self.pool.write().import(xt)?) + }) + .collect() + } + + /// Imports one unverified extrinsic to the pool + pub fn submit_one(&self, at: &BlockId, xt: ExtrinsicFor) -> Result>, B::Error> { + Ok(self.submit_at(at, ::std::iter::once(xt))?.pop().expect("One extrinsic passed; one result returned; qed")) + } + + /// Import a single extrinsic and starts to watch their progress in the pool. + pub fn submit_and_watch(&self, at: &BlockId, xt: ExtrinsicFor) -> Result, B::Error> { + let xt = self.submit_at(at, Some(xt))?.pop().expect("One extrinsic passed; one result returned; qed"); + Ok(self.pool.write().listener_mut().create_watcher(xt)) + } + + /// Remove from the pool. + pub fn remove(&self, hashes: &[B::Hash], is_valid: bool) -> Vec>>> { + let mut pool = self.pool.write(); + let mut results = Vec::with_capacity(hashes.len()); + + // temporarily ban invalid transactions + if !is_valid { + debug!(target: "transaction-pool", "Banning invalid transactions: {:?}", hashes); + self.rotator.ban(&time::Instant::now(), hashes); + } + + for hash in hashes { + results.push(pool.remove(hash, is_valid)); + } + + results + } + + /// Cull transactions from the queue. + pub fn cull_from( + &self, + at: &BlockId, + senders: Option<&[::Sender]>, + ) -> usize + { + self.rotator.clear_timeouts(&time::Instant::now()); + let ready = self.ready(at); + self.pool.write().cull(senders, ready) + } + + /// Cull old transactions from the queue. + pub fn cull(&self, at: &BlockId) -> Result { + Ok(self.cull_from(at, None)) + } + + /// Cull transactions from the queue and then compute the pending set. + pub fn cull_and_get_pending(&self, at: &BlockId, f: F) -> Result where + F: FnOnce(txpool::PendingIterator, Ready, ScoringAdapter, Listener>) -> T, + { + self.cull_from(at, None); + Ok(self.pending(at, f)) + } + + /// Get the full status of the queue (including readiness) + pub fn status>>(&self, ready: R) -> txpool::Status { + self.pool.read().status(ready) + } + + /// Returns light status of the pool. + pub fn light_status(&self) -> txpool::LightStatus { + self.pool.read().light_status() + } + + /// Removes all transactions from given sender + pub fn remove_sender(&self, sender: ::Sender) -> Vec>> { + let mut pool = self.pool.write(); + let pending = pool.pending_from_sender(|_: &VerifiedFor| txpool::Readiness::Ready, &sender).collect(); + // remove all transactions from this sender + pool.cull(Some(&[sender]), |_: &VerifiedFor| txpool::Readiness::Stale); + pending + } + + /// Retrieve the pending set. Be careful to not leak the pool `ReadGuard` to prevent deadlocks. + pub fn pending(&self, at: &BlockId, f: F) -> T where + F: FnOnce(txpool::PendingIterator, Ready, ScoringAdapter, Listener>) -> T, + { + let ready = self.ready(at); + f(self.pool.read().pending(ready)) + } + + /// Retry to import all verified transactions from given sender. + pub fn retry_verification(&self, at: &BlockId, sender: ::Sender) -> Result<(), B::Error> { + let to_reverify = self.remove_sender(sender); + self.submit_at(at, to_reverify.into_iter().map(|ex| Arc::try_unwrap(ex).expect("Removed items have no references").original))?; + Ok(()) + } + + /// Reverify transaction that has been reported incorrect. + /// + /// Returns `Ok(None)` in case the hash is missing, `Err(e)` in case of verification error and new transaction + /// reference otherwise. + /// + /// TODO [ToDr] That method is currently unused, should be used together with BlockBuilder + /// when we detect that particular transaction has failed. + /// In such case we will attempt to remove or re-verify it. + pub fn reverify_transaction(&self, at: &BlockId, hash: B::Hash) -> Result>>, B::Error> { + let result = self.remove(&[hash], false).pop().expect("One hash passed; one result received; qed"); + if let Some(ex) = result { + self.submit_one(at, Arc::try_unwrap(ex).expect("Removed items have no references").original).map(Some) + } else { + Ok(None) + } + } + + /// Retrieve all transactions in the pool grouped by sender. + pub fn all(&self) -> AllExtrinsics { + use txpool::VerifiedTransaction; + let pool = self.pool.read(); + let all = pool.unordered_pending(AlwaysReady); + all.fold(Default::default(), |mut map: AllExtrinsics, tx| { + // Map with `null` key is not serializable, so we fallback to default accountId. + map.entry(tx.verified.sender().clone()) + .or_insert_with(Vec::new) + // use bytes type to make it serialize nicer. + .push(tx.original.clone()); + map + }) + } + + fn ready<'a, 'b>(&'a self, at: &'b BlockId) -> Ready<'a, 'b, B> { + Ready { + api: &self.api, + rotator: &self.rotator, + context: self.api.ready(), + at, + now: time::Instant::now(), + } + } +} + + /// A Readiness implementation that returns `Ready` for all transactions. +pub struct AlwaysReady; +impl txpool::Ready for AlwaysReady { + fn is_ready(&mut self, _tx: &VEx) -> txpool::Readiness { + txpool::Readiness::Ready + } +} + +#[cfg(test)] +pub mod tests { + use txpool; + use super::{VerifiedFor, ExtrinsicFor}; + use std::collections::HashMap; + use std::cmp::Ordering; + use {Pool, ChainApi, scoring, Readiness}; + use keyring::Keyring::{self, *}; + use codec::Encode; + use test_client::runtime::{AccountId, Block, Hash, Index, Extrinsic, Transfer}; + use runtime_primitives::{generic, traits::{Hash as HashT, BlindCheckable, BlakeTwo256}}; + use VerifiedTransaction as VerifiedExtrinsic; + + type BlockId = generic::BlockId; + + #[derive(Clone, Debug)] + pub struct VerifiedTransaction { + pub hash: Hash, + pub sender: AccountId, + pub nonce: u64, + } + + impl txpool::VerifiedTransaction for VerifiedTransaction { + type Hash = Hash; + type Sender = AccountId; + + fn hash(&self) -> &Self::Hash { + &self.hash + } + + fn sender(&self) -> &Self::Sender { + &self.sender + } + + fn mem_usage(&self) -> usize { + 256 + } + } + + struct TestApi; + + impl TestApi { + fn default() -> Self { + TestApi + } + } + + impl ChainApi for TestApi { + type Block = Block; + type Hash = Hash; + type Sender = AccountId; + type Error = txpool::Error; + type VEx = VerifiedTransaction; + type Ready = HashMap; + type Score = u64; + type Event = (); + + fn verify_transaction(&self, _at: &BlockId, uxt: &ExtrinsicFor) -> Result { + let hash = BlakeTwo256::hash(&uxt.encode()); + let xt = uxt.clone().check()?; + Ok(VerifiedTransaction { + hash, + sender: xt.transfer.from, + nonce: xt.transfer.nonce, + }) + } + + fn is_ready(&self, at: &BlockId, nonce_cache: &mut Self::Ready, xt: &VerifiedFor) -> Readiness { + let sender = xt.verified.sender; + let next_index = nonce_cache.entry(sender) + .or_insert_with(|| index(at, sender)); + + let result = match xt.original.transfer.nonce.cmp(&next_index) { + Ordering::Greater => Readiness::Future, + Ordering::Equal => Readiness::Ready, + Ordering::Less => Readiness::Stale, + }; + + // remember to increment `next_index` + *next_index = next_index.saturating_add(1); + + result + } + + fn ready(&self) -> Self::Ready { + HashMap::default() + } + + fn compare(old: &VerifiedFor, other: &VerifiedFor) -> Ordering { + old.original.transfer.nonce.cmp(&other.original.transfer.nonce) + } + + fn choose(old: &VerifiedFor, new: &VerifiedFor) -> scoring::Choice { + assert!(new.verified.sender == old.verified.sender, "Scoring::choose called with transactions from different senders"); + if old.original.transfer.nonce == new.original.transfer.nonce { + return scoring::Choice::RejectNew; + } + scoring::Choice::InsertNew + } + + fn update_scores( + xts: &[txpool::Transaction>], + scores: &mut [Self::Score], + _change: scoring::Change<()> + ) { + for i in 0..xts.len() { + scores[i] = xts[i].original.transfer.amount; + } + } + + fn should_replace(_old: &VerifiedFor, _new: &VerifiedFor) -> scoring::Choice { + scoring::Choice::InsertNew + } + } + + fn index(at: &BlockId, _account: AccountId) -> u64 { + (_account[0] as u64) + number_of(at) + } + + fn number_of(at: &BlockId) -> u64 { + match at { + generic::BlockId::Number(n) => *n as u64, + _ => 0, + } + } + + fn uxt(who: Keyring, nonce: Index) -> Extrinsic { + let transfer = Transfer { + from: who.to_raw_public().into(), + to: AccountId::default(), + nonce, + amount: 1, + }; + let signature = transfer.using_encoded(|e| who.sign(e)); + Extrinsic { + transfer, + signature: signature.into(), + } + } + + fn pool() -> Pool { + Pool::new(Default::default(), TestApi::default()) + } + + #[test] + fn submission_should_work() { + let pool = pool(); + assert_eq!(209, index(&BlockId::number(0), Alice.to_raw_public().into())); + pool.submit_one(&BlockId::number(0), uxt(Alice, 209)).unwrap(); + + let pending: Vec<_> = pool.cull_and_get_pending(&BlockId::number(0), |p| p.map(|a| (*a.sender(), a.original.transfer.nonce)).collect()).unwrap(); + assert_eq!(pending, vec![(Alice.to_raw_public().into(), 209)]); + } + + #[test] + fn multiple_submission_should_work() { + let pool = pool(); + pool.submit_one(&BlockId::number(0), uxt(Alice, 209)).unwrap(); + pool.submit_one(&BlockId::number(0), uxt(Alice, 210)).unwrap(); + + let pending: Vec<_> = pool.cull_and_get_pending(&BlockId::number(0), |p| p.map(|a| (*a.sender(), a.original.transfer.nonce)).collect()).unwrap(); + assert_eq!(pending, vec![(Alice.to_raw_public().into(), 209), (Alice.to_raw_public().into(), 210)]); + } + + #[test] + fn early_nonce_should_be_culled() { + let pool = pool(); + pool.submit_one(&BlockId::number(0), uxt(Alice, 208)).unwrap(); + + let pending: Vec<_> = pool.cull_and_get_pending(&BlockId::number(0), |p| p.map(|a| (*a.sender(), a.original.transfer.nonce)).collect()).unwrap(); + assert_eq!(pending, vec![]); + } + + #[test] + fn late_nonce_should_be_queued() { + let pool = pool(); + + pool.submit_one(&BlockId::number(0), uxt(Alice, 210)).unwrap(); + let pending: Vec<_> = pool.cull_and_get_pending(&BlockId::number(0), |p| p.map(|a| (*a.sender(), a.original.transfer.nonce)).collect()).unwrap(); + assert_eq!(pending, vec![]); + + pool.submit_one(&BlockId::number(0), uxt(Alice, 209)).unwrap(); + let pending: Vec<_> = pool.cull_and_get_pending(&BlockId::number(0), |p| p.map(|a| (*a.sender(), a.original.transfer.nonce)).collect()).unwrap(); + assert_eq!(pending, vec![(Alice.to_raw_public().into(), 209), (Alice.to_raw_public().into(), 210)]); + } + + #[test] + fn retrying_verification_might_not_change_anything() { + let pool = pool(); + pool.submit_one(&BlockId::number(0), uxt(Alice, 209)).unwrap(); + pool.submit_one(&BlockId::number(0), uxt(Alice, 210)).unwrap(); + + let pending: Vec<_> = pool.cull_and_get_pending(&BlockId::number(0), |p| p.map(|a| (*a.sender(), a.original.transfer.nonce)).collect()).unwrap(); + assert_eq!(pending, vec![(Alice.to_raw_public().into(), 209), (Alice.to_raw_public().into(), 210)]); + + pool.retry_verification(&BlockId::number(1), Alice.to_raw_public().into()).unwrap(); + + let pending: Vec<_> = pool.cull_and_get_pending(&BlockId::number(0), |p| p.map(|a| (*a.sender(), a.original.transfer.nonce)).collect()).unwrap(); + assert_eq!(pending, vec![(Alice.to_raw_public().into(), 209), (Alice.to_raw_public().into(), 210)]); + } + + #[test] + fn should_ban_invalid_transactions() { + let pool = pool(); + let uxt = uxt(Alice, 209); + let hash = *pool.submit_one(&BlockId::number(0), uxt.clone()).unwrap().hash(); + pool.remove(&[hash], true); + pool.submit_one(&BlockId::number(0), uxt.clone()).unwrap(); + + // when + pool.remove(&[hash], false); + let pending: Vec = pool.cull_and_get_pending(&BlockId::number(0), |p| p.map(|a| *a.sender()).collect()).unwrap(); + assert_eq!(pending, vec![]); + + // then + pool.submit_one(&BlockId::number(0), uxt.clone()).unwrap_err(); + } +} diff --git a/core/extrinsic-pool/src/rotator.rs b/core/extrinsic-pool/src/rotator.rs new file mode 100644 index 0000000000000..93acce9e9130c --- /dev/null +++ b/core/extrinsic-pool/src/rotator.rs @@ -0,0 +1,209 @@ +// Copyright 2018 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 . + +//! Rotate extrinsic inside the pool. +//! +//! Keeps only recent extrinsic and discard the ones kept for a significant amount of time. +//! Discarded extrinsics are banned so that they don't get re-imported again. + +use std::{ + collections::HashMap, + fmt, + hash, + time::{Duration, Instant}, +}; +use parking_lot::RwLock; +use txpool::VerifiedTransaction; +use Verified; + +/// Expected size of the banned extrinsics cache. +const EXPECTED_SIZE: usize = 2048; + +/// Pool rotator is responsible to only keep fresh extrinsics in the pool. +/// +/// Extrinsics that occupy the pool for too long are culled and temporarily banned from entering +/// the pool again. +pub struct PoolRotator { + /// How long the extrinsic is banned for. + ban_time: Duration, + /// Currently banned extrinsics. + banned_until: RwLock>, +} + +impl Default for PoolRotator { + fn default() -> Self { + PoolRotator { + ban_time: Duration::from_secs(60 * 30), + banned_until: Default::default(), + } + } +} + +impl PoolRotator { + /// Returns `true` if extrinsic hash is currently banned. + pub fn is_banned(&self, hash: &Hash) -> bool { + self.banned_until.read().contains_key(hash) + } + + /// Bans given set of hashes. + pub fn ban(&self, now: &Instant, hashes: &[Hash]) { + let mut banned = self.banned_until.write(); + + for hash in hashes { + banned.insert(hash.clone(), *now + self.ban_time); + } + + if banned.len() > 2 * EXPECTED_SIZE { + while banned.len() > EXPECTED_SIZE { + if let Some(key) = banned.keys().next().cloned() { + banned.remove(&key); + } + } + } + } + + /// Bans extrinsic if it's stale. + /// + /// Returns `true` if extrinsic is stale and got banned. + pub fn ban_if_stale(&self, now: &Instant, xt: &Verified) -> bool where + VEx: VerifiedTransaction, + Hash: fmt::Debug + fmt::LowerHex, + { + if &xt.valid_till > now { + return false; + } + + self.ban(now, &[xt.verified.hash().clone()]); + true + } + + /// Removes timed bans. + pub fn clear_timeouts(&self, now: &Instant) { + let mut banned = self.banned_until.write(); + + banned.retain(|_, &mut v| v >= *now); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use pool::tests::VerifiedTransaction; + use test_client::runtime::Hash; + + fn rotator() -> PoolRotator { + PoolRotator { + ban_time: Duration::from_millis(10), + ..Default::default() + } + } + + fn tx() -> (Hash, Verified) { + let hash = 5.into(); + let tx = Verified { + original: 5, + verified: VerifiedTransaction { + hash, + sender: Default::default(), + nonce: Default::default(), + }, + valid_till: Instant::now(), + }; + + (hash, tx) + } + + #[test] + fn should_not_ban_if_not_stale() { + // given + let (hash, tx) = tx(); + let rotator = rotator(); + assert!(!rotator.is_banned(&hash)); + let past = Instant::now() - Duration::from_millis(1000); + + // when + assert!(!rotator.ban_if_stale(&past, &tx)); + + // then + assert!(!rotator.is_banned(&hash)); + } + + #[test] + fn should_ban_stale_extrinsic() { + // given + let (hash, tx) = tx(); + let rotator = rotator(); + assert!(!rotator.is_banned(&hash)); + + // when + assert!(rotator.ban_if_stale(&Instant::now(), &tx)); + + // then + assert!(rotator.is_banned(&hash)); + } + + + #[test] + fn should_clear_banned() { + // given + let (hash, tx) = tx(); + let rotator = rotator(); + assert!(rotator.ban_if_stale(&Instant::now(), &tx)); + assert!(rotator.is_banned(&hash)); + + // when + let future = Instant::now() + rotator.ban_time + rotator.ban_time; + rotator.clear_timeouts(&future); + + // then + assert!(!rotator.is_banned(&hash)); + } + + #[test] + fn should_garbage_collect() { + // given + fn tx_with(i: u64, time: Instant) -> Verified { + let hash = i.into(); + Verified { + original: i, + verified: VerifiedTransaction { + hash, + sender: Default::default(), + nonce: Default::default(), + }, + valid_till: time, + } + } + + let rotator = rotator(); + + let now = Instant::now(); + let past = now - Duration::from_secs(1); + + // when + for i in 0..2*EXPECTED_SIZE { + let tx = tx_with(i as u64, past); + assert!(rotator.ban_if_stale(&now, &tx)); + } + assert_eq!(rotator.banned_until.read().len(), 2*EXPECTED_SIZE); + + // then + let tx = tx_with(2*EXPECTED_SIZE as u64, past); + // trigger a garbage collection + assert!(rotator.ban_if_stale(&now, &tx)); + assert_eq!(rotator.banned_until.read().len(), EXPECTED_SIZE); + } +} diff --git a/core/extrinsic-pool/src/watcher.rs b/core/extrinsic-pool/src/watcher.rs new file mode 100644 index 0000000000000..78b27326f861f --- /dev/null +++ b/core/extrinsic-pool/src/watcher.rs @@ -0,0 +1,103 @@ +// Copyright 2018 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 . + +//! Extrinsics status updates. + +use futures::{ + Stream, + sync::mpsc, +}; + +/// Possible extrinsic status events +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum Status { + /// Extrinsic has been finalised in block with given hash. + Finalised(H), + /// Some state change (perhaps another extrinsic was included) rendered this extrinsic invalid. + Usurped(H), + /// The extrinsic has been broadcast to the given peers. + Broadcast(Vec), + /// Extrinsic has been dropped from the pool because of the limit. + Dropped, +} + +/// Extrinsic watcher. +/// +/// Represents a stream of status updates for particular extrinsic. +#[derive(Debug)] +pub struct Watcher { + receiver: mpsc::UnboundedReceiver>, +} + +impl Watcher { + /// Pipe the notifications to given sink. + /// + /// Make sure to drive the future to completion. + pub fn into_stream(self) -> impl Stream, Error=()> { + // we can safely ignore the error here, `UnboundedReceiver` never fails. + self.receiver.map_err(|_| ()) + } +} + +/// Sender part of the watcher. Exposed only for testing purposes. +#[derive(Debug, Default)] +pub struct Sender { + receivers: Vec>>, + finalised: bool, +} + +impl Sender { + /// Add a new watcher to this sender object. + pub fn new_watcher(&mut self) -> Watcher { + let (tx, receiver) = mpsc::unbounded(); + self.receivers.push(tx); + Watcher { + receiver, + } + } + + /// Some state change (perhaps another extrinsic was included) rendered this extrinsic invalid. + pub fn usurped(&mut self, hash: H) { + self.send(Status::Usurped(hash)) + } + + /// Extrinsic has been finalised in block with given hash. + pub fn finalised(&mut self, hash: H) { + self.send(Status::Finalised(hash)); + self.finalised = true; + } + + /// Transaction has been dropped from the pool because of the limit. + pub fn dropped(&mut self) { + self.send(Status::Dropped); + } + + /// The extrinsic has been broadcast to the given peers. + pub fn broadcast(&mut self, peers: Vec) { + self.send(Status::Broadcast(peers)) + } + + + /// Returns true if the are no more listeners for this extrinsic or it was finalised. + pub fn is_done(&self) -> bool { + self.finalised || self.receivers.is_empty() + } + + fn send(&mut self, status: Status) { + self.receivers.retain(|sender| sender.unbounded_send(status.clone()).is_ok()) + } +} diff --git a/core/keyring/Cargo.toml b/core/keyring/Cargo.toml new file mode 100644 index 0000000000000..04d6c2bd134bf --- /dev/null +++ b/core/keyring/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "substrate-keyring" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +substrate-primitives = { path = "../primitives" } +hex-literal = { version = "0.1.0" } +lazy_static = { version = "1.0" } diff --git a/core/keyring/README.adoc b/core/keyring/README.adoc new file mode 100644 index 0000000000000..0118fe883d1a6 --- /dev/null +++ b/core/keyring/README.adoc @@ -0,0 +1,13 @@ + += Keyring + +.Summary +[source, toml] +---- +include::Cargo.toml[lines=2..5] +---- + +.Description +---- +include::src/lib.rs[tag=description] +---- diff --git a/core/keyring/src/lib.rs b/core/keyring/src/lib.rs new file mode 100644 index 0000000000000..321ea6c04cf89 --- /dev/null +++ b/core/keyring/src/lib.rs @@ -0,0 +1,176 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +// tag::description[] +//! Support code for the runtime. +// end::description[] + +#[macro_use] extern crate hex_literal; +#[macro_use] extern crate lazy_static; +pub extern crate substrate_primitives; + +use std::collections::HashMap; +use std::ops::Deref; +use substrate_primitives::ed25519::{Pair, Public, Signature}; + +/// Set of test accounts. +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub enum Keyring { + Alice, + Bob, + Charlie, + Dave, + Eve, + Ferdie, + One, + Two, +} + +impl Keyring { + pub fn from_public(who: Public) -> Option { + [ + Keyring::Alice, + Keyring::Bob, + Keyring::Charlie, + Keyring::Dave, + Keyring::Eve, + Keyring::Ferdie, + Keyring::One, + Keyring::Two, + ].iter() + .map(|i| *i) + .find(|&k| Public::from(k) == who) + } + + pub fn from_raw_public(who: [u8; 32]) -> Option { + Self::from_public(Public::from_raw(who)) + } + + pub fn to_raw_public(self) -> [u8; 32] { + *Public::from(self).as_array_ref() + } + + pub fn to_raw_public_vec(self) -> Vec { + Public::from(self).to_raw_vec() + } + + pub fn sign(self, msg: &[u8]) -> Signature { + Pair::from(self).sign(msg) + } + + pub fn pair(self) -> Pair { + match self { + Keyring::Alice => Pair::from_seed(b"Alice "), + Keyring::Bob => Pair::from_seed(b"Bob "), + Keyring::Charlie => Pair::from_seed(b"Charlie "), + Keyring::Dave => Pair::from_seed(b"Dave "), + Keyring::Eve => Pair::from_seed(b"Eve "), + Keyring::Ferdie => Pair::from_seed(b"Ferdie "), + Keyring::One => Pair::from_seed(b"12345678901234567890123456789012"), + Keyring::Two => Pair::from_seed(&hex!("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60")), + } + } +} + +impl From for &'static str { + fn from(k: Keyring) -> Self { + match k { + Keyring::Alice => "Alice", + Keyring::Bob => "Bob", + Keyring::Charlie => "Charlie", + Keyring::Dave => "Dave", + Keyring::Eve => "Eve", + Keyring::Ferdie => "Ferdie", + Keyring::One => "one", + Keyring::Two => "two", + } + } +} + +lazy_static! { + static ref PRIVATE_KEYS: HashMap = { + [ + Keyring::Alice, + Keyring::Bob, + Keyring::Charlie, + Keyring::Dave, + Keyring::Eve, + Keyring::Ferdie, + Keyring::One, + Keyring::Two, + ].iter().map(|&i| (i, i.pair())).collect() + }; + + static ref PUBLIC_KEYS: HashMap = { + PRIVATE_KEYS.iter().map(|(&name, pair)| (name, pair.public())).collect() + }; +} + +impl From for Public { + fn from(k: Keyring) -> Self { + (*PUBLIC_KEYS).get(&k).unwrap().clone() + } +} + +impl From for Pair { + fn from(k: Keyring) -> Self { + k.pair() + } +} + +impl From for [u8; 32] { + fn from(k: Keyring) -> Self { + *(*PUBLIC_KEYS).get(&k).unwrap().as_array_ref() + } +} + +impl From for &'static [u8; 32] { + fn from(k: Keyring) -> Self { + (*PUBLIC_KEYS).get(&k).unwrap().as_array_ref() + } +} + +impl AsRef<[u8; 32]> for Keyring { + fn as_ref(&self) -> &[u8; 32] { + (*PUBLIC_KEYS).get(self).unwrap().as_array_ref() + } +} + +impl AsRef for Keyring { + fn as_ref(&self) -> &Public { + (*PUBLIC_KEYS).get(self).unwrap() + } +} + +impl Deref for Keyring { + type Target = [u8; 32]; + fn deref(&self) -> &[u8; 32] { + (*PUBLIC_KEYS).get(self).unwrap().as_array_ref() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use substrate_primitives::ed25519::Verifiable; + + #[test] + fn should_work() { + assert!(Keyring::Alice.sign(b"I am Alice!").verify(b"I am Alice!", Keyring::Alice)); + assert!(!Keyring::Alice.sign(b"I am Alice!").verify(b"I am Bob!", Keyring::Alice)); + assert!(!Keyring::Alice.sign(b"I am Alice!").verify(b"I am Alice!", Keyring::Bob)); + } +} diff --git a/core/keystore/Cargo.toml b/core/keystore/Cargo.toml new file mode 100644 index 0000000000000..a464f7005d6fe --- /dev/null +++ b/core/keystore/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "substrate-keystore" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +substrate-primitives = { path = "../primitives" } +parity-crypto = { version = "0.1", default_features = false } + +error-chain = "0.12" +hex = "0.3" +rand = "0.4" +serde_json = "1.0" +serde = "1.0" +serde_derive = "1.0" +subtle = "0.5" + +[dev-dependencies] +tempdir = "0.3" diff --git a/core/keystore/README.adoc b/core/keystore/README.adoc new file mode 100644 index 0000000000000..5a66a882ff098 --- /dev/null +++ b/core/keystore/README.adoc @@ -0,0 +1,13 @@ + += Keystore + +.Summary +[source, toml] +---- +include::Cargo.toml[lines=2..5] +---- + +.Description +---- +include::src/lib.rs[tag=description] +---- diff --git a/core/keystore/src/lib.rs b/core/keystore/src/lib.rs new file mode 100644 index 0000000000000..f3eeb4be3f81f --- /dev/null +++ b/core/keystore/src/lib.rs @@ -0,0 +1,293 @@ +// Copyright 2017-2018 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +// tag::description[] +//! Keystore (and session key management) for ed25519 based chains like Polkadot. +// end::description[] + +extern crate substrate_primitives; +extern crate parity_crypto as crypto; +extern crate subtle; +extern crate rand; +extern crate serde_json; +extern crate serde; +extern crate hex; + +#[macro_use] +extern crate serde_derive; + +#[macro_use] +extern crate error_chain; + +#[cfg(test)] +extern crate tempdir; + +use std::collections::HashMap; +use std::path::PathBuf; +use std::fs::{self, File}; +use std::io::{self, Write}; + +use substrate_primitives::{hashing::blake2_256, ed25519::{Pair, Public, PKCS_LEN}}; + +pub use crypto::KEY_ITERATIONS; + +error_chain! { + foreign_links { + Io(io::Error); + Json(serde_json::Error); + } + + errors { + InvalidPassword { + description("Invalid password"), + display("Invalid password"), + } + InvalidPKCS8 { + description("Invalid PKCS#8 data"), + display("Invalid PKCS#8 data"), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct InvalidPassword; + +#[derive(Serialize, Deserialize)] +struct EncryptedKey { + mac: [u8; 32], + salt: [u8; 32], + ciphertext: Vec, // TODO: switch to fixed-size when serde supports + iv: [u8; 16], + iterations: u32, +} + +impl EncryptedKey { + fn encrypt(plain: &[u8; PKCS_LEN], password: &str, iterations: u32) -> Self { + use rand::{Rng, OsRng}; + + let mut rng = OsRng::new().expect("OS Randomness available on all supported platforms; qed"); + + let salt: [u8; 32] = rng.gen(); + let iv: [u8; 16] = rng.gen(); + + // two parts of derived key + // DK = [ DK[0..15] DK[16..31] ] = [derived_left_bits, derived_right_bits] + let (derived_left_bits, derived_right_bits) = crypto::derive_key_iterations(password.as_bytes(), &salt, iterations); + + // preallocated (on-stack in case of `Secret`) buffer to hold cipher + // length = length(plain) as we are using CTR-approach + let mut ciphertext = vec![0; PKCS_LEN]; + + // aes-128-ctr with initial vector of iv + crypto::aes::encrypt_128_ctr(&derived_left_bits, &iv, plain, &mut *ciphertext) + .expect("input lengths of key and iv are both 16; qed"); + + // Blake2_256(DK[16..31] ++ ), where DK[16..31] - derived_right_bits + let mac = blake2_256(&crypto::derive_mac(&derived_right_bits, &*ciphertext)); + + EncryptedKey { + salt, + iv, + mac, + iterations, + ciphertext, + } + } + + fn decrypt(&self, password: &str) -> Result<[u8; PKCS_LEN]> { + let (derived_left_bits, derived_right_bits) = + crypto::derive_key_iterations(password.as_bytes(), &self.salt, self.iterations); + + let mac = blake2_256(&crypto::derive_mac(&derived_right_bits, &self.ciphertext)); + + if subtle::slices_equal(&mac[..], &self.mac[..]) != 1 { + return Err(ErrorKind::InvalidPassword.into()); + } + + let mut plain = [0; PKCS_LEN]; + crypto::aes::decrypt_128_ctr(&derived_left_bits, &self.iv, &self.ciphertext, &mut plain[..]) + .expect("input lengths of key and iv are both 16; qed"); + Ok(plain) + } +} + +type Seed = [u8; 32]; + +/// Key store. +pub struct Store { + path: PathBuf, + additional: HashMap, +} + +impl Store { + /// Create a new store at the given path. + pub fn open(path: PathBuf) -> Result { + fs::create_dir_all(&path)?; + Ok(Store { path, additional: HashMap::new() }) + } + + /// Generate a new key, placing it into the store. + pub fn generate(&self, password: &str) -> Result { + let (pair, pkcs_bytes) = Pair::generate_with_pkcs8(); + let key_file = EncryptedKey::encrypt(&pkcs_bytes, password, KEY_ITERATIONS as u32); + + let mut file = File::create(self.key_file_path(&pair.public()))?; + ::serde_json::to_writer(&file, &key_file)?; + + file.flush()?; + + Ok(pair) + } + + /// Create a new key from seed. Do not place it into the store. + /// Only the first 32 bytes of the sead are used. This is meant to be used for testing only. + // TODO: Remove this + pub fn generate_from_seed(&mut self, seed: &str) -> Result { + let mut s: [u8; 32] = [' ' as u8; 32]; + + let was_hex = if seed.len() == 66 && &seed[0..2] == "0x" { + if let Ok(d) = hex::decode(&seed[2..]) { + s.copy_from_slice(&d); + true + } else { false } + } else { false }; + + if !was_hex { + let len = ::std::cmp::min(32, seed.len()); + &mut s[..len].copy_from_slice(&seed.as_bytes()[..len]); + } + + let pair = Pair::from_seed(&s); + self.additional.insert(pair.public(), s); + Ok(pair) + } + + /// Load a key file with given public key. + pub fn load(&self, public: &Public, password: &str) -> Result { + if let Some(ref seed) = self.additional.get(public) { + let pair = Pair::from_seed(seed); + return Ok(pair); + } + let path = self.key_file_path(public); + let file = File::open(path)?; + + let encrypted_key: EncryptedKey = ::serde_json::from_reader(&file)?; + let pkcs_bytes = encrypted_key.decrypt(password)?; + + Pair::from_pkcs8(&pkcs_bytes[..]).map_err(|_| ErrorKind::InvalidPKCS8.into()) + } + + /// Get public keys of all stored keys. + pub fn contents(&self) -> Result> { + let mut public_keys: Vec = self.additional.keys().cloned().collect(); + 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)); + } + _ => continue, + } + } + } + + Ok(public_keys) + } + + fn key_file_path(&self, public: &Public) -> PathBuf { + let mut buf = self.path.clone(); + buf.push(hex::encode(public.as_slice())); + buf + } +} + +#[cfg(test)] +mod tests { + use super::*; + use tempdir::TempDir; + + #[test] + fn encrypt_and_decrypt() { + let plain = [1; PKCS_LEN]; + let encrypted_key = EncryptedKey::encrypt(&plain, "thepassword", KEY_ITERATIONS as u32); + + let decrypted_key = encrypted_key.decrypt("thepassword").unwrap(); + + assert_eq!(&plain[..], &decrypted_key[..]); + } + + #[test] + fn decrypt_wrong_password_fails() { + let plain = [1; PKCS_LEN]; + let encrypted_key = EncryptedKey::encrypt(&plain, "thepassword", KEY_ITERATIONS as u32); + + assert!(encrypted_key.decrypt("thepassword2").is_err()); + } + + #[test] + fn decrypt_wrong_iterations_fails() { + let plain = [1; PKCS_LEN]; + let mut encrypted_key = EncryptedKey::encrypt(&plain, "thepassword", KEY_ITERATIONS as u32); + + encrypted_key.iterations -= 64; + + assert!(encrypted_key.decrypt("thepassword").is_err()); + } + + #[test] + fn basic_store() { + let temp_dir = TempDir::new("keystore").unwrap(); + let store = Store::open(temp_dir.path().to_owned()).unwrap(); + + assert!(store.contents().unwrap().is_empty()); + + let key = store.generate("thepassword").unwrap(); + let key2 = store.load(&key.public(), "thepassword").unwrap(); + + assert!(store.load(&key.public(), "notthepassword").is_err()); + + assert_eq!(key.public(), key2.public()); + + assert_eq!(store.contents().unwrap()[0], key.public()); + } + + #[test] + fn test_generate_from_seed() { + let temp_dir = TempDir::new("keystore").unwrap(); + let mut store = Store::open(temp_dir.path().to_owned()).unwrap(); + + let pair = store.generate_from_seed("0x1").unwrap(); + assert_eq!("5GqhgbUd2S9uc5Tm7hWhw29Tw2jBnuHshmTV1fDF4V1w3G2z", pair.public().to_ss58check()); + + let pair = store.generate_from_seed("0x3d97c819d68f9bafa7d6e79cb991eebcd77d966c5334c0b94d9e1fa7ad0869dc").unwrap(); + assert_eq!("5DKUrgFqCPV8iAXx9sjy1nyBygQCeiUYRFWurZGhnrn3HBL8", pair.public().to_ss58check()); + + let pair = store.generate_from_seed("12345678901234567890123456789022").unwrap(); + assert_eq!("5DscZvfjnM5im7oKRXXP9xtCG1SEwfMb8J5eGLmw5EHhoHR3", pair.public().to_ss58check()); + + let pair = store.generate_from_seed("1").unwrap(); + assert_eq!("5DYnksEZFc7kgtfyNM1xK2eBtW142gZ3Ho3NQubrF2S6B2fq", pair.public().to_ss58check()); + } +} diff --git a/core/misbehavior-check/Cargo.toml b/core/misbehavior-check/Cargo.toml new file mode 100644 index 0000000000000..d455a4fc045ad --- /dev/null +++ b/core/misbehavior-check/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "substrate-misbehavior-check" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +substrate-codec = { path = "../codec", default-features = false } +substrate-primitives = { path = "../primitives", default-features = false } +substrate-runtime-primitives = { path = "../../runtime/primitives", default-features = false } +substrate-runtime-io = { path = "../runtime-io", default-features = false } + +[dev-dependencies] +substrate-bft = { path = "../bft" } +rhododendron = "0.3" +substrate-keyring = { path = "../keyring" } + +[features] +default = ["std"] +std = ["substrate-codec/std", "substrate-primitives/std", "substrate-runtime-primitives/std", "substrate-runtime-io/std"] diff --git a/core/misbehavior-check/README.adoc b/core/misbehavior-check/README.adoc new file mode 100644 index 0000000000000..e5b52b954b673 --- /dev/null +++ b/core/misbehavior-check/README.adoc @@ -0,0 +1,13 @@ + += Misbehavior-check + +.Summary +[source, toml] +---- +include::Cargo.toml[lines=2..5] +---- + +.Description +---- +include::src/lib.rs[tag=description] +---- diff --git a/core/misbehavior-check/src/lib.rs b/core/misbehavior-check/src/lib.rs new file mode 100644 index 0000000000000..eff87ab1e5efb --- /dev/null +++ b/core/misbehavior-check/src/lib.rs @@ -0,0 +1,206 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +// tag::description[] +//! Utility for substrate-based runtimes that want to check misbehavior reports. +// end::description[] + +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate substrate_codec as codec; +extern crate substrate_primitives as primitives; +extern crate substrate_runtime_io as runtime_io; +extern crate substrate_runtime_primitives as runtime_primitives; + +#[cfg(test)] +extern crate substrate_bft; +#[cfg(test)] +extern crate substrate_keyring as keyring; +#[cfg(test)] +extern crate rhododendron; + +use codec::{Codec, Encode}; +use primitives::{AuthorityId, Signature}; + +use runtime_primitives::bft::{Action, Message, MisbehaviorKind}; + +// check a message signature. returns true if signed by that authority. +fn check_message_sig( + message: Message, + signature: &Signature, + from: &AuthorityId +) -> bool { + let msg: Vec = message.encode(); + runtime_io::ed25519_verify(&signature.0, &msg, from) +} + +fn prepare(parent: H, round_number: u32, hash: H) -> Message { + Message { + parent, + action: Action::Prepare(round_number, hash), + } +} + +fn commit(parent: H, round_number: u32, hash: H) -> Message { + Message { + parent, + action: Action::Commit(round_number, hash), + } +} + +/// Evaluate misbehavior. +/// +/// Doesn't check that the header hash in question is +/// valid or whether the misbehaving authority was part of +/// the set at that block. +pub fn evaluate_misbehavior( + misbehaved: &AuthorityId, + parent_hash: H, + kind: &MisbehaviorKind, +) -> bool { + match *kind { + MisbehaviorKind::BftDoublePrepare(round, (h_1, ref s_1), (h_2, ref s_2)) => { + s_1 != s_2 && + check_message_sig::(prepare::(parent_hash, round, h_1), s_1, misbehaved) && + check_message_sig::(prepare::(parent_hash, round, h_2), s_2, misbehaved) + } + MisbehaviorKind::BftDoubleCommit(round, (h_1, ref s_1), (h_2, ref s_2)) => { + s_1 != s_2 && + check_message_sig::(commit::(parent_hash, round, h_1), s_1, misbehaved) && + check_message_sig::(commit::(parent_hash, round, h_2), s_2, misbehaved) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use keyring::ed25519; + use keyring::Keyring; + + use runtime_primitives::testing::{H256, Block as RawBlock}; + + type Block = RawBlock; + + fn sign_prepare(key: &ed25519::Pair, round: u32, hash: H256, parent_hash: H256) -> (H256, Signature) { + let msg = substrate_bft::sign_message::( + rhododendron::Message::Vote(rhododendron::Vote::Prepare(round as _, hash)), + key, + parent_hash + ); + + match msg { + rhododendron::LocalizedMessage::Vote(vote) => (hash, vote.signature.signature), + _ => panic!("signing vote leads to signed vote"), + } + } + + fn sign_commit(key: &ed25519::Pair, round: u32, hash: H256, parent_hash: H256) -> (H256, Signature) { + let msg = substrate_bft::sign_message::( + rhododendron::Message::Vote(rhododendron::Vote::Commit(round as _, hash)), + key, + parent_hash + ); + + match msg { + rhododendron::LocalizedMessage::Vote(vote) => (hash, vote.signature.signature), + _ => panic!("signing vote leads to signed vote"), + } + } + + #[test] + fn evaluates_double_prepare() { + let key: ed25519::Pair = Keyring::One.into(); + let parent_hash = [0xff; 32].into(); + let hash_1 = [0; 32].into(); + let hash_2 = [1; 32].into(); + + assert!(evaluate_misbehavior::( + &key.public().into(), + parent_hash, + &MisbehaviorKind::BftDoublePrepare( + 1, + sign_prepare(&key, 1, hash_1, parent_hash), + sign_prepare(&key, 1, hash_2, parent_hash) + ) + )); + + // same signature twice is not misbehavior. + let signed = sign_prepare(&key, 1, hash_1, parent_hash); + assert!(evaluate_misbehavior::( + &key.public().into(), + parent_hash, + &MisbehaviorKind::BftDoublePrepare( + 1, + signed, + signed, + ) + ) == false); + + // misbehavior has wrong target. + assert!(evaluate_misbehavior::( + &Keyring::Two.to_raw_public().into(), + parent_hash, + &MisbehaviorKind::BftDoublePrepare( + 1, + sign_prepare(&key, 1, hash_1, parent_hash), + sign_prepare(&key, 1, hash_2, parent_hash), + ) + ) == false); + } + + #[test] + fn evaluates_double_commit() { + let key: ed25519::Pair = Keyring::One.into(); + let parent_hash = [0xff; 32].into(); + let hash_1 = [0; 32].into(); + let hash_2 = [1; 32].into(); + + assert!(evaluate_misbehavior::( + &key.public().into(), + parent_hash, + &MisbehaviorKind::BftDoubleCommit( + 1, + sign_commit(&key, 1, hash_1, parent_hash), + sign_commit(&key, 1, hash_2, parent_hash) + ) + )); + + // same signature twice is not misbehavior. + let signed = sign_commit(&key, 1, hash_1, parent_hash); + assert!(evaluate_misbehavior::( + &key.public().into(), + parent_hash, + &MisbehaviorKind::BftDoubleCommit( + 1, + signed, + signed, + ) + ) == false); + + // misbehavior has wrong target. + assert!(evaluate_misbehavior::( + &Keyring::Two.to_raw_public().into(), + parent_hash, + &MisbehaviorKind::BftDoubleCommit( + 1, + sign_commit(&key, 1, hash_1, parent_hash), + sign_commit(&key, 1, hash_2, parent_hash), + ) + ) == false); + } +} diff --git a/core/network-libp2p/Cargo.toml b/core/network-libp2p/Cargo.toml new file mode 100644 index 0000000000000..b002fa82e969a --- /dev/null +++ b/core/network-libp2p/Cargo.toml @@ -0,0 +1,34 @@ +[package] +description = "libp2p implementation of the ethcore network library" +homepage = "http://parity.io" +license = "GPL-3.0" +name = "substrate-network-libp2p" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +bytes = "0.4" +error-chain = { version = "0.12", default-features = false } +fnv = "1.0" +futures = "0.1" +libp2p = { git = "https://github.com/libp2p/rust-libp2p", rev = "304e9c72c88bc97824f2734dc19d1b5f4556d346", default-features = false, features = ["libp2p-secio", "libp2p-secio-secp256k1"] } +ethcore-io = { git = "https://github.com/paritytech/parity.git" } +ethkey = { git = "https://github.com/paritytech/parity.git" } +ethereum-types = "0.3" +parking_lot = "0.5" +libc = "0.2" +log = "0.3" +rand = "0.5.0" +serde = "1.0.70" +serde_derive = "1.0.70" +serde_json = "1.0.24" +tokio = "0.1" +tokio-io = "0.1" +tokio-timer = "0.2" +unsigned-varint = { version = "0.2.1", features = ["codec"] } + +[dev-dependencies] +assert_matches = "1.2" +parity-bytes = "0.1" +ethcore-io = { git = "https://github.com/paritytech/parity.git" } +ethcore-logger = { git = "https://github.com/paritytech/parity.git" } diff --git a/core/network-libp2p/README.adoc b/core/network-libp2p/README.adoc new file mode 100644 index 0000000000000..2f340aa397e73 --- /dev/null +++ b/core/network-libp2p/README.adoc @@ -0,0 +1,13 @@ + += Network libp2p + +.Summary +[source, toml] +---- +include::Cargo.toml[lines=2..5] +---- + +.Description +---- +include::src/lib.rs[tag=description] +---- diff --git a/core/network-libp2p/src/connection_filter.rs b/core/network-libp2p/src/connection_filter.rs new file mode 100644 index 0000000000000..46d9d86b58d33 --- /dev/null +++ b/core/network-libp2p/src/connection_filter.rs @@ -0,0 +1,31 @@ +// Copyright 2015-2018 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 . + +//! Connection filter trait. + +use super::NodeId; + +/// Filtered connection direction. +pub enum ConnectionDirection { + Inbound, + Outbound, +} + +/// Connection filter. Each connection is checked against `connection_allowed`. +pub trait ConnectionFilter : Send + Sync { + /// Filter a connection. Returns `true` if connection should be allowed. `false` if rejected. + fn connection_allowed(&self, own_id: &NodeId, connecting_id: &NodeId, direction: ConnectionDirection) -> bool; +} diff --git a/core/network-libp2p/src/custom_proto.rs b/core/network-libp2p/src/custom_proto.rs new file mode 100644 index 0000000000000..72807f21e8f2e --- /dev/null +++ b/core/network-libp2p/src/custom_proto.rs @@ -0,0 +1,290 @@ +// Copyright 2018 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 bytes::{Bytes, BytesMut}; +use ProtocolId; +use libp2p::core::{Multiaddr, ConnectionUpgrade, Endpoint}; +use PacketId; +use std::io::Error as IoError; +use std::vec::IntoIter as VecIntoIter; +use futures::{future, Future, stream, Stream, Sink}; +use futures::sync::mpsc; +use tokio_io::{AsyncRead, AsyncWrite}; +use unsigned_varint::codec::UviBytes; + +/// Connection upgrade for a single protocol. +/// +/// Note that "a single protocol" here refers to `par` for example. However +/// each protocol can have multiple different versions for networking purposes. +#[derive(Clone)] +pub struct RegisteredProtocol { + /// Id of the protocol for API purposes. + id: ProtocolId, + /// Base name of the protocol as advertised on the network. + /// Ends with `/` so that we can append a version number behind. + base_name: Bytes, + /// List of protocol versions that we support, plus their packet count. + /// Ordered in descending order so that the best comes first. + /// The packet count is used to filter out invalid messages. + supported_versions: Vec<(u8, u8)>, + /// Custom data. + custom_data: T, +} + +/// Output of a `RegisteredProtocol` upgrade. +pub struct RegisteredProtocolOutput { + /// Data passed to `RegisteredProtocol::new`. + pub custom_data: T, + + /// Id of the protocol. + pub protocol_id: ProtocolId, + + /// Endpoint of the connection. + pub endpoint: Endpoint, + + /// Version of the protocol that was negotiated. + pub protocol_version: u8, + + /// Channel to sender outgoing messages to. + // TODO: consider assembling packet_id here + pub outgoing: mpsc::UnboundedSender, + + /// Stream where incoming messages are received. The stream ends whenever + /// either side is closed. + pub incoming: Box + Send>, +} + +impl RegisteredProtocol { + /// Creates a new `RegisteredProtocol`. The `custom_data` parameter will be + /// passed inside the `RegisteredProtocolOutput`. + pub fn new(custom_data: T, protocol: ProtocolId, versions: &[(u8, u8)]) + -> Self { + let mut proto_name = Bytes::from_static(b"/substrate/"); + proto_name.extend_from_slice(&protocol); + proto_name.extend_from_slice(b"/"); + + RegisteredProtocol { + base_name: proto_name, + id: protocol, + supported_versions: { + let mut tmp: Vec<_> = versions.iter().rev().cloned().collect(); + tmp.sort_unstable_by(|a, b| b.1.cmp(&a.1)); + tmp + }, + custom_data: custom_data, + } + } + + /// Returns the ID of the protocol. + pub fn id(&self) -> ProtocolId { + self.id + } + + /// Returns the custom data that was passed to `new`. + pub fn custom_data(&self) -> &T { + &self.custom_data + } +} + +// `Maf` is short for `MultiaddressFuture` +impl ConnectionUpgrade for RegisteredProtocol +where C: AsyncRead + AsyncWrite + Send + 'static, // TODO: 'static :-/ + Maf: Future + Send + 'static, // TODO: 'static :( +{ + type NamesIter = VecIntoIter<(Bytes, Self::UpgradeIdentifier)>; + type UpgradeIdentifier = u8; // Protocol version + + #[inline] + fn protocol_names(&self) -> Self::NamesIter { + // Report each version as an individual protocol. + self.supported_versions.iter().map(|&(ver, _)| { + let num = ver.to_string(); + let mut name = self.base_name.clone(); + name.extend_from_slice(num.as_bytes()); + (name, ver) + }).collect::>().into_iter() + } + + type Output = RegisteredProtocolOutput; + type MultiaddrFuture = Maf; + type Future = future::FutureResult<(Self::Output, Self::MultiaddrFuture), IoError>; + + #[allow(deprecated)] + fn upgrade( + self, + socket: C, + protocol_version: Self::UpgradeIdentifier, + endpoint: Endpoint, + remote_addr: Maf + ) -> Self::Future { + let packet_count = self.supported_versions + .iter() + .find(|&(v, _)| *v == protocol_version) + .expect("negotiated protocol version that wasn't advertised ; \ + programmer error") + .1; + + // This function is called whenever we successfully negotiated a + // protocol with a remote (both if initiated by us or by the remote) + + // This channel is used to send outgoing packets to the custom_data + // for this open substream. + let (msg_tx, msg_rx) = mpsc::unbounded(); + + // Build the sink for outgoing network bytes, and the stream for + // incoming instructions. `stream` implements `Stream`. + enum Message { + /// Received data from the network. + RecvSocket(BytesMut), + /// Data to send to the network. + /// The packet_id must already be inside the `Bytes`. + SendReq(Bytes), + /// The socket has been closed. + Finished, + } + + let (sink, stream) = { + let framed = AsyncRead::framed(socket, UviBytes::default()); + let msg_rx = msg_rx.map(Message::SendReq) + .map_err(|()| unreachable!("mpsc::UnboundedReceiver never errors")); + let (sink, stream) = framed.split(); + let stream = stream.map(Message::RecvSocket) + .chain(stream::once(Ok(Message::Finished))); + (sink, msg_rx.select(stream)) + }; + + let incoming = stream::unfold((sink, stream, false), move |(sink, stream, finished)| { + if finished { + return None + } + + Some(stream + .into_future() + .map_err(|(err, _)| err) + .and_then(move |(message, stream)| + match message { + Some(Message::RecvSocket(mut data)) => { + // The `data` should be prefixed by the packet ID, + // therefore an empty packet is invalid. + if data.is_empty() { + debug!(target: "sub-libp2p", "ignoring incoming \ + packet because it was empty"); + let f = future::ok((None, (sink, stream, false))); + return future::Either::A(f) + } + + let packet_id = data[0]; + let data = data.split_off(1); + + if packet_id >= packet_count { + debug!(target: "sub-libp2p", "ignoring incoming packet \ + because packet_id {} is too large", packet_id); + let f = future::ok((None, (sink, stream, false))); + future::Either::A(f) + } else { + let out = Some((packet_id, data.freeze())); + let f = future::ok((out, (sink, stream, false))); + future::Either::A(f) + } + }, + + Some(Message::SendReq(data)) => { + let fut = sink.send(data) + .map(move |sink| (None, (sink, stream, false))); + future::Either::B(fut) + }, + + Some(Message::Finished) | None => { + let f = future::ok((None, (sink, stream, true))); + future::Either::A(f) + }, + } + )) + }).filter_map(|v| v); + + let out = RegisteredProtocolOutput { + custom_data: self.custom_data, + protocol_id: self.id, + endpoint, + protocol_version: protocol_version, + outgoing: msg_tx, + incoming: Box::new(incoming), + }; + + future::ok((out, remote_addr)) + } +} + +// Connection upgrade for all the protocols contained in it. +#[derive(Clone)] +pub struct RegisteredProtocols(pub Vec>); + +impl RegisteredProtocols { + /// Finds a protocol in the list by its id. + pub fn find_protocol(&self, protocol: ProtocolId) + -> Option<&RegisteredProtocol> { + self.0.iter().find(|p| p.id == protocol) + } + + /// Returns true if the given protocol is in the list. + pub fn has_protocol(&self, protocol: ProtocolId) -> bool { + self.0.iter().any(|p| p.id == protocol) + } +} + +impl Default for RegisteredProtocols { + fn default() -> Self { + RegisteredProtocols(Vec::new()) + } +} + +impl ConnectionUpgrade for RegisteredProtocols +where C: AsyncRead + AsyncWrite + Send + 'static, // TODO: 'static :-/ + Maf: Future + Send + 'static, // TODO: 'static :( +{ + type NamesIter = VecIntoIter<(Bytes, Self::UpgradeIdentifier)>; + type UpgradeIdentifier = (usize, + as ConnectionUpgrade>::UpgradeIdentifier); + + fn protocol_names(&self) -> Self::NamesIter { + // We concat the lists of `RegisteredProtocol::protocol_names` for + // each protocol. + self.0.iter().enumerate().flat_map(|(n, proto)| + ConnectionUpgrade::::protocol_names(proto) + .map(move |(name, id)| (name, (n, id))) + ).collect::>().into_iter() + } + + type Output = as ConnectionUpgrade>::Output; + type MultiaddrFuture = as + ConnectionUpgrade>::MultiaddrFuture; + type Future = as ConnectionUpgrade>::Future; + + #[inline] + fn upgrade( + self, + socket: C, + upgrade_identifier: Self::UpgradeIdentifier, + endpoint: Endpoint, + remote_addr: Maf + ) -> Self::Future { + let (protocol_index, inner_proto_id) = upgrade_identifier; + self.0.into_iter() + .nth(protocol_index) + .expect("invalid protocol index ; programmer logic error") + .upgrade(socket, inner_proto_id, endpoint, remote_addr) + } +} diff --git a/core/network-libp2p/src/error.rs b/core/network-libp2p/src/error.rs new file mode 100644 index 0000000000000..d095858b12203 --- /dev/null +++ b/core/network-libp2p/src/error.rs @@ -0,0 +1,221 @@ +// Copyright 2015-2018 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::{io, net, fmt}; +use libc::{ENFILE, EMFILE}; +use io::IoError; +use ethkey; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum DisconnectReason +{ + DisconnectRequested, + TCPError, + BadProtocol, + UselessPeer, + TooManyPeers, + DuplicatePeer, + IncompatibleProtocol, + NullIdentity, + ClientQuit, + UnexpectedIdentity, + LocalIdentity, + PingTimeout, + Unknown, +} + +impl DisconnectReason { + pub fn from_u8(n: u8) -> DisconnectReason { + match n { + 0 => DisconnectReason::DisconnectRequested, + 1 => DisconnectReason::TCPError, + 2 => DisconnectReason::BadProtocol, + 3 => DisconnectReason::UselessPeer, + 4 => DisconnectReason::TooManyPeers, + 5 => DisconnectReason::DuplicatePeer, + 6 => DisconnectReason::IncompatibleProtocol, + 7 => DisconnectReason::NullIdentity, + 8 => DisconnectReason::ClientQuit, + 9 => DisconnectReason::UnexpectedIdentity, + 10 => DisconnectReason::LocalIdentity, + 11 => DisconnectReason::PingTimeout, + _ => DisconnectReason::Unknown, + } + } +} + +impl fmt::Display for DisconnectReason { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::DisconnectReason::*; + + let msg = match *self { + DisconnectRequested => "disconnect requested", + TCPError => "TCP error", + BadProtocol => "bad protocol", + UselessPeer => "useless peer", + TooManyPeers => "too many peers", + DuplicatePeer => "duplicate peer", + IncompatibleProtocol => "incompatible protocol", + NullIdentity => "null identity", + ClientQuit => "client quit", + UnexpectedIdentity => "unexpected identity", + LocalIdentity => "local identity", + PingTimeout => "ping timeout", + Unknown => "unknown", + }; + + f.write_str(msg) + } +} + +error_chain! { + foreign_links { + SocketIo(IoError) #[doc = "Socket IO error."]; + } + + errors { + #[doc = "Error concerning the network address parsing subsystem."] + AddressParse { + description("Failed to parse network address"), + display("Failed to parse network address"), + } + + #[doc = "Error concerning the network address resolution subsystem."] + AddressResolve(err: Option) { + description("Failed to resolve network address"), + display("Failed to resolve network address {}", err.as_ref().map_or("".to_string(), |e| e.to_string())), + } + + #[doc = "Authentication failure"] + Auth { + description("Authentication failure"), + display("Authentication failure"), + } + + #[doc = "Unrecognised protocol"] + BadProtocol { + description("Bad protocol"), + display("Bad protocol"), + } + + #[doc = "Expired message"] + Expired { + description("Expired message"), + display("Expired message"), + } + + #[doc = "Peer not found"] + PeerNotFound { + description("Peer not found"), + display("Peer not found"), + } + + #[doc = "Peer is disconnected"] + Disconnect(reason: DisconnectReason) { + description("Peer disconnected"), + display("Peer disconnected: {}", reason), + } + + #[doc = "Invalid node id"] + InvalidNodeId { + description("Invalid node id"), + display("Invalid node id"), + } + + #[doc = "Packet size is over the protocol limit"] + OversizedPacket { + description("Packet is too large"), + display("Packet is too large"), + } + + #[doc = "Reached system resource limits for this process"] + ProcessTooManyFiles { + description("Too many open files in process."), + display("Too many open files in this process. Check your resource limits and restart parity"), + } + + #[doc = "Reached system wide resource limits"] + SystemTooManyFiles { + description("Too many open files on system."), + display("Too many open files on system. Consider closing some processes/release some file handlers or increas the system-wide resource limits and restart parity."), + } + + #[doc = "An unknown IO error occurred."] + Io(err: io::Error) { + description("IO Error"), + display("Unexpected IO error: {}", err), + } + } +} + +impl From for Error { + fn from(err: io::Error) -> Self { + match err.raw_os_error() { + Some(ENFILE) => ErrorKind::ProcessTooManyFiles.into(), + Some(EMFILE) => ErrorKind::SystemTooManyFiles.into(), + _ => Error::from_kind(ErrorKind::Io(err)) + } + } +} + +impl From for Error { + fn from(_err: ethkey::Error) -> Self { + ErrorKind::Auth.into() + } +} + +impl From for Error { + fn from(_err: ethkey::crypto::Error) -> Self { + ErrorKind::Auth.into() + } +} + +impl From for Error { + fn from(_err: net::AddrParseError) -> Self { ErrorKind::AddressParse.into() } +} + +#[test] +fn test_errors() { + assert_eq!(DisconnectReason::ClientQuit, DisconnectReason::from_u8(8)); + let mut r = DisconnectReason::DisconnectRequested; + for i in 0 .. 20 { + r = DisconnectReason::from_u8(i); + } + assert_eq!(DisconnectReason::Unknown, r); +} + +#[test] +fn test_io_errors() { + use libc::{EMFILE, ENFILE}; + + assert_matches!( + >::from( + io::Error::from_raw_os_error(ENFILE) + ).kind(), + ErrorKind::ProcessTooManyFiles); + + assert_matches!( + >::from( + io::Error::from_raw_os_error(EMFILE) + ).kind(), + ErrorKind::SystemTooManyFiles); + + assert_matches!( + >::from( + io::Error::from_raw_os_error(0) + ).kind(), + ErrorKind::Io(_)); +} diff --git a/core/network-libp2p/src/lib.rs b/core/network-libp2p/src/lib.rs new file mode 100644 index 0000000000000..f06c8fefd865f --- /dev/null +++ b/core/network-libp2p/src/lib.rs @@ -0,0 +1,75 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +// tag::description[] +//! TODO: Missing doc +// end::description[] + +#![recursion_limit="128"] +#![type_length_limit = "268435456"] + +extern crate parking_lot; +extern crate fnv; +extern crate futures; +extern crate tokio; +extern crate tokio_io; +extern crate tokio_timer; +extern crate ethkey; +extern crate libc; +extern crate libp2p; +extern crate rand; +extern crate serde; +#[macro_use] +extern crate serde_derive; +extern crate serde_json; +extern crate bytes; +extern crate unsigned_varint; + +extern crate ethcore_io as io; +extern crate ethereum_types; + +#[macro_use] +extern crate error_chain; +#[macro_use] +extern crate log; +#[cfg(test)] #[macro_use] +extern crate assert_matches; + +pub use connection_filter::{ConnectionFilter, ConnectionDirection}; +pub use io::TimerToken; +pub use error::{Error, ErrorKind, DisconnectReason}; +pub use libp2p::{Multiaddr, multiaddr::AddrComponent}; +pub use traits::*; + +mod connection_filter; +mod custom_proto; +mod error; +mod network_state; +mod service; +mod timeouts; +mod topology; +mod traits; +mod transport; + +pub use service::NetworkService; + +/// Check if node url is valid +pub fn validate_node_url(url: &str) -> Result<(), Error> { + match url.parse::() { + Ok(_) => Ok(()), + Err(_) => Err(ErrorKind::InvalidNodeId.into()), + } +} diff --git a/core/network-libp2p/src/network_state.rs b/core/network-libp2p/src/network_state.rs new file mode 100644 index 0000000000000..e06735f647fc5 --- /dev/null +++ b/core/network-libp2p/src/network_state.rs @@ -0,0 +1,953 @@ +// Copyright 2018 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 bytes::Bytes; +use fnv::{FnvHashMap, FnvHashSet}; +use futures::sync::mpsc; +use libp2p::core::{multiaddr::ToMultiaddr, Multiaddr, AddrComponent, Endpoint, UniqueConnec}; +use libp2p::core::{UniqueConnecState, PeerId, PublicKey}; +use libp2p::kad::KadConnecController; +use libp2p::ping::Pinger; +use libp2p::secio; +use {Error, ErrorKind, NetworkConfiguration, NonReservedPeerMode}; +use {NodeIndex, ProtocolId, SessionInfo}; +use parking_lot::{Mutex, RwLock}; +use rand::{self, Rng}; +use topology::{DisconnectReason, NetTopology}; +use std::fs; +use std::io::{Error as IoError, ErrorKind as IoErrorKind, Read, Write}; +use std::path::Path; +use std::sync::atomic; +use std::time::{Duration, Instant}; + +// File where the peers are stored. +const NODES_FILE: &str = "nodes.json"; +// File where the private key is stored. +const SECRET_FILE: &str = "secret"; +// Duration during which a peer is disabled. +const PEER_DISABLE_DURATION: Duration = Duration::from_secs(5 * 60); + +// Common struct shared throughout all the components of the service. +pub struct NetworkState { + /// Contains the information about the network. + topology: RwLock, + + /// Active connections. + connections: RwLock, + + /// Maximum incoming peers. + max_incoming_peers: u32, + /// Maximum outgoing peers. + max_outgoing_peers: u32, + + /// If true, only reserved peers can connect. + reserved_only: atomic::AtomicBool, + /// List of the IDs of the reserved peers. + reserved_peers: RwLock>, + + /// Each node we discover gets assigned a new unique ID. This ID increases linearly. + next_node_index: atomic::AtomicUsize, + + /// List of the IDs of the disabled peers. These peers will see their + /// connections refused. Includes the time when the disabling expires. + disabled_nodes: Mutex>, + + /// Local private key. + local_private_key: secio::SecioKeyPair, + /// Local public key. + local_public_key: PublicKey, +} + +struct Connections { + /// For each libp2p peer ID, the ID of the peer in the API we expose. + /// Also corresponds to the index in `info_by_peer`. + peer_by_nodeid: FnvHashMap, + + /// For each peer ID, information about our connection to this peer. + info_by_peer: FnvHashMap, +} + +struct PeerConnectionInfo { + /// A list of protocols, and the potential corresponding connection. + /// The `UniqueConnec` contains a sender and the protocol version. + /// The sender can be used to transmit data for the remote. Note that the + /// packet_id has to be inside the `Bytes`. + protocols: Vec<(ProtocolId, UniqueConnec<(mpsc::UnboundedSender, u8)>)>, + + /// The Kademlia connection to this node. + kad_connec: UniqueConnec, + + /// The ping connection to this node. + ping_connec: UniqueConnec, + + /// Id of the peer. + id: PeerId, + + /// True if this connection was initiated by us. `None` if we're not connected. + /// Note that it is theoretically possible that we dial the remote at the + /// same time they dial us, in which case the protocols may be dispatched + /// between both connections, and in which case the value here will be racy. + originated: Option, + + /// Latest known ping duration. + ping: Option, + + /// The client version of the remote, or `None` if not known. + client_version: Option, + + /// The multiaddresses of the remote, or `None` if not known. + remote_addresses: Vec, + + /// The local multiaddress used to communicate with the remote, or `None` + /// if not known. + // TODO: never filled ; also shouldn't be an `Option` + local_address: Option, +} + +/// Simplified, POD version of PeerConnectionInfo. +#[derive(Debug, Clone)] +pub struct PeerInfo { + /// Id of the peer. + pub id: PeerId, + + /// True if this connection was initiated by us. + /// Note that it is theoretically possible that we dial the remote at the + /// same time they dial us, in which case the protocols may be dispatched + /// between both connections, and in which case the value here will be racy. + pub originated: bool, + + /// Latest known ping duration. + pub ping: Option, + + /// The client version of the remote, or `None` if not known. + pub client_version: Option, + + /// The multiaddress of the remote. + pub remote_address: Option, + + /// The local multiaddress used to communicate with the remote, or `None` + /// if not known. + pub local_address: Option, +} + +impl<'a> From<&'a PeerConnectionInfo> for PeerInfo { + fn from(i: &'a PeerConnectionInfo) -> PeerInfo { + PeerInfo { + id: i.id.clone(), + originated: i.originated.unwrap_or(true), + ping: i.ping, + client_version: i.client_version.clone(), + remote_address: i.remote_addresses.get(0).map(|a| a.clone()), + local_address: i.local_address.clone(), + } + } +} + +impl NetworkState { + pub fn new(config: &NetworkConfiguration) -> Result { + // Private and public keys configuration. + let local_private_key = obtain_private_key(&config)?; + let local_public_key = local_private_key.to_public_key(); + + // Build the storage for peers, including the bootstrap nodes. + let mut topology = if let Some(ref path) = config.net_config_path { + let path = Path::new(path).join(NODES_FILE); + debug!(target: "sub-libp2p", "Initializing peer store for JSON file {:?}", path); + NetTopology::from_file(path) + } else { + debug!(target: "sub-libp2p", "No peers file configured ; peers won't be saved"); + NetTopology::memory() + }; + + let reserved_peers = { + let mut reserved_peers = FnvHashSet::with_capacity_and_hasher( + config.reserved_nodes.len(), + Default::default() + ); + for peer in config.reserved_nodes.iter() { + let (id, _) = parse_and_add_to_topology(peer, &mut topology)?; + reserved_peers.insert(id); + } + RwLock::new(reserved_peers) + }; + + let expected_max_peers = config.max_peers as usize + config.reserved_nodes.len(); + + Ok(NetworkState { + topology: RwLock::new(topology), + max_outgoing_peers: config.min_peers, + max_incoming_peers: config.max_peers.saturating_sub(config.min_peers), + connections: RwLock::new(Connections { + peer_by_nodeid: FnvHashMap::with_capacity_and_hasher(expected_max_peers, Default::default()), + info_by_peer: FnvHashMap::with_capacity_and_hasher(expected_max_peers, Default::default()), + }), + reserved_only: atomic::AtomicBool::new(config.non_reserved_mode == NonReservedPeerMode::Deny), + reserved_peers, + next_node_index: atomic::AtomicUsize::new(0), + disabled_nodes: Mutex::new(Default::default()), + local_private_key, + local_public_key, + }) + } + + /// Returns the private key of the local node. + pub fn local_private_key(&self) -> &secio::SecioKeyPair { + &self.local_private_key + } + + /// Returns the public key of the local node. + pub fn local_public_key(&self) -> &PublicKey { + &self.local_public_key + } + + /// Returns a list of peers and addresses which we should try connect to. + /// + /// Because of expiration and back-off mechanisms, this list can change + /// by itself over time. The `Instant` that is returned corresponds to + /// the earlier known time when a new entry will be added automatically to + /// the list. + pub fn outgoing_connections_to_attempt(&self) -> (Vec<(PeerId, Multiaddr)>, Instant) { + // TODO: handle better + let connections = self.connections.read(); + + let num_to_attempt = if self.reserved_only.load(atomic::Ordering::Relaxed) { + 0 + } else { + let num_open_custom_connections = num_open_custom_connections(&connections, &self.reserved_peers.read()); + self.max_outgoing_peers.saturating_sub(num_open_custom_connections.unreserved_outgoing) + }; + + let topology = self.topology.read(); + let (list, change) = topology.addrs_to_attempt(); + let list = list + .filter(|&(peer, _)| { + // Filter out peers which we are already connected to. + let cur = match connections.peer_by_nodeid.get(peer) { + Some(e) => e, + None => return true + }; + + let infos = match connections.info_by_peer.get(&cur) { + Some(i) => i, + None => return true + }; + + !infos.protocols.iter().any(|(_, conn)| conn.is_alive()) + }) + .take(num_to_attempt as usize) + .map(|(addr, peer)| (addr.clone(), peer.clone())) + .collect(); + (list, change) + } + + /// Returns true if we are connected to any peer at all. + pub fn has_connected_peer(&self) -> bool { + !self.connections.read().peer_by_nodeid.is_empty() + } + + /// Get a list of all connected peers by id. + pub fn connected_peers(&self) -> Vec { + self.connections.read().peer_by_nodeid.values().cloned().collect() + } + + /// Returns true if the given `NodeIndex` is valid. + /// + /// `NodeIndex`s are never reused, so once this function returns `false` it + /// will never return `true` again for the same `NodeIndex`. + pub fn is_peer_connected(&self, peer: NodeIndex) -> bool { + self.connections.read().info_by_peer.contains_key(&peer) + } + + /// Reports the ping of the peer. Returned later by `session_info()`. + /// No-op if the `who` is not valid/expired. + pub fn report_ping_duration(&self, who: NodeIndex, ping: Duration) { + let mut connections = self.connections.write(); + let info = match connections.info_by_peer.get_mut(&who) { + Some(info) => info, + None => return, + }; + info.ping = Some(ping); + } + + /// If we're connected to a peer with the given protocol, returns + /// information about the connection. Otherwise, returns `None`. + pub fn session_info(&self, peer: NodeIndex, protocol: ProtocolId) -> Option { + let connections = self.connections.read(); + let info = match connections.info_by_peer.get(&peer) { + Some(info) => info, + None => return None, + }; + + let protocol_version = match info.protocols.iter().find(|&(ref p, _)| p == &protocol) { + Some(&(_, ref unique_connec)) => + if let Some(val) = unique_connec.poll() { + val.1 as u32 + } else { + return None + } + None => return None, + }; + + Some(SessionInfo { + id: None, // TODO: ???? what to do??? wrong format! + client_version: info.client_version.clone().take().unwrap_or(String::new()), + protocol_version, + capabilities: Vec::new(), // TODO: list of supported protocols ; hard + peer_capabilities: Vec::new(), // TODO: difference with `peer_capabilities`? + ping: info.ping, + originated: info.originated.unwrap_or(true), + remote_address: info.remote_addresses.get(0).map(|a| a.to_string()).unwrap_or_default(), + local_address: info.local_address.as_ref().map(|a| a.to_string()) + .unwrap_or(String::new()), + }) + } + + /// If we're connected to a peer with the given protocol, returns the + /// protocol version. Otherwise, returns `None`. + pub fn protocol_version(&self, peer: NodeIndex, protocol: ProtocolId) -> Option { + let connections = self.connections.read(); + let peer = match connections.info_by_peer.get(&peer) { + Some(peer) => peer, + None => return None, + }; + + peer.protocols.iter() + .find(|p| p.0 == protocol) + .and_then(|p| p.1.poll()) + .map(|(_, version)| version) + } + + /// Equivalent to `session_info(peer).map(|info| info.client_version)`. + pub fn peer_client_version(&self, peer: NodeIndex, protocol: ProtocolId) -> Option { + // TODO: implement more directly, without going through `session_info` + self.session_info(peer, protocol) + .map(|info| info.client_version) + } + + /// Adds an address discovered by Kademlia. + /// Note that we don't have to be connected to a peer to add an address. + /// If `connectable` is `true`, that means we have a hint from a remote that this node can be + /// connected to. + pub fn add_kad_discovered_addr(&self, node_id: &PeerId, addr: Multiaddr, connectable: bool) { + self.topology.write().add_kademlia_discovered_addr(node_id, addr, connectable) + } + + /// Returns the known multiaddresses of a peer. + /// + /// The boolean associated to each address indicates whether we're connected to it. + pub fn addrs_of_peer(&self, node_id: &PeerId) -> Vec<(Multiaddr, bool)> { + let topology = self.topology.read(); + // Note: I have no idea why, but fusing the two lines below fails the + // borrow check + let out: Vec<_> = topology + .addrs_of_peer(node_id).map(|(a, c)| (a.clone(), c)).collect(); + out + } + + /// Sets information about a peer. + /// + /// No-op if the node index is invalid. + pub fn set_node_info( + &self, + node_index: NodeIndex, + client_version: String + ) { + let mut connections = self.connections.write(); + let infos = match connections.info_by_peer.get_mut(&node_index) { + Some(i) => i, + None => return + }; + + infos.client_version = Some(client_version); + } + + /// Adds a peer to the internal peer store. + /// Returns an error if the peer address is invalid. + pub fn add_bootstrap_peer(&self, peer: &str) -> Result<(PeerId, Multiaddr), Error> { + parse_and_add_to_topology(peer, &mut self.topology.write()) + } + + /// Adds a reserved peer to the list of reserved peers. + /// Returns an error if the peer address is invalid. + pub fn add_reserved_peer(&self, peer: &str) -> Result<(), Error> { + let (id, _) = parse_and_add_to_topology(peer, &mut self.topology.write())?; + self.reserved_peers.write().insert(id); + Ok(()) + } + + /// Removes the peer from the list of reserved peers. If we're in reserved mode, drops any + /// active connection to this peer. + /// Returns an error if the peer address is invalid. + pub fn remove_reserved_peer(&self, peer: &str) -> Result<(), Error> { + let (id, _) = parse_and_add_to_topology(peer, &mut self.topology.write())?; + self.reserved_peers.write().remove(&id); + + // Dropping the peer if we're in reserved mode. + if self.reserved_only.load(atomic::Ordering::SeqCst) { + let mut connections = self.connections.write(); + if let Some(who) = connections.peer_by_nodeid.remove(&id) { + connections.info_by_peer.remove(&who); + // TODO: use drop_peer instead + } + } + + Ok(()) + } + + /// Set the non-reserved peer mode. + pub fn set_non_reserved_mode(&self, mode: NonReservedPeerMode) { + match mode { + NonReservedPeerMode::Accept => + self.reserved_only.store(false, atomic::Ordering::SeqCst), + NonReservedPeerMode::Deny => + // TODO: drop existing peers? + self.reserved_only.store(true, atomic::Ordering::SeqCst), + } + } + + /// Reports that we tried to connect to the given address but failed. + /// + /// This decreases the chance this address will be tried again in the future. + #[inline] + pub fn report_failed_to_connect(&self, addr: &Multiaddr) { + trace!(target: "sub-libp2p", "Failed to connect to {:?}", addr); + self.topology.write().report_failed_to_connect(addr); + } + + /// Returns the `NodeIndex` corresponding to a node id, or assigns a `NodeIndex` if none + /// exists. + /// + /// Returns an error if this node is on the list of disabled/banned nodes.. + pub fn assign_node_index( + &self, + node_id: &PeerId + ) -> Result { + // Check whether node is disabled. + // TODO: figure out the locking strategy here to avoid possible deadlocks + // TODO: put disabled_nodes in connections? + let mut disabled_nodes = self.disabled_nodes.lock(); + if let Some(timeout) = disabled_nodes.get(node_id).cloned() { + if timeout > Instant::now() { + debug!(target: "sub-libp2p", "Refusing peer {:?} because it is disabled", node_id); + return Err(IoError::new(IoErrorKind::ConnectionRefused, "peer is disabled")); + } else { + disabled_nodes.remove(node_id); + } + } + drop(disabled_nodes); + + let mut connections = self.connections.write(); + let connections = &mut *connections; + let peer_by_nodeid = &mut connections.peer_by_nodeid; + let info_by_peer = &mut connections.info_by_peer; + + let who = *peer_by_nodeid.entry(node_id.clone()).or_insert_with(|| { + let new_id = self.next_node_index.fetch_add(1, atomic::Ordering::Relaxed); + trace!(target: "sub-libp2p", "Creating new peer #{:?} for {:?}", new_id, node_id); + + info_by_peer.insert(new_id, PeerConnectionInfo { + protocols: Vec::new(), // TODO: Vec::with_capacity(num_registered_protocols), + kad_connec: UniqueConnec::empty(), + ping_connec: UniqueConnec::empty(), + id: node_id.clone(), + originated: None, + ping: None, + client_version: None, + local_address: None, + remote_addresses: Vec::with_capacity(1), + }); + + new_id + }); + + Ok(who) + } + + /// Notifies that we're connected to a node through an address. + /// + /// Returns an error if we refuse the connection. + /// + /// Note that is it legal to connection multiple times to the same node id through different + /// addresses and endpoints. + pub fn report_connected( + &self, + node_index: NodeIndex, + addr: &Multiaddr, + endpoint: Endpoint + ) -> Result<(), IoError> { + let mut connections = self.connections.write(); + + // TODO: double locking in this function ; although this has been reviewed to not deadlock + // as of the writing of this code, it is possible that a later change that isn't carefully + // reviewed triggers one + + if endpoint == Endpoint::Listener { + let stats = num_open_custom_connections(&connections, &self.reserved_peers.read()); + if stats.unreserved_incoming >= self.max_incoming_peers { + debug!(target: "sub-libp2p", "Refusing incoming connection from {} because we \ + reached max incoming peers", addr); + return Err(IoError::new(IoErrorKind::ConnectionRefused, + "maximum incoming peers reached")); + } + } + + let infos = match connections.info_by_peer.get_mut(&node_index) { + Some(i) => i, + None => return Ok(()) + }; + + if !infos.remote_addresses.iter().any(|a| a == addr) { + infos.remote_addresses.push(addr.clone()); + } + + if infos.originated.is_none() { + infos.originated = Some(endpoint == Endpoint::Dialer); + } + + self.topology.write().report_connected(addr, &infos.id); + + Ok(()) + } + + /// Returns the node id from a node index. + /// + /// Returns `None` if the node index is invalid. + pub fn node_id_from_index( + &self, + node_index: NodeIndex + ) -> Option { + let mut connections = self.connections.write(); + let infos = match connections.info_by_peer.get_mut(&node_index) { + Some(i) => i, + None => return None + }; + Some(infos.id.clone()) + } + + /// Obtains the `UniqueConnec` corresponding to the Kademlia connection to a peer. + /// + /// Returns `None` if the node index is invalid. + pub fn kad_connection( + &self, + node_index: NodeIndex + ) -> Option> { + let mut connections = self.connections.write(); + let infos = match connections.info_by_peer.get_mut(&node_index) { + Some(i) => i, + None => return None + }; + Some(infos.kad_connec.clone()) + } + + /// Obtains the `UniqueConnec` corresponding to the Ping connection to a peer. + /// + /// Returns `None` if the node index is invalid. + pub fn ping_connection( + &self, + node_index: NodeIndex + ) -> Option> { + let mut connections = self.connections.write(); + let infos = match connections.info_by_peer.get_mut(&node_index) { + Some(i) => i, + None => return None + }; + Some(infos.ping_connec.clone()) + } + + /// Cleans up inactive connections and returns a list of + /// connections to ping and identify. + pub fn cleanup_and_prepare_updates( + &self + ) -> Vec { + self.topology.write().cleanup(); + + let mut connections = self.connections.write(); + let connections = &mut *connections; + let peer_by_nodeid = &mut connections.peer_by_nodeid; + let info_by_peer = &mut connections.info_by_peer; + + let mut ret = Vec::with_capacity(info_by_peer.len()); + info_by_peer.retain(|&who, infos| { + // Remove the peer if neither Kad nor any protocol is alive. + if !infos.kad_connec.is_alive() && + !infos.protocols.iter().any(|(_, conn)| conn.is_alive()) + { + peer_by_nodeid.remove(&infos.id); + trace!(target: "sub-libp2p", "Cleaning up expired peer \ + #{:?} ({:?})", who, infos.id); + return false; + } + + if let Some(addr) = infos.remote_addresses.get(0) { + ret.push(PeriodicUpdate { + node_index: who, + peer_id: infos.id.clone(), + address: addr.clone(), + pinger: infos.ping_connec.clone(), + identify: infos.client_version.is_none(), + }); + } + true + }); + ret + } + + /// Obtains the `UniqueConnec` corresponding to a custom protocol connection to a peer. + /// + /// Returns `None` if the node index is invalid. + pub fn custom_proto( + &self, + node_index: NodeIndex, + protocol_id: ProtocolId, + ) -> Option, u8)>> { + let mut connections = self.connections.write(); + let infos = match connections.info_by_peer.get_mut(&node_index) { + Some(i) => i, + None => return None + }; + + if let Some((_, ref uconn)) = infos.protocols.iter().find(|&(prot, _)| prot == &protocol_id) { + return Some(uconn.clone()) + } + + let unique_connec = UniqueConnec::empty(); + infos.protocols.push((protocol_id.clone(), unique_connec.clone())); + Some(unique_connec) + } + + /// Sends some data to the given peer, using the sender that was passed + /// to the `UniqueConnec` of `custom_proto`. + pub fn send(&self, who: NodeIndex, protocol: ProtocolId, message: Bytes) -> Result<(), Error> { + if let Some(peer) = self.connections.read().info_by_peer.get(&who) { + let sender = peer.protocols.iter().find(|elem| elem.0 == protocol) + .and_then(|e| e.1.poll()) + .map(|e| e.0); + if let Some(sender) = sender { + sender.unbounded_send(message) + .map_err(|err| ErrorKind::Io(IoError::new(IoErrorKind::Other, err)))?; + Ok(()) + } else { + // We are connected to this peer, but not with the current + // protocol. + debug!(target: "sub-libp2p", + "Tried to send message to peer {} for which we aren't connected with the requested protocol", + who + ); + return Err(ErrorKind::PeerNotFound.into()) + } + } else { + debug!(target: "sub-libp2p", "Tried to send message to invalid peer ID {}", who); + return Err(ErrorKind::PeerNotFound.into()) + } + } + + /// Get the info on a peer, if there's an active connection. + pub fn peer_info(&self, who: NodeIndex) -> Option { + self.connections.read().info_by_peer.get(&who).map(Into::into) + } + + /// Reports that an attempt to make a low-level ping of the peer failed. + pub fn report_ping_failed(&self, who: NodeIndex) { + self.drop_peer(who); + } + + /// Disconnects a peer, if a connection exists (ie. drops the Kademlia + /// controller, and the senders that were stored in the `UniqueConnec` of + /// `custom_proto`). + pub fn drop_peer(&self, who: NodeIndex) { + let mut connections = self.connections.write(); + if let Some(peer_info) = connections.info_by_peer.remove(&who) { + trace!(target: "sub-libp2p", "Destroying peer #{} {:?} ; kademlia = {:?} ; num_protos = {:?}", + who, + peer_info.id, + peer_info.kad_connec.is_alive(), + peer_info.protocols.iter().filter(|c| c.1.is_alive()).count()); + let old = connections.peer_by_nodeid.remove(&peer_info.id); + debug_assert_eq!(old, Some(who)); + for addr in &peer_info.remote_addresses { + self.topology.write().report_disconnected(addr, + DisconnectReason::ClosedGracefully); // TODO: wrong reason + } + } + } + + /// Disconnects all the peers. + /// This destroys all the Kademlia controllers and the senders that were + /// stored in the `UniqueConnec` of `custom_proto`. + pub fn disconnect_all(&self) { + let mut connec = self.connections.write(); + *connec = Connections { + info_by_peer: FnvHashMap::with_capacity_and_hasher( + connec.peer_by_nodeid.capacity(), Default::default()), + peer_by_nodeid: FnvHashMap::with_capacity_and_hasher( + connec.peer_by_nodeid.capacity(), Default::default()), + }; + } + + /// Disables a peer for `PEER_DISABLE_DURATION`. This adds the peer to the + /// list of disabled peers, and drops any existing connections if + /// necessary (ie. drops the sender that was stored in the `UniqueConnec` + /// of `custom_proto`). + pub fn ban_peer(&self, who: NodeIndex, reason: &str) { + // TODO: what do we do if the peer is reserved? + // TODO: same logging as in drop_peer + let mut connections = self.connections.write(); + let peer_info = if let Some(peer_info) = connections.info_by_peer.remove(&who) { + if let &Some(ref client_version) = &peer_info.client_version { + info!(target: "network", "Peer {} (version: {}, addresses: {:?}) disabled. {}", who, client_version, peer_info.remote_addresses, reason); + } else { + info!(target: "network", "Peer {} (addresses: {:?}) disabled. {}", who, peer_info.remote_addresses, reason); + } + let old = connections.peer_by_nodeid.remove(&peer_info.id); + debug_assert_eq!(old, Some(who)); + peer_info + } else { + return + }; + + drop(connections); + let timeout = Instant::now() + PEER_DISABLE_DURATION; + self.disabled_nodes.lock().insert(peer_info.id.clone(), timeout); + } + + /// Flushes the caches to the disk. + /// + /// This is done in an atomical way, so that an error doesn't corrupt + /// anything. + pub fn flush_caches_to_disk(&self) -> Result<(), IoError> { + match self.topology.read().flush_to_disk() { + Ok(()) => { + debug!(target: "sub-libp2p", "Flushed JSON peer store to disk"); + Ok(()) + } + Err(err) => { + warn!(target: "sub-libp2p", "Failed to flush changes to JSON peer store: {}", err); + Err(err) + } + } + } +} + +impl Drop for NetworkState { + fn drop(&mut self) { + let _ = self.flush_caches_to_disk(); + } +} + +/// Periodic update that should be performed by the user of the network state. +pub struct PeriodicUpdate { + /// Index of the node in the network state. + pub node_index: NodeIndex, + /// Id of the peer. + pub peer_id: PeerId, + /// Address of the node to ping. + pub address: Multiaddr, + /// Object that allows pinging the node. + pub pinger: UniqueConnec, + /// The node should be identified as well. + pub identify: bool, +} + +struct OpenCustomConnectionsNumbers { + /// Total number of open and pending connections. + pub total: u32, + /// Unreserved incoming number of open and pending connections. + pub unreserved_incoming: u32, + /// Unreserved outgoing number of open and pending connections. + pub unreserved_outgoing: u32, +} + +/// Returns the number of open and pending connections with +/// custom protocols. +fn num_open_custom_connections(connections: &Connections, reserved_peers: &FnvHashSet) -> OpenCustomConnectionsNumbers { + let filtered = connections + .info_by_peer + .values() + .filter(|info| + info.protocols.iter().any(|&(_, ref connec)| + match connec.state() { + UniqueConnecState::Pending | UniqueConnecState::Full => true, + _ => false + } + ) + ); + + let mut total: u32 = 0; + let mut unreserved_incoming: u32 = 0; + let mut unreserved_outgoing: u32 = 0; + + for info in filtered { + total += 1; + let node_is_reserved = reserved_peers.contains(&info.id); + if !node_is_reserved { + if !info.originated.unwrap_or(true) { + unreserved_incoming += 1; + } else { + unreserved_outgoing += 1; + } + } + } + + OpenCustomConnectionsNumbers { + total, + unreserved_incoming, + unreserved_outgoing, + } +} + +/// Parses an address of the form `/ip4/x.x.x.x/tcp/x/p2p/xxxxxx`, and adds it +/// to the given topology. Returns the corresponding peer ID and multiaddr. +fn parse_and_add_to_topology( + addr_str: &str, + topology: &mut NetTopology +) -> Result<(PeerId, Multiaddr), Error> { + + let mut addr = addr_str.to_multiaddr().map_err(|_| ErrorKind::AddressParse)?; + let who = match addr.pop() { + Some(AddrComponent::P2P(key)) => + PeerId::from_multihash(key).map_err(|_| ErrorKind::AddressParse)?, + _ => return Err(ErrorKind::AddressParse.into()), + }; + + topology.add_bootstrap_addr(&who, addr.clone()); + Ok((who, addr)) +} + +/// Obtains or generates the local private key using the configuration. +fn obtain_private_key(config: &NetworkConfiguration) + -> Result { + if let Some(ref secret) = config.use_secret { + // Key was specified in the configuration. + secio::SecioKeyPair::secp256k1_raw_key(&secret[..]) + .map_err(|err| IoError::new(IoErrorKind::InvalidData, err)) + + } else { + if let Some(ref path) = config.net_config_path { + fs::create_dir_all(Path::new(path))?; + + // Try fetch the key from a the file containing th esecret. + let secret_path = Path::new(path).join(SECRET_FILE); + match load_private_key_from_file(&secret_path) { + Ok(s) => Ok(s), + Err(err) => { + // Failed to fetch existing file ; generate a new key + trace!(target: "sub-libp2p", + "Failed to load existing secret key file {:?}, generating new key ; err = {:?}", + secret_path, + err + ); + Ok(gen_key_and_try_write_to_file(&secret_path)) + } + } + + } else { + // No path in the configuration, nothing we can do except generate + // a new key. + let mut key: [u8; 32] = [0; 32]; + rand::rngs::EntropyRng::new().fill(&mut key); + Ok(secio::SecioKeyPair::secp256k1_raw_key(&key) + .expect("randomly-generated key with correct len should always be valid")) + } + } +} + +/// Tries to load a private key from a file located at the given path. +fn load_private_key_from_file

(path: P) + -> Result + where P: AsRef + { + fs::File::open(path) + .and_then(|mut file| { + // We are in 2018 and there is still no method on `std::io::Read` + // that directly returns a `Vec`. + let mut buf = Vec::new(); + file.read_to_end(&mut buf).map(|_| buf) + }) + .and_then(|content| + secio::SecioKeyPair::secp256k1_raw_key(&content) + .map_err(|err| IoError::new(IoErrorKind::InvalidData, err)) + ) +} + +/// Generates a new secret key and tries to write it to the given file. +/// Doesn't error if we couldn't open or write to the file. +fn gen_key_and_try_write_to_file

(path: P) -> secio::SecioKeyPair + where P: AsRef { + let raw_key: [u8; 32] = rand::rngs::EntropyRng::new().gen(); + let secio_key = secio::SecioKeyPair::secp256k1_raw_key(&raw_key) + .expect("randomly-generated key with correct len should always be valid"); + + // And store the newly-generated key in the file if possible. + // Errors that happen while doing so are ignored. + match open_priv_key_file(&path) { + Ok(mut file) => + match file.write_all(&raw_key) { + Ok(()) => (), + Err(err) => warn!(target: "sub-libp2p", + "Failed to write secret key in file {:?} ; err = {:?}", + path.as_ref(), + err + ), + }, + Err(err) => warn!(target: "sub-libp2p", + "Failed to store secret key in file {:?} ; err = {:?}", + path.as_ref(), + err + ), + } + + secio_key +} + +/// Opens a file containing a private key in write mode. +#[cfg(unix)] +fn open_priv_key_file

(path: P) -> Result + where P: AsRef +{ + use std::os::unix::fs::OpenOptionsExt; + fs::OpenOptions::new() + .write(true) + .create_new(true) + .mode(256 | 128) // 0o600 in decimal + .open(path) +} +/// Opens a file containing a private key in write mode. +#[cfg(not(unix))] +fn open_priv_key_file

(path: P) -> Result + where P: AsRef +{ + fs::OpenOptions::new() + .write(true) + .create_new(true) + .open(path) +} + +#[cfg(test)] +mod tests { + use libp2p::core::PublicKey; + use network_state::NetworkState; + + #[test] + fn refuse_disabled_peer() { + let state = NetworkState::new(&Default::default()).unwrap(); + let example_peer = PublicKey::Rsa(vec![1, 2, 3, 4]).into_peer_id(); + + let who = state.assign_node_index(&example_peer).unwrap(); + state.ban_peer(who, "Just a test"); + + assert!(state.assign_node_index(&example_peer).is_err()); + } +} diff --git a/core/network-libp2p/src/service.rs b/core/network-libp2p/src/service.rs new file mode 100644 index 0000000000000..4e82973893240 --- /dev/null +++ b/core/network-libp2p/src/service.rs @@ -0,0 +1,1437 @@ +// Copyright 2018 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 bytes::Bytes; +use {Error, ErrorKind, NetworkConfiguration, NetworkProtocolHandler}; +use {NonReservedPeerMode, NetworkContext, Severity, NodeIndex, ProtocolId}; +use parking_lot::RwLock; +use libp2p; +use libp2p::multiaddr::{AddrComponent, Multiaddr}; +use libp2p::kad::{KadSystem, KadConnecConfig, KadSystemConfig}; +use libp2p::kad::{KadIncomingRequest, KadConnecController, KadPeer}; +use libp2p::kad::{KadConnectionType, KadQueryEvent}; +use libp2p::identify::{IdentifyInfo, IdentifyOutput, IdentifySender}; +use libp2p::identify::{IdentifyProtocolConfig}; +use libp2p::core::{upgrade, Transport, MuxedTransport, ConnectionUpgrade}; +use libp2p::core::{Endpoint, PeerId as PeerstorePeerId, PublicKey}; +use libp2p::core::{SwarmController, UniqueConnecState}; +use libp2p::ping; +use libp2p::transport_timeout::TransportTimeout; +use {PacketId, SessionInfo, TimerToken}; +use rand; +use std::io::{Error as IoError, ErrorKind as IoErrorKind}; +use std::iter; +use std::net::SocketAddr; +use std::sync::Arc; +use std::sync::mpsc as sync_mpsc; +use std::thread; +use std::time::{Duration, Instant}; +use futures::{future, Future, stream, Stream, select_all}; +use futures::sync::{mpsc, oneshot}; +use tokio::runtime::current_thread; +use tokio_io::{AsyncRead, AsyncWrite}; +use tokio_timer::{Interval, Timeout}; + +use custom_proto::{RegisteredProtocol, RegisteredProtocols}; +use custom_proto::RegisteredProtocolOutput; +use network_state::{NetworkState, PeriodicUpdate}; +use timeouts; +use transport; + +/// IO Service with networking. +pub struct NetworkService { + shared: Arc, + + /// Holds the networking-running background thread alive. The `Option` is + /// only set to `None` in the destructor. + /// Sending a message on the channel will trigger the end of the + /// background thread. We can then wait on the join handle. + bg_thread: Option<(oneshot::Sender<()>, thread::JoinHandle<()>)>, +} + +/// Common struct shared throughout all the components of the service. +struct Shared { + /// Original configuration of the service. + config: NetworkConfiguration, + + /// Contains the state of the network. + network_state: NetworkState, + + /// Kademlia system. Contains the DHT. + kad_system: KadSystem, + + /// Configuration for the Kademlia upgrade. + kad_upgrade: KadConnecConfig, + + /// List of protocols available on the network. It is a logic error to + /// remove protocols from this list, and the code may assume that protocols + /// stay at the same index forever. + protocols: RegisteredProtocols>, + + /// Use this channel to send a timeout request to the background thread's + /// events loop. After the timeout, elapsed, it will call `timeout` on the + /// `NetworkProtocolHandler`. This can be closed if the background thread + /// is not running. The sender will be overwritten every time we start + /// the service. + timeouts_register_tx: mpsc::UnboundedSender<(Duration, (Arc, ProtocolId, TimerToken))>, + + /// Original address from the configuration, after being adjusted by the `Transport`. + // TODO: because we create the `Shared` before starting to listen, this + // has to be set later ; sort this out + original_listened_addr: RwLock>, + + /// Contains the addresses we known about ourselves. + listened_addrs: RwLock>, +} + +impl NetworkService { + /// Starts the networking service. + /// + /// Note that we could use an iterator for `protocols`, but having a + /// generic here is too much and crashes the Rust compiler. + pub fn new( + config: NetworkConfiguration, + protocols: Vec<(Arc, ProtocolId, &[(u8, u8)])> + ) -> Result { + let network_state = NetworkState::new(&config)?; + + let local_peer_id = network_state.local_public_key().clone() + .into_peer_id(); + for mut addr in config.listen_addresses.iter().cloned() { + addr.append(AddrComponent::P2P(local_peer_id.clone().into())); + info!(target: "sub-libp2p", "Local node address is: {}", addr); + } + + let kad_system = KadSystem::without_init(KadSystemConfig { + parallelism: 3, + local_peer_id: local_peer_id.clone(), + kbuckets_timeout: Duration::from_secs(600), + request_timeout: Duration::from_secs(10), + known_initial_peers: iter::empty(), + }); + + // Channel we use to signal success or failure of the bg thread + // initialization process. + let (init_tx, init_rx) = sync_mpsc::channel(); + // Channel the main thread uses to signal the bg thread that it + // should stop + let (close_tx, close_rx) = oneshot::channel(); + let (timeouts_register_tx, timeouts_register_rx) = mpsc::unbounded(); + + let listened_addrs = config.public_addresses.clone(); + + let shared = Arc::new(Shared { + network_state, + protocols: RegisteredProtocols(protocols.into_iter() + .map(|(handler, protocol, versions)| + RegisteredProtocol::new(handler.clone(), protocol, versions)) + .collect() + ), + kad_system, + kad_upgrade: KadConnecConfig::new(), + config, + timeouts_register_tx, + original_listened_addr: RwLock::new(Vec::new()), + listened_addrs: RwLock::new(listened_addrs), + }); + + // Initialize all the protocols now. + // TODO: what about failure to initialize? we can't uninitialize a protocol + // TODO: remove this `initialize` method eventually, as it's only used for timers + for protocol in shared.protocols.0.iter() { + protocol.custom_data().initialize(&NetworkContextImpl { + inner: shared.clone(), + protocol: protocol.id().clone(), + current_peer: None, + }); + } + + let shared_clone = shared.clone(); + let join_handle = thread::spawn(move || { + // Tokio runtime that is going to run everything in this thread. + let mut runtime = match current_thread::Runtime::new() { + Ok(c) => c, + Err(err) => { + let _ = init_tx.send(Err(err.into())); + return + } + }; + + let fut = match init_thread(shared_clone, timeouts_register_rx, close_rx) { + Ok(future) => { + debug!(target: "sub-libp2p", "Successfully started networking service"); + let _ = init_tx.send(Ok(())); + future + }, + Err(err) => { + let _ = init_tx.send(Err(err)); + return + } + }; + + match runtime.block_on(fut) { + Ok(()) => debug!(target: "sub-libp2p", "libp2p future finished"), + Err(err) => error!(target: "sub-libp2p", "error while running libp2p: {:?}", err), + } + }); + + init_rx.recv().expect("libp2p background thread panicked")?; + + Ok(NetworkService { + shared, + bg_thread: Some((close_tx, join_handle)), + }) + } + + /// Returns network configuration. + pub fn config(&self) -> &NetworkConfiguration { + &self.shared.config + } + + pub fn external_url(&self) -> Option { + // TODO: in the context of libp2p, it is hard to define what an external + // URL is, as different nodes can have multiple different ways to + // reach us + self.shared.original_listened_addr.read().get(0) + .map(|addr| + format!("{}/p2p/{}", addr, self.shared.kad_system.local_peer_id().to_base58()) + ) + } + + /// Get a list of all connected peers by id. + pub fn connected_peers(&self) -> Vec { + self.shared.network_state.connected_peers() + } + + /// Try to add a reserved peer. + pub fn add_reserved_peer(&self, peer: &str) -> Result<(), Error> { + // TODO: try to dial the peer? + self.shared.network_state.add_reserved_peer(peer) + } + + /// Try to remove a reserved peer. + pub fn remove_reserved_peer(&self, peer: &str) -> Result<(), Error> { + self.shared.network_state.remove_reserved_peer(peer) + } + + /// Set the non-reserved peer mode. + pub fn set_non_reserved_mode(&self, mode: NonReservedPeerMode) { + self.shared.network_state.set_non_reserved_mode(mode) + } + + /// Executes action in the network context + pub fn with_context(&self, protocol: ProtocolId, action: F) + where F: FnOnce(&NetworkContext) { + self.with_context_eval(protocol, action); + } + + /// Evaluates function in the network context + pub fn with_context_eval(&self, protocol: ProtocolId, action: F) + -> Option + where F: FnOnce(&NetworkContext) -> T { + if !self.shared.protocols.has_protocol(protocol) { + return None + } + + Some(action(&NetworkContextImpl { + inner: self.shared.clone(), + protocol: protocol.clone(), + current_peer: None, + })) + } +} + +impl Drop for NetworkService { + fn drop(&mut self) { + if let Some((close_tx, join)) = self.bg_thread.take() { + let _ = close_tx.send(()); + if let Err(e) = join.join() { + warn!(target: "sub-libp2p", "error while waiting on libp2p background thread: {:?}", e); + } + } + + debug_assert!(!self.shared.network_state.has_connected_peer()); + } +} + +#[derive(Clone)] +struct NetworkContextImpl { + inner: Arc, + protocol: ProtocolId, + current_peer: Option, +} + +impl NetworkContext for NetworkContextImpl { + fn send(&self, peer: NodeIndex, packet_id: PacketId, data: Vec) { + self.send_protocol(self.protocol, peer, packet_id, data) + } + + fn send_protocol( + &self, + protocol: ProtocolId, + peer: NodeIndex, + packet_id: PacketId, + data: Vec + ) { + debug_assert!(self.inner.protocols.has_protocol(protocol), + "invalid protocol id requested in the API of the libp2p networking"); + // TODO: could be "optimized" by building `message` only after checking the validity of + // the peer, but that's probably not worth the effort + let mut message = Bytes::with_capacity(1 + data.len()); + message.extend_from_slice(&[packet_id]); + message.extend_from_slice(&data); + if self.inner.network_state.send(peer, protocol, message).is_err() { + debug!(target: "sub-libp2p", "Sending to peer {} failed. Dropping.", peer); + self.inner.network_state.drop_peer(peer); + } + } + + fn respond(&self, packet_id: PacketId, data: Vec) { + if let Some(peer) = self.current_peer { + self.send_protocol(self.protocol, peer, packet_id, data) + } else { + panic!("respond() called outside of a received message"); + } + } + + fn report_peer(&self, peer: NodeIndex, reason: Severity) { + if let Some(info) = self.inner.network_state.peer_info(peer) { + if let Some(client_version) = info.client_version { + info!(target: "sub-libp2p", + "Peer {} ({:?} {}) reported by client: {}", + peer, + info.remote_address, + client_version, + reason + ); + } else { + info!(target: "sub-libp2p", "Peer {} reported by client: {}", peer, reason); + } + } + match reason { + Severity::Bad(reason) => self.inner.network_state.ban_peer(peer, reason), + Severity::Useless(_) => self.inner.network_state.drop_peer(peer), + Severity::Timeout => self.inner.network_state.drop_peer(peer), + } + } + + fn is_expired(&self) -> bool { + if let Some(current_peer) = self.current_peer { + !self.inner.network_state.is_peer_connected(current_peer) + } else { + // TODO: is this correct? + true + } + } + + fn register_timer(&self, token: usize, duration: Duration) + -> Result<(), Error> { + let handler = self.inner.protocols + .find_protocol(self.protocol) + .ok_or(ErrorKind::BadProtocol)? + .custom_data() + .clone(); + self.inner.timeouts_register_tx + .unbounded_send((duration, (handler, self.protocol, token))) + .map_err(|err| ErrorKind::Io(IoError::new(IoErrorKind::Other, err)))?; + Ok(()) + } + + fn peer_client_version(&self, peer: NodeIndex) -> String { + // Devp2p returns "unknown" on unknown peer ID, so we do the same. + self.inner.network_state.peer_client_version(peer, self.protocol) + .unwrap_or_else(|| "unknown".to_string()) + } + + fn session_info(&self, peer: NodeIndex) -> Option { + self.inner.network_state.session_info(peer, self.protocol) + } + + fn protocol_version(&self, protocol: ProtocolId, peer: NodeIndex) -> Option { + self.inner.network_state.protocol_version(peer, protocol) + } + + fn subprotocol_name(&self) -> ProtocolId { + self.protocol.clone() + } +} + +/// Builds the main `Future` for the network service. +/// +/// - `timeouts_register_rx` should receive newly-registered timeouts. +/// - `close_rx` should be triggered when we want to close the network. +fn init_thread( + shared: Arc, + timeouts_register_rx: mpsc::UnboundedReceiver< + (Duration, (Arc, ProtocolId, TimerToken)) + >, + close_rx: oneshot::Receiver<()> +) -> Result, Error> { + // Build the transport layer. + let transport = { + let base = transport::build_transport( + shared.network_state.local_private_key().clone() + ); + + let base = base.map_err_dial({ + let shared = shared.clone(); + move |err, addr| { + trace!(target: "sub-libp2p", "Failed to dial {}: {:?}", addr, err); + shared.network_state.report_failed_to_connect(&addr); + err + } + }); + + let shared = shared.clone(); + Transport::and_then(base, move |(peer_id, stream), endpoint, remote_addr| { + remote_addr.and_then(move |remote_addr| { + if &peer_id == shared.kad_system.local_peer_id() { + // TODO: this happens very frequently for now and floods the logs + //warn!(target: "sub-libp2p", "Refusing connection from our local peer id"); + return Err(IoErrorKind::ConnectionRefused.into()) + } + + // TODO: there's a possible race condition here if `cleanup_and_prepare_updates` is + // called between `assign_node_index` and one of `kad_connec`, `unique_connec`, + // etc. ; in practice though, it is very unlikely to happen + let node_index = shared.network_state.assign_node_index(&peer_id)?; + shared.network_state.report_connected(node_index, &remote_addr, endpoint)?; + let out = TransportOutput { + socket: stream, + node_index, + original_addr: remote_addr.clone(), + }; + Ok((out, future::ok(remote_addr))) + }) + }) + }; + + // Build the swarm. The swarm is the single entry point where successfully + // negotiated protocols arrive. + let (swarm_controller, swarm_events) = { + let upgraded_transport = transport.clone() + .and_then({ + let shared = shared.clone(); + move |out, endpoint, client_addr| { + let node_index = out.node_index; + let original_addr = out.original_addr; + let listener_upgrade = upgrade::or(upgrade::or(upgrade::or( + upgrade::map(shared.kad_upgrade.clone(), move |(c, f)| FinalUpgrade::Kad(node_index, c, f)), + upgrade::map(IdentifyProtocolConfig, move |id| FinalUpgrade::from((node_index, id, original_addr)))), + upgrade::map(ping::Ping, move |out| FinalUpgrade::from((node_index, out)))), + upgrade::map(DelayedProtosList(shared), move |c| FinalUpgrade::Custom(node_index, c))); + upgrade::apply(out.socket, listener_upgrade, endpoint, client_addr) + } + }); + let shared = shared.clone(); + + libp2p::core::swarm( + upgraded_transport, + move |upgrade, _client_addr| + listener_handle(shared.clone(), upgrade) + ) + }; + + // Listen on multiaddresses. + for addr in &shared.config.listen_addresses { + match swarm_controller.listen_on(addr.clone()) { + Ok(new_addr) => { + debug!(target: "sub-libp2p", "Libp2p listening on {}", new_addr); + shared.original_listened_addr.write().push(new_addr.clone()); + }, + Err(_) => { + warn!(target: "sub-libp2p", "Can't listen on {}, protocol not supported", addr); + return Err(ErrorKind::BadProtocol.into()) + }, + } + } + + // Explicitely connect to _all_ the boostrap nodes as a temporary measure. + for bootnode in shared.config.boot_nodes.iter() { + match shared.network_state.add_bootstrap_peer(bootnode) { + Ok((who, addr)) => { + trace!(target: "sub-libp2p", "Dialing bootnode {:?} through {}", who, addr); + for proto in shared.protocols.0.clone().into_iter() { + open_peer_custom_proto( + shared.clone(), + transport.clone(), + addr.clone(), + Some(who.clone()), + proto, + &swarm_controller + ) + } + }, + Err(Error(ErrorKind::AddressParse, _)) => { + // fallback: trying with IP:Port + let multi = match bootnode.parse::() { + Ok(SocketAddr::V4(socket)) => + format!("/ip4/{}/tcp/{}", socket.ip(), socket.port()).parse::(), + Ok(SocketAddr::V6(socket)) => + format!("/ip6/{}/tcp/{}", socket.ip(), socket.port()).parse::(), + _ => { + warn!(target: "sub-libp2p", "Not a valid Bootnode Address {:}", bootnode); + continue; + } + }; + + if let Ok(addr) = multi { + trace!(target: "sub-libp2p", "Missing NodeIndex for Bootnode {:}. Querying", bootnode); + for proto in shared.protocols.0.clone().into_iter() { + open_peer_custom_proto( + shared.clone(), + transport.clone(), + addr.clone(), + None, + proto, + &swarm_controller + ) + } + } else { + warn!(target: "sub-libp2p", "Not a valid Bootnode Address {:}", bootnode); + continue; + } + }, + Err(err) => warn!(target:"sub-libp2p", "Couldn't parse Bootnode Address: {}", err), + } + } + + let outgoing_connections = Interval::new(Instant::now(), Duration::from_secs(5)) + .map_err(|err| IoError::new(IoErrorKind::Other, err)) + .for_each({ + let shared = shared.clone(); + let transport = transport.clone(); + let swarm_controller = swarm_controller.clone(); + move |_| { + connect_to_nodes(shared.clone(), transport.clone(), &swarm_controller); + Ok(()) + } + }); + + // Build the timeouts system for the `register_timeout` function. + // (note: this has nothing to do with socket timeouts) + let timeouts = timeouts::build_timeouts_stream(timeouts_register_rx) + .for_each({ + let shared = shared.clone(); + move |(handler, protocol_id, timer_token)| { + handler.timeout(&NetworkContextImpl { + inner: shared.clone(), + protocol: protocol_id, + current_peer: None, + }, timer_token); + Ok(()) + } + }) + .then(|val| { + warn!(target: "sub-libp2p", "Timeouts stream closed unexpectedly: {:?}", val); + val + }); + + // Start the process of periodically discovering nodes to connect to. + let discovery = start_kademlia_discovery(shared.clone(), + transport.clone(), swarm_controller.clone()); + + // Start the process of pinging the active nodes on the network. + let periodic = start_periodic_updates(shared.clone(), transport, swarm_controller); + + let futures: Vec>> = vec![ + Box::new(swarm_events.for_each(|_| Ok(()))), + Box::new(discovery), + Box::new(periodic), + Box::new(outgoing_connections), + Box::new(timeouts), + Box::new(close_rx.map_err(|err| IoError::new(IoErrorKind::Other, err))), + ]; + + Ok( + select_all(futures) + .and_then(move |_| { + debug!(target: "sub-libp2p", "Networking ended ; disconnecting all peers"); + shared.network_state.disconnect_all(); + Ok(()) + }) + .map_err(|(r, _, _)| r) + ) +} + +/// Output of the common transport layer. +struct TransportOutput { + socket: S, + node_index: NodeIndex, + original_addr: Multiaddr, +} + +/// Enum of all the possible protocols our service handles. +enum FinalUpgrade { + Kad(NodeIndex, KadConnecController, Box + Send>), + /// The remote identification system, and the multiaddress we see the remote as. + IdentifyListener(NodeIndex, IdentifySender, Multiaddr), + /// The remote information about the address they see us as. + IdentifyDialer(NodeIndex, IdentifyInfo, Multiaddr), + PingDialer(NodeIndex, ping::Pinger, Box + Send>), + PingListener(NodeIndex, Box + Send>), + /// `Custom` means anything not in the core libp2p and is handled + /// by `CustomProtoConnectionUpgrade`. + Custom(NodeIndex, RegisteredProtocolOutput>), +} + +impl From<(NodeIndex, ping::PingOutput)> for FinalUpgrade { + fn from((node_index, out): (NodeIndex, ping::PingOutput)) -> FinalUpgrade { + match out { + ping::PingOutput::Ponger(processing) => + FinalUpgrade::PingListener(node_index, processing), + ping::PingOutput::Pinger { pinger, processing } => + FinalUpgrade::PingDialer(node_index, pinger, processing), + } + } +} + +impl From<(NodeIndex, IdentifyOutput, Multiaddr)> for FinalUpgrade { + fn from((node_index, out, addr): (NodeIndex, IdentifyOutput, Multiaddr)) -> FinalUpgrade { + match out { + IdentifyOutput::RemoteInfo { info, observed_addr } => + FinalUpgrade::IdentifyDialer(node_index, info, observed_addr), + IdentifyOutput::Sender { sender } => + FinalUpgrade::IdentifyListener(node_index, sender, addr), + } + } +} + +/// Called whenever we successfully open a multistream with a remote. +fn listener_handle<'a, C>( + shared: Arc, + upgrade: FinalUpgrade, +) -> Box + Send + 'a> + where C: AsyncRead + AsyncWrite + Send + 'a { + match upgrade { + FinalUpgrade::Kad(node_index, controller, kademlia_stream) => { + trace!(target: "sub-libp2p", "Opened kademlia substream with #{:?}", node_index); + match handle_kademlia_connection(shared, node_index, controller, kademlia_stream) { + Ok(fut) => Box::new(fut) as Box<_>, + Err(err) => Box::new(future::err(err)) as Box<_>, + } + }, + + FinalUpgrade::IdentifyListener(node_index, sender, original_addr) => { + trace!(target: "sub-libp2p", "Sending back identification info to #{}", node_index); + sender.send( + IdentifyInfo { + public_key: shared.network_state.local_public_key().clone(), + protocol_version: concat!("substrate/", + env!("CARGO_PKG_VERSION")).to_owned(), // TODO: ? + agent_version: concat!("substrate/", + env!("CARGO_PKG_VERSION")).to_owned(), + listen_addrs: shared.listened_addrs.read().clone(), + protocols: Vec::new(), // TODO: protocols_to_report, + }, + &original_addr + ) + }, + + FinalUpgrade::IdentifyDialer(node_index, info, observed_addr) => { + process_identify_info(&shared, node_index, &info, &observed_addr); + Box::new(future::ok(())) + }, + + FinalUpgrade::PingListener(node_index, future) => { + trace!(target: "sub-libp2p", "Received ping substream from #{}", node_index); + future + }, + + FinalUpgrade::PingDialer(node_index, pinger, future) => { + let ping_connec = match shared.network_state.ping_connection(node_index) { + Some(p) => p, + None => return Box::new(future::ok(())) as Box<_> + }; + trace!(target: "sub-libp2p", "Successfully opened ping substream with #{}", node_index); + let fut = ping_connec.tie_or_passthrough(pinger, future); + Box::new(fut) as Box<_> + }, + + FinalUpgrade::Custom(node_index, custom_proto_out) => { + // A "custom" protocol is one that is part of substrate and not part of libp2p. + let shared = shared.clone(); + let fut = handle_custom_connection(shared, node_index, custom_proto_out); + Box::new(fut) as Box<_> + }, + } +} + +/// Handles a newly-opened Kademlia connection. +fn handle_kademlia_connection( + shared: Arc, + node_index: NodeIndex, + controller: KadConnecController, + kademlia_stream: Box + Send> +) -> Result, IoError> { + let kad_connec = match shared.network_state.kad_connection(node_index) { + Some(kad) => kad, + None => return Err(IoError::new(IoErrorKind::Other, "node no longer exists")), + }; + + let node_id = match shared.network_state.node_id_from_index(node_index) { + Some(id) => id, + None => return Err(IoError::new(IoErrorKind::Other, "node no longer exists")), + }; + + let node_id2 = node_id.clone(); + let future = future::loop_fn(kademlia_stream, move |kademlia_stream| { + let shared = shared.clone(); + let node_id = node_id.clone(); + + let next = kademlia_stream + .into_future() + .map_err(|(err, _)| err); + + Timeout::new(next, Duration::from_secs(20)) + .map_err(|err| + // TODO: improve the error reporting here, but tokio-timer's API is bad + IoError::new(IoErrorKind::Other, err) + ) + .and_then(move |(req, rest)| { + shared.kad_system.update_kbuckets(node_id); + match req { + Some(KadIncomingRequest::FindNode { searched, responder }) => { + let resp = build_kademlia_response(&shared, &searched); + trace!(target: "sub-libp2p", "Responding to Kad {:?} with {:?}", searched, resp); + responder.respond(resp) + }, + Some(KadIncomingRequest::PingPong) => (), + None => return Ok(future::Loop::Break(())) + } + Ok(future::Loop::Continue(rest)) + }) + }).then(move |val| { + trace!(target: "sub-libp2p", "Closed Kademlia connection with #{} {:?} => {:?}", node_index, node_id2, val); + val + }); + + Ok(kad_connec.tie_or_passthrough(controller, future)) +} + +/// When a remote performs a `FIND_NODE` Kademlia request for `searched`, +/// this function builds the response to send back. +fn build_kademlia_response( + shared: &Arc, + searched: &PeerstorePeerId +) -> Vec { + shared.kad_system + .known_closest_peers(searched) + .map(move |who| { + if who == *shared.kad_system.local_peer_id() { + KadPeer { + node_id: who.clone(), + multiaddrs: shared.listened_addrs.read().clone(), + connection_ty: KadConnectionType::Connected, + } + } else { + let mut addrs = shared.network_state.addrs_of_peer(&who); + let connected = addrs.iter().any(|&(_, conn)| conn); + // The Kademlia protocol of libp2p doesn't allow specifying which address is valid + // and which is outdated, therefore in order to stay honest towards the network + // we only report the addresses we're connected to if we're connected to any. + if connected { + addrs = addrs.into_iter() + .filter_map(|(a, c)| if c { Some((a, c)) } else { None }) + .collect(); + } + + KadPeer { + node_id: who.clone(), + multiaddrs: addrs.into_iter().map(|(a, _)| a).collect(), + connection_ty: if connected { + KadConnectionType::Connected + } else { + KadConnectionType::NotConnected + }, + } + } + }) + // TODO: we really want to remove nodes with no multiaddress from + // the results, but a flaw in the Kad protocol of libp2p makes it + // impossible to return empty results ; therefore we must at least + // return ourselves + .filter(|p| p.node_id == *shared.kad_system.local_peer_id() || + !p.multiaddrs.is_empty()) + .take(20) + .collect::>() +} + +/// Handles a newly-opened connection to a remote with a custom protocol +/// (eg. `/substrate/dot/0`). +/// Returns a future that corresponds to when the handling is finished. +fn handle_custom_connection( + shared: Arc, + node_index: NodeIndex, + custom_proto_out: RegisteredProtocolOutput> +) -> Box + Send> { + let handler = custom_proto_out.custom_data; + let protocol_id = custom_proto_out.protocol_id; + + // Determine the ID of this peer, or drop the connection if the peer is disabled, + // if we reached `max_peers`, or a similar reason. + // TODO: is there a better way to refuse connections than to drop the + // newly-opened substream? should we refuse the connection + // beforehand? + let unique_connec = match shared.network_state.custom_proto( + node_index, + protocol_id, + ) { + Some(c) => c, + None => return Box::new(future::err(IoErrorKind::Other.into())) as Box<_>, + }; + + if let UniqueConnecState::Full = unique_connec.state() { + debug!(target: "sub-libp2p", + "Interrupting connection attempt to #{} with {:?} because we're already connected", + node_index, + custom_proto_out.protocol_id + ); + return Box::new(future::ok(())) as Box<_> + } + + struct ProtoDisconnectGuard { + inner: Arc, + who: NodeIndex, + handler: Arc, + protocol: ProtocolId, + print_log_message: bool, + } + + impl Drop for ProtoDisconnectGuard { + fn drop(&mut self) { + if self.print_log_message { + info!(target: "sub-libp2p", + "Node {:?} with peer ID {} through protocol {:?} disconnected", + self.inner.network_state.node_id_from_index(self.who), + self.who, + self.protocol + ); + } + self.handler.disconnected(&NetworkContextImpl { + inner: self.inner.clone(), + protocol: self.protocol, + current_peer: Some(self.who), + }, &self.who); + + // When any custom protocol drops, we drop the peer entirely. + // TODO: is this correct? + self.inner.network_state.drop_peer(self.who); + } + } + + let mut dc_guard = ProtoDisconnectGuard { + inner: shared.clone(), + who: node_index, + handler: handler.clone(), + protocol: protocol_id, + print_log_message: true, + }; + + let fut = custom_proto_out.incoming + .for_each({ + let shared = shared.clone(); + let handler = handler.clone(); + move |(packet_id, data)| { + if let Some(id) = shared.network_state.node_id_from_index(node_index) { + shared.kad_system.update_kbuckets(id); + } + handler.read(&NetworkContextImpl { + inner: shared.clone(), + protocol: protocol_id, + current_peer: Some(node_index.clone()), + }, &node_index, packet_id, &data); + Ok(()) + } + }); + + let val = (custom_proto_out.outgoing, custom_proto_out.protocol_version); + let final_fut = unique_connec.tie_or_stop(val, fut) + .then(move |val| { + info!(target: "sub-libp2p", "Finishing future for proto {:?} with {:?} => {:?}", + protocol_id, node_index, val); + // Makes sure that `dc_guard` is kept alive until here. + dc_guard.print_log_message = false; + drop(dc_guard); + val + }); + + debug!(target: "sub-libp2p", + "Successfully connected to {:?} (peer id #{}) with protocol {:?} version {}", + shared.network_state.node_id_from_index(node_index), + node_index, + protocol_id, + custom_proto_out.protocol_version + ); + + handler.connected(&NetworkContextImpl { + inner: shared.clone(), + protocol: protocol_id, + current_peer: Some(node_index), + }, &node_index); + + Box::new(final_fut) as Box<_> +} + +/// Randomly discovers peers to connect to. +/// This works by running a round at a regular interval, and skipping if we +/// reached `min_peers`. When we are over `min_peers`, we stop trying to dial +/// nodes and only accept incoming connections. +fn start_kademlia_discovery( + shared: Arc, + transport: T, + swarm_controller: SwarmController + Send>> +) -> Box + Send> + where T: MuxedTransport> + Clone + Send + 'static, + T::Dial: Send, + T::MultiaddrFuture: Send + 'static, + T::Listener: Send, + T::ListenerUpgrade: Send, + T::Incoming: Send, + T::IncomingUpgrade: Send, + To: AsyncRead + AsyncWrite + Send + 'static, + St: MuxedTransport> + Clone + Send + 'static, + St::Dial: Send, + St::MultiaddrFuture: Send, + St::Listener: Send, + St::ListenerUpgrade: Send, + St::Incoming: Send, + St::IncomingUpgrade: Send, + C: Send + 'static { + let kad_init = shared.kad_system.perform_initialization({ + let shared = shared.clone(); + let transport = transport.clone(); + let swarm_controller = swarm_controller.clone(); + move |who| + obtain_kad_connection( + shared.clone(), + who.clone(), + transport.clone(), + swarm_controller.clone() + ) + }); + + // We perform a random Kademlia query at a regular interval. + let discovery = Interval::new(Instant::now(), Duration::from_secs(32)) + // TODO: add a timeout to the lookups? + .map_err(|err| IoError::new(IoErrorKind::Other, err)) + .for_each({ + let shared = shared.clone(); + let transport = transport.clone(); + let swarm_controller = swarm_controller.clone(); + move |_| { + let _ = shared.network_state.flush_caches_to_disk(); + perform_kademlia_query(shared.clone(), transport.clone(), swarm_controller.clone()) + } + }); + + let final_future = kad_init + .select(discovery) + .map_err(|(err, _)| err) + .and_then(|(_, rest)| rest); + + // Note that we use a Box in order to speed compilation time. + Box::new(final_future) as Box + Send> +} + +/// Performs a kademlia request to a random node. +/// Note that we don't actually care about the results, so the future +/// produces `()`. +fn perform_kademlia_query( + shared: Arc, + transport: T, + swarm_controller: SwarmController + Send>> +) -> Box + Send> + where T: MuxedTransport> + Clone + Send + 'static, + T::MultiaddrFuture: Send + 'static, + T::Dial: Send, + T::Listener: Send, + T::ListenerUpgrade: Send, + T::Incoming: Send, + T::IncomingUpgrade: Send, + To: AsyncRead + AsyncWrite + Send + 'static, + St: MuxedTransport> + Send + Clone + 'static, + St::Dial: Send, + St::MultiaddrFuture: Send, + St::Listener: Send, + St::ListenerUpgrade: Send, + St::Incoming: Send, + St::IncomingUpgrade: Send, + C: Send + 'static { + // Query the node IDs that are closest to a random ID. + // Note that the randomness doesn't have to be secure, as this only + // influences which nodes we end up being connected to. + let random_key = PublicKey::Ed25519((0 .. 32) + .map(|_| -> u8 { rand::random() }).collect()); + let random_peer_id = random_key.into_peer_id(); + trace!(target: "sub-libp2p", "Start kademlia discovery for {:?}", random_peer_id); + + let future = shared.clone() + .kad_system + .find_node(random_peer_id, { + let shared = shared.clone(); + let transport = transport.clone(); + let swarm_controller = swarm_controller.clone(); + move |who| obtain_kad_connection(shared.clone(), who.clone(), + transport.clone(), swarm_controller.clone()) + }) + .filter_map(move |event| + match event { + KadQueryEvent::PeersReported(peers) => { + for peer in peers { + let connected = match peer.connection_ty { + KadConnectionType::NotConnected => false, + KadConnectionType::Connected => true, + KadConnectionType::CanConnect => true, + KadConnectionType::CannotConnect => continue, + }; + + for addr in peer.multiaddrs { + shared.network_state.add_kad_discovered_addr( + &peer.node_id, + addr, + connected + ); + } + } + None + }, + KadQueryEvent::Finished(_) => Some(()), + } + ) + .into_future() + .map_err(|(err, _)| err) + .map(|_| ()); + + // Note that we use a `Box` in order to speed up compilation. + Box::new(future) as Box + Send> +} + +/// Connects to additional nodes, if necessary. +fn connect_to_nodes( + shared: Arc, + base_transport: T, + swarm_controller: &SwarmController + Send>> +) + where T: MuxedTransport> + Clone + Send + 'static, + T::MultiaddrFuture: Send + 'static, + T::Dial: Send, + T::Listener: Send, + T::ListenerUpgrade: Send, + T::Incoming: Send, + T::IncomingUpgrade: Send, + To: AsyncRead + AsyncWrite + Send + 'static, + St: MuxedTransport> + Clone + Send + 'static, + St::Dial: Send, + St::MultiaddrFuture: Send, + St::Listener: Send, + St::ListenerUpgrade: Send, + St::Incoming: Send, + St::IncomingUpgrade: Send, + C: Send + 'static { + let (addrs, _will_change) = shared.network_state.outgoing_connections_to_attempt(); + + for (peer, addr) in addrs.into_iter() { + // Try to dial that node for each registered protocol. Since dialing + // upgrades the connection to use multiplexing, dialing multiple times + // should automatically open multiple substreams. + for proto in shared.protocols.0.clone().into_iter() { + open_peer_custom_proto( + shared.clone(), + base_transport.clone(), + addr.clone(), + Some(peer.clone()), + proto, + swarm_controller + ) + } + } +} + +/// Dials the given address for the given protocol and using the given `swarm_controller`. +/// +/// This function *always* performs a dial, and doesn't check whether we already have an existing +/// connection to the remote. This is expected to be checked by the caller. +/// +/// The dialing will fail if the obtained peer ID doesn't match the expected ID. This is an +/// opinionated decision, as we could just let the new connection through. But we decide not to. +/// If `None` is passed for the expected peer ID, we always accept the connection. +fn open_peer_custom_proto( + shared: Arc, + base_transport: T, + addr: Multiaddr, + expected_peer_id: Option, + proto: RegisteredProtocol>, + swarm_controller: &SwarmController + Send>> +) + where T: MuxedTransport> + Clone + Send + 'static, + T::MultiaddrFuture: Send + 'static, + T::Dial: Send, + T::Listener: Send, + T::ListenerUpgrade: Send, + T::Incoming: Send, + T::IncomingUpgrade: Send, + To: AsyncRead + AsyncWrite + Send + 'static, + St: MuxedTransport> + Clone + Send + 'static, + St::Dial: Send, + St::MultiaddrFuture: Send, + St::Listener: Send, + St::ListenerUpgrade: Send, + St::Incoming: Send, + St::IncomingUpgrade: Send, + C: Send + 'static, +{ + let proto_id = proto.id(); + + let with_proto = base_transport + .and_then(move |out, endpoint, client_addr| { + let node_index = out.node_index; + upgrade::apply(out.socket, proto, endpoint, client_addr) + .map(move |(custom, client_addr)| + ((node_index, FinalUpgrade::Custom(node_index, custom)), client_addr)) + }); + + let with_timeout = TransportTimeout::new(with_proto, Duration::from_secs(20)); + + if let Some(expected_peer_id) = expected_peer_id { + let expected_node_index = match shared.network_state.assign_node_index(&expected_peer_id) { + Ok(i) => i, + Err(_) => return, + }; + + let unique_connec = match shared.network_state.custom_proto(expected_node_index, proto_id) { + Some(uc) => uc, + None => return, + }; + + let with_peer_check = with_timeout + .and_then(move |(node_index, custom), _, client_addr| { + if node_index == expected_node_index { + future::ok((custom, client_addr)) + } else { + future::err(IoError::new(IoErrorKind::ConnectionRefused, "Peer id mismatch")) + } + }); + + trace!(target: "sub-libp2p", + "Opening connection to {:?} through {} with proto {:?}", + expected_peer_id, + addr, + proto_id + ); + + let _ = unique_connec.dial(swarm_controller, &addr, with_peer_check); + + } else { + let trans = with_timeout.map(|(_, out), _| out); + if let Err(addr) = swarm_controller.dial(addr, trans) { + debug!(target: "sub-libp2p", "Failed to dial {:?}", addr); + } + } +} + +/// Obtain a Kademlia connection to the given peer. +fn obtain_kad_connection( + shared: Arc, + who: PeerstorePeerId, + transport: T, + swarm_controller: SwarmController + Send>> +) -> Box + Send> + where T: MuxedTransport> + Clone + Send + 'static, + T::MultiaddrFuture: Send + 'static, + T::Dial: Send, + T::Listener: Send, + T::ListenerUpgrade: Send, + T::Incoming: Send, + T::IncomingUpgrade: Send, + To: AsyncRead + AsyncWrite + Send + 'static, + St: MuxedTransport> + Clone + Send + 'static, + St::Dial: Send, + St::MultiaddrFuture: Send, + St::Listener: Send, + St::ListenerUpgrade: Send, + St::Incoming: Send, + St::IncomingUpgrade: Send, + C: Send + 'static { + let kad_upgrade = shared.kad_upgrade.clone(); + let transport = transport + .and_then(move |out, endpoint, client_addr| { + let node_index = out.node_index; + upgrade::apply(out.socket, kad_upgrade.clone(), endpoint, client_addr) + .map(move |((ctrl, fut), addr)| (FinalUpgrade::Kad(node_index, ctrl, fut), addr)) + }); + + // This function consists in trying all the addresses we know one by one until we find + // one that works. + // + // This `future` returns a Kad controller, or an error if all dialing attempts failed. + let future = stream::iter_ok(shared.network_state.addrs_of_peer(&who)) + .and_then(move |addr| { + let node_index = shared.network_state.assign_node_index(&who)?; + let kad = match shared.network_state.kad_connection(node_index) { + Some(kad) => kad, + None => return Err(IoError::new(IoErrorKind::Other, "node no longer exists")), + }; + Ok((kad, addr)) + }) + .and_then(move |(unique_connec, addr)| { + unique_connec.dial(&swarm_controller, &addr.0, transport.clone()) + }) + .then(|result| -> Result<_, ()> { Ok(result.ok()) }) + .filter_map(|result| result) + .into_future() + .map_err(|_| -> IoError { unreachable!("all items always succeed") }) + .and_then(|(kad, _)| kad.ok_or_else(|| IoErrorKind::ConnectionRefused.into())); + + // Note that we use a Box in order to speed up compilation. + Box::new(future) as Box + Send> +} + +/// Processes the identification information that we received about a node. +fn process_identify_info( + shared: &Shared, + node_index: NodeIndex, + info: &IdentifyInfo, + observed_addr: &Multiaddr, +) { + trace!(target: "sub-libp2p", "Received identification info from #{}", node_index); + + shared.network_state.set_node_info(node_index, info.agent_version.clone()); + + for original_listened_addr in &*shared.original_listened_addr.read() { + // TODO: we're using a hack here ; ideally we would call `nat_traversal` on our + // `Transport` ; but that's complicated to pass around ; we could put it in a `Box` in + // `Shared`, but since our transport doesn't implement `Send` (libp2p doesn't implement + // `Send` on modifiers), we can't. Instead let's just recreate a transport locally every + // time. + let transport = libp2p::tcp::TcpConfig::new(); + if let Some(mut ext_addr) = transport.nat_traversal(original_listened_addr, &observed_addr) { + let mut listened_addrs = shared.listened_addrs.write(); + if !listened_addrs.iter().any(|a| a == &ext_addr) { + trace!(target: "sub-libp2p", + "NAT traversal: remote observes us as {}; registering {} as one of our own addresses", + observed_addr, + ext_addr + ); + listened_addrs.push(ext_addr.clone()); + ext_addr.append(AddrComponent::P2P(shared.kad_system + .local_peer_id().clone().into())); + info!(target: "sub-libp2p", "New external node address: {}", ext_addr); + } + } + } + + for addr in info.listen_addrs.iter() { + if let Some(node_id) = shared.network_state.node_id_from_index(node_index) { + shared.network_state.add_kad_discovered_addr(&node_id, addr.clone(), true); + } + } +} + +/// Returns a future that regularly pings every peer we're connected to. +/// If a peer doesn't respond after a while, we disconnect it. +fn start_periodic_updates( + shared: Arc, + transport: T, + swarm_controller: SwarmController + Send>> +) -> Box + Send> + where T: MuxedTransport> + Clone + Send + 'static, + T::MultiaddrFuture: Send + 'static, + T::Dial: Send, + T::Listener: Send, + T::ListenerUpgrade: Send, + T::Incoming: Send, + T::IncomingUpgrade: Send, + To: AsyncRead + AsyncWrite + Send + 'static, + St: MuxedTransport> + Clone + Send + 'static, + St::Dial: Send, + St::MultiaddrFuture: Send, + St::Listener: Send, + St::ListenerUpgrade: Send, + St::Incoming: Send, + St::IncomingUpgrade: Send, + C: Send + 'static { + let ping_transport = transport.clone() + .and_then(move |out, endpoint, client_addr| { + let node_index = out.node_index; + upgrade::apply(out.socket, ping::Ping, endpoint, client_addr) + .map(move |(stream, addr)| (FinalUpgrade::from((node_index, stream)), addr)) + }); + + let identify_transport = transport + .and_then(move |out, endpoint, client_addr| { + let node_index = out.node_index; + upgrade::apply(out.socket, IdentifyProtocolConfig, endpoint, client_addr) + .map(move |(id, addr)| { + let fin = match id { + IdentifyOutput::RemoteInfo { info, observed_addr } => + FinalUpgrade::IdentifyDialer(node_index, info, observed_addr), + IdentifyOutput::Sender { .. } => unreachable!("can't reach that on the dialer side"), + }; + (fin, addr) + }) + }); + + let fut = Interval::new(Instant::now() + Duration::from_secs(5), Duration::from_secs(30)) + .map_err(|err| IoError::new(IoErrorKind::Other, err)) + .for_each(move |_| periodic_updates( + shared.clone(), + ping_transport.clone(), + identify_transport.clone(), + &swarm_controller + )) + .then(|val| { + warn!(target: "sub-libp2p", "Periodic updates stream has stopped: {:?}", val); + val + }); + + // Note that we use a Box in order to speed compilation time. + Box::new(fut) as Box + Send> +} + +/// Pings all the nodes we're connected to and disconnects any node that +/// doesn't respond. Identifies nodes that need to be identified. Returns +/// a `Future` when all the pings have either suceeded or timed out. +fn periodic_updates( + shared: Arc, + ping_transport: Tp, + identify_transport: Tid, + swarm_controller: &SwarmController + Send>> +) -> Box + Send> + where Tp: MuxedTransport> + Clone + Send + 'static, + Tp::MultiaddrFuture: Send + 'static, + Tp::Dial: Send, + Tp::MultiaddrFuture: Send, + Tp::Listener: Send, + Tp::ListenerUpgrade: Send, + Tp::Incoming: Send, + Tp::IncomingUpgrade: Send, + Tid: MuxedTransport> + Clone + Send + 'static, + Tid::MultiaddrFuture: Send + 'static, + Tid::Dial: Send, + Tid::MultiaddrFuture: Send, + Tid::Listener: Send, + Tid::ListenerUpgrade: Send, + Tid::Incoming: Send, + Tid::IncomingUpgrade: Send, + St: MuxedTransport> + Clone + Send + 'static, + St::Dial: Send, + St::MultiaddrFuture: Send, + St::Listener: Send, + St::ListenerUpgrade: Send, + St::Incoming: Send, + St::IncomingUpgrade: Send, + C: Send + 'static { + trace!(target: "sub-libp2p", "Periodic update cycle"); + + let mut ping_futures = Vec::new(); + + for PeriodicUpdate { node_index, peer_id, address, pinger, identify } in + shared.network_state.cleanup_and_prepare_updates() { + let shared = shared.clone(); + + let fut = pinger + .dial(&swarm_controller, &address, ping_transport.clone()) + .and_then(move |mut p| { + trace!(target: "sub-libp2p", "Pinging peer #{} aka. {:?}", node_index, peer_id); + p.ping() + .map(move |()| peer_id) + .map_err(|err| IoError::new(IoErrorKind::Other, err)) + }); + let ping_start_time = Instant::now(); + let fut = Timeout::new_at(fut, ping_start_time + Duration::from_secs(30)) + .then(move |val| + match val { + Err(err) => { + trace!(target: "sub-libp2p", "Error while pinging #{:?} => {:?}", node_index, err); + shared.network_state.report_ping_failed(node_index); + // Return Ok, otherwise we would close the ping service + Ok(()) + }, + Ok(who) => { + let elapsed = ping_start_time.elapsed(); + trace!(target: "sub-libp2p", "Pong from #{:?} in {:?}", who, elapsed); + shared.network_state.report_ping_duration(node_index, elapsed); + shared.kad_system.update_kbuckets(who); + Ok(()) + } + } + ); + ping_futures.push(fut); + + if identify { + // Ignore dialing errors, as identifying is only about diagnostics. + trace!(target: "sub-libp2p", "Attempting to identify #{}", node_index); + let _ = swarm_controller.dial(address, identify_transport.clone()); + } + } + + let future = future::loop_fn(ping_futures, |ping_futures| { + if ping_futures.is_empty() { + let fut = future::ok(future::Loop::Break(())); + return future::Either::A(fut) + } + + let fut = future::select_all(ping_futures) + .map(|((), _, rest)| future::Loop::Continue(rest)) + .map_err(|(err, _, _)| err); + future::Either::B(fut) + }); + + // Note that we use a Box in order to speed up compilation. + Box::new(future) as Box + Send> +} + +/// Since new protocols are added after the networking starts, we have to load the protocols list +/// in a lazy way. This is what this wrapper does. +#[derive(Clone)] +struct DelayedProtosList(Arc); +// `Maf` is short for `MultiaddressFuture` +impl ConnectionUpgrade for DelayedProtosList +where C: AsyncRead + AsyncWrite + Send + 'static, // TODO: 'static :-/ + Maf: Future + Send + 'static, // TODO: 'static :( +{ + type NamesIter = > as ConnectionUpgrade>::NamesIter; + type UpgradeIdentifier = > as ConnectionUpgrade>::UpgradeIdentifier; + + fn protocol_names(&self) -> Self::NamesIter { + ConnectionUpgrade::::protocol_names(&self.0.protocols) + } + + type Output = > as ConnectionUpgrade>::Output; + type MultiaddrFuture = > as ConnectionUpgrade>::MultiaddrFuture; + type Future = > as ConnectionUpgrade>::Future; + + #[inline] + fn upgrade(self, socket: C, id: Self::UpgradeIdentifier, endpoint: Endpoint, + remote_addr: Maf) -> Self::Future + { + self.0.protocols + .clone() + .upgrade(socket, id, endpoint, remote_addr) + } +} + +#[cfg(test)] +mod tests { + use super::NetworkService; + + #[test] + fn builds_and_finishes_in_finite_time() { + // Checks that merely starting the network doesn't end up in an infinite loop. + let _service = NetworkService::new(Default::default(), vec![]).unwrap(); + } +} diff --git a/core/network-libp2p/src/timeouts.rs b/core/network-libp2p/src/timeouts.rs new file mode 100644 index 0000000000000..9b5615b0d54ae --- /dev/null +++ b/core/network-libp2p/src/timeouts.rs @@ -0,0 +1,115 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +use futures::{Async, future, Future, Poll, stream, Stream, sync::mpsc}; +use std::io::{Error as IoError, ErrorKind as IoErrorKind}; +use std::marker::PhantomData; +use std::time::{Duration, Instant}; +use tokio_timer::{self, Delay}; + +/// Builds the timeouts system. +/// +/// The `timeouts_rx` should be a stream receiving newly-created timeout +/// requests. Returns a stream that produces items as their timeout elapses. +/// `T` can be anything you want, as it is transparently passed from the input +/// to the output. Timeouts continue to fire forever, as there is no way to +/// unregister them. +pub fn build_timeouts_stream<'a, T>( + timeouts_rx: mpsc::UnboundedReceiver<(Duration, T)> +) -> Box + 'a> + where T: Clone + 'a { + let next_timeout = next_in_timeouts_stream(timeouts_rx); + + // The `unfold` function is essentially a loop turned into a stream. The + // first parameter is the initial state, and the closure returns the new + // state and an item. + let stream = stream::unfold(vec![future::Either::A(next_timeout)], move |timeouts| { + // `timeouts` is a `Vec` of futures that produce an `Out`. + + // `select_ok` panics if `timeouts` is empty anyway. + if timeouts.is_empty() { + return None + } + + Some(future::select_ok(timeouts.into_iter()) + .and_then(move |(item, mut timeouts)| + match item { + Out::NewTimeout((Some((duration, item)), next_timeouts)) => { + // Received a new timeout request on the channel. + let next_timeout = next_in_timeouts_stream(next_timeouts); + let timeout = Delay::new(Instant::now() + duration); + let timeout = TimeoutWrapper(timeout, duration, Some(item), PhantomData); + timeouts.push(future::Either::B(timeout)); + timeouts.push(future::Either::A(next_timeout)); + Ok((None, timeouts)) + }, + Out::NewTimeout((None, _)) => + // The channel has been closed. + Ok((None, timeouts)), + Out::Timeout(duration, item) => { + // A timeout has happened. + let returned = item.clone(); + let timeout = Delay::new(Instant::now() + duration); + let timeout = TimeoutWrapper(timeout, duration, Some(item), PhantomData); + timeouts.push(future::Either::B(timeout)); + Ok((Some(returned), timeouts)) + }, + } + ) + ) + }).filter_map(|item| item); + + // Note that we use a `Box` in order to speed up compilation time. + Box::new(stream) as Box> +} + +/// Local enum representing the output of the selection. +enum Out { + NewTimeout(A), + Timeout(Duration, B), +} + +/// Convenience function that calls `.into_future()` on the timeouts stream, +/// and applies some modifiers. +/// This function is necessary. Otherwise if we copy-paste its content we run +/// into errors because the type of the copy-pasted closures differs. +fn next_in_timeouts_stream( + stream: mpsc::UnboundedReceiver +) -> impl Future, mpsc::UnboundedReceiver), B>, Error = IoError> { + stream + .into_future() + .map(Out::NewTimeout) + .map_err(|_| unreachable!("an UnboundedReceiver can never error")) +} + +/// Does the equivalent to `future.map(move |()| (duration, item)).map_err(|err| to_io_err(err))`. +struct TimeoutWrapper(F, Duration, Option, PhantomData); +impl Future for TimeoutWrapper + where F: Future { + type Item = Out; + type Error = IoError; + + fn poll(&mut self) -> Poll { + match self.0.poll() { + Ok(Async::Ready(())) => (), + Ok(Async::NotReady) => return Ok(Async::NotReady), + Err(err) => return Err(IoError::new(IoErrorKind::Other, err.to_string())), + } + + let out = Out::Timeout(self.1, self.2.take().expect("poll() called again after success")); + Ok(Async::Ready(out)) + } +} diff --git a/core/network-libp2p/src/topology.rs b/core/network-libp2p/src/topology.rs new file mode 100644 index 0000000000000..d36eb07624bca --- /dev/null +++ b/core/network-libp2p/src/topology.rs @@ -0,0 +1,655 @@ +// Copyright 2018 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 fnv::FnvHashMap; +use parking_lot::Mutex; +use libp2p::{Multiaddr, PeerId}; +use serde_json; +use std::{cmp, fs}; +use std::io::{Read, Cursor, Error as IoError, ErrorKind as IoErrorKind, Write, BufReader, BufWriter}; +use std::path::{Path, PathBuf}; +use std::time::{Duration, Instant, SystemTime}; + +/// For each address we're connected to, a period of this duration increases the score by 1. +const CONNEC_DURATION_PER_SCORE: Duration = Duration::from_secs(10); +/// Maximum value for the score. +const MAX_SCORE: u32 = 100; +/// When we successfully connect to a node, raises its score to the given minimum value. +const CONNECTED_MINIMUM_SCORE: u32 = 20; +/// Initial score that a node discovered through Kademlia receives, where we have a hint that the +/// node is reachable. +const KADEMLIA_DISCOVERY_INITIAL_SCORE_CONNECTABLE: u32 = 15; +/// Initial score that a node discovered through Kademlia receives, without any hint. +const KADEMLIA_DISCOVERY_INITIAL_SCORE: u32 = 10; +/// Score adjustement when we fail to connect to an address. +const SCORE_DIFF_ON_FAILED_TO_CONNECT: i32 = -1; +/// Default time-to-live for addresses discovered through Kademlia. +/// After this time has elapsed and no connection has succeeded, the address will be removed. +const KADEMLIA_DISCOVERY_EXPIRATION: Duration = Duration::from_secs(2 * 3600); +/// After a successful connection, the TTL is set to a minimum at this amount. +const EXPIRATION_PUSH_BACK_CONNEC: Duration = Duration::from_secs(2 * 3600); +/// Initial score that a bootstrap node receives when registered. +const BOOTSTRAP_NODE_SCORE: u32 = 100; +/// Time to live of a boostrap node. This only applies if you start the node later *without* +/// that bootstrap node configured anymore. +const BOOTSTRAP_NODE_EXPIRATION: Duration = Duration::from_secs(24 * 3600); +/// The first time we fail to connect to an address, wait this duration before trying again. +const FIRST_CONNECT_FAIL_BACKOFF: Duration = Duration::from_secs(2); +/// Every time we fail to connect to an address, multiply the backoff by this constant. +const FAIL_BACKOFF_MULTIPLIER: u32 = 2; +/// We need a maximum value for the backoff, overwise we risk an overflow. +const MAX_BACKOFF: Duration = Duration::from_secs(30 * 60); + +// TODO: should be merged with the Kademlia k-buckets + +/// Stores information about the topology of the network. +#[derive(Debug)] +pub struct NetTopology { + store: FnvHashMap, + cache_path: Option, +} + +impl Default for NetTopology { + #[inline] + fn default() -> NetTopology { + NetTopology::memory() + } +} + +impl NetTopology { + /// Initializes a new `NetTopology` that isn't tied to any file. + /// + /// `flush_to_disk()` will be a no-op. + #[inline] + pub fn memory() -> NetTopology { + NetTopology { + store: Default::default(), + cache_path: None, + } + } + + /// Builds a `NetTopology` that will use `path` as a cache. + /// + /// This function tries to load a known topology from the file. If the file doesn't exist + /// or contains garbage data, the execution still continues. + /// + /// Calling `flush_to_disk()` in the future writes to the given path. + pub fn from_file>(path: P) -> NetTopology { + let path = path.as_ref(); + debug!(target: "sub-libp2p", "Initializing peer store for JSON file {:?}", path); + NetTopology { + store: try_load(path), + cache_path: Some(path.to_owned()), + } + } + + /// Writes the topology into the path passed to `from_file`. + /// + /// No-op if the object was created with `memory()`. + pub fn flush_to_disk(&self) -> Result<(), IoError> { + let path = match self.cache_path { + Some(ref p) => p, + None => return Ok(()) + }; + + let file = fs::File::create(path)?; + // TODO: the capacity of the BufWriter is kind of arbitrary ; decide better + serialize(BufWriter::with_capacity(1024 * 1024, file), &self.store) + } + + /// Perform a cleanup pass, removing all obsolete addresses and peers. + /// + /// This should be done from time to time. + pub fn cleanup(&mut self) { + let now_systime = SystemTime::now(); + self.store.retain(|_, peer| { + peer.addrs.retain(|a| { + a.expires > now_systime || a.is_connected() + }); + !peer.addrs.is_empty() + }); + } + + /// Returns the known potential addresses of a peer, ordered by score. + /// + /// The boolean associated to each address indicates whether we're connected to it. + // TODO: filter out backed off ones? + pub fn addrs_of_peer(&self, peer: &PeerId) -> impl Iterator { + let peer = if let Some(peer) = self.store.get(peer) { + peer + } else { + // TODO: use an EitherIterator or something + return Vec::new().into_iter(); + }; + + let now = SystemTime::now(); + let mut list = peer.addrs.iter().filter_map(move |addr| { + let (score, connected) = addr.score_and_is_connected(); + if (addr.expires >= now && score > 0) || connected { + Some((score, connected, &addr.addr)) + } else { + None + } + }).collect::>(); + list.sort_by(|a, b| a.0.cmp(&b.0)); + // TODO: meh, optimize + let l = list.into_iter().map(|(_, connec, addr)| (addr, connec)).collect::>(); + l.into_iter() + } + + /// Returns a list of all the known addresses of peers, ordered by the + /// order in which we should attempt to connect to them. + /// + /// Because of expiration and back-off mechanisms, this list can grow + /// by itself over time. The `Instant` that is returned corresponds to + /// the earlier known time when a new entry will be added automatically to + /// the list. + pub fn addrs_to_attempt(&self) -> (impl Iterator, Instant) { + // TODO: optimize + let now = Instant::now(); + let now_systime = SystemTime::now(); + let mut instant = now + Duration::from_secs(3600); + let mut addrs_out = Vec::new(); + + for (peer, info) in &self.store { + for addr in &info.addrs { + let (score, is_connected) = addr.score_and_is_connected(); + if score == 0 || addr.expires < now_systime { + continue; + } + if !is_connected && addr.back_off_until > now { + instant = cmp::min(instant, addr.back_off_until); + continue; + } + + addrs_out.push(((peer, &addr.addr), score)); + } + } + + addrs_out.sort_by(|a, b| b.1.cmp(&a.1)); + (addrs_out.into_iter().map(|a| a.0), instant) + } + + /// Adds an address corresponding to a boostrap node. + /// + /// We assume that the address is valid, so its score starts very high. + pub fn add_bootstrap_addr(&mut self, peer: &PeerId, addr: Multiaddr) { + let now_systime = SystemTime::now(); + let now = Instant::now(); + + let peer = peer_access(&mut self.store, peer); + + let mut found = false; + peer.addrs.retain(|a| { + if a.expires < now_systime && !a.is_connected() { + return false; + } + if a.addr == addr { + found = true; + } + true + }); + + if !found { + peer.addrs.push(Addr { + addr, + expires: now_systime + BOOTSTRAP_NODE_EXPIRATION, + back_off_until: now, + next_back_off: FIRST_CONNECT_FAIL_BACKOFF, + score: Mutex::new(AddrScore { + connected_since: None, + score: BOOTSTRAP_NODE_SCORE, + latest_score_update: now, + }), + }); + } + } + + /// Adds an address discovered through the Kademlia DHT. + /// + /// This address is not necessarily valid and should expire after a TTL. + /// + /// If `connectable` is true, that means we have some sort of hint that this node can + /// be reached. + pub fn add_kademlia_discovered_addr( + &mut self, + peer_id: &PeerId, + addr: Multiaddr, + connectable: bool + ) { + let now_systime = SystemTime::now(); + let now = Instant::now(); + + let peer = peer_access(&mut self.store, peer_id); + + let mut found = false; + peer.addrs.retain(|a| { + if a.expires < now_systime && !a.is_connected() { + return false; + } + if a.addr == addr { + found = true; + } + true + }); + + if !found { + trace!( + target: "sub-libp2p", + "Peer store: adding address {} for {:?} (connectable hint: {:?})", + addr, + peer_id, + connectable + ); + + let initial_score = if connectable { + KADEMLIA_DISCOVERY_INITIAL_SCORE_CONNECTABLE + } else { + KADEMLIA_DISCOVERY_INITIAL_SCORE + }; + + peer.addrs.push(Addr { + addr, + expires: now_systime + KADEMLIA_DISCOVERY_EXPIRATION, + back_off_until: now, + next_back_off: FIRST_CONNECT_FAIL_BACKOFF, + score: Mutex::new(AddrScore { + connected_since: None, + score: initial_score, + latest_score_update: now, + }), + }); + } + } + + /// Indicates the peer store that we're connected to this given address. + /// + /// This increases the score of the address that we connected to. Since we assume that only + /// one peer can be reached with any specific address, we also remove all addresses from other + /// peers that match the one we connected to. + pub fn report_connected(&mut self, addr: &Multiaddr, peer: &PeerId) { + let now = Instant::now(); + + // Just making sure that we have an entry for this peer in `store`, but don't use it. + let _ = peer_access(&mut self.store, peer); + + for (peer_in_store, info_in_store) in self.store.iter_mut() { + if peer == peer_in_store { + if let Some(addr) = info_in_store.addrs.iter_mut().find(|a| &a.addr == addr) { + addr.connected_now(CONNECTED_MINIMUM_SCORE); + addr.back_off_until = now; + addr.next_back_off = FIRST_CONNECT_FAIL_BACKOFF; + continue; + } + + // TODO: a else block would be better, but we get borrowck errors + info_in_store.addrs.push(Addr { + addr: addr.clone(), + expires: SystemTime::now() + EXPIRATION_PUSH_BACK_CONNEC, + back_off_until: now, + next_back_off: FIRST_CONNECT_FAIL_BACKOFF, + score: Mutex::new(AddrScore { + connected_since: Some(now), + latest_score_update: now, + score: CONNECTED_MINIMUM_SCORE, + }), + }); + + } else { + // Set the score to 0 for any address that matches the one we connected to. + for addr_in_store in &mut info_in_store.addrs { + if &addr_in_store.addr == addr { + addr_in_store.adjust_score(-(MAX_SCORE as i32)); + } + } + } + } + } + + /// Indicates the peer store that we're disconnected from an address. + /// + /// There's no need to indicate a peer ID, as each address can only have one peer ID. + /// If we were indeed connected to this addr, then we can find out which peer ID it is. + pub fn report_disconnected(&mut self, addr: &Multiaddr, reason: DisconnectReason) { + let score_diff = match reason { + DisconnectReason::ClosedGracefully => -1, + }; + + for info in self.store.values_mut() { + for a in info.addrs.iter_mut() { + if &a.addr == addr { + a.disconnected_now(score_diff); + a.back_off_until = Instant::now() + a.next_back_off; + a.next_back_off = cmp::min(a.next_back_off * FAIL_BACKOFF_MULTIPLIER, MAX_BACKOFF); + let expires_push_back = SystemTime::now() + EXPIRATION_PUSH_BACK_CONNEC; + if a.expires < expires_push_back { + a.expires = expires_push_back; + } + return; + } + } + } + } + + /// Indicates the peer store that we failed to connect to an address. + /// + /// We don't care about which peer is supposed to be behind that address. If we failed to dial + /// it for a specific peer, we would also fail to dial it for all peers that have this + /// address. + pub fn report_failed_to_connect(&mut self, addr: &Multiaddr) { + for info in self.store.values_mut() { + for a in info.addrs.iter_mut() { + if &a.addr == addr { + a.adjust_score(SCORE_DIFF_ON_FAILED_TO_CONNECT); + a.back_off_until = Instant::now() + a.next_back_off; + a.next_back_off = cmp::min(a.next_back_off * FAIL_BACKOFF_MULTIPLIER, MAX_BACKOFF); + } + } + } + } +} + +/// Reason why we disconnected from a peer. +pub enum DisconnectReason { + /// The disconnection was graceful. + ClosedGracefully, +} + +fn peer_access<'a>(store: &'a mut FnvHashMap, peer: &PeerId) -> &'a mut PeerInfo { + // TODO: should be optimizable if HashMap gets a better API + store.entry(peer.clone()).or_insert_with(Default::default) +} + +#[derive(Debug, Clone, Default)] +struct PeerInfo { + /// Addresses of that peer. + addrs: Vec, +} + +#[derive(Debug)] +struct Addr { + /// The multiaddress. + addr: Multiaddr, + /// When the address expires. + expires: SystemTime, + next_back_off: Duration, + /// Don't try to connect to this node until `Instant`. + back_off_until: Instant, + score: Mutex, +} + +impl Clone for Addr { + fn clone(&self) -> Addr { + Addr { + addr: self.addr.clone(), + expires: self.expires.clone(), + next_back_off: self.next_back_off.clone(), + back_off_until: self.back_off_until.clone(), + score: Mutex::new(self.score.lock().clone()), + } + } +} + +#[derive(Debug, Clone)] +struct AddrScore { + /// If connected, contains the moment when we connected. `None` if we're not connected. + connected_since: Option, + /// Score of this address. Potentially needs to be updated based on `latest_score_update`. + score: u32, + /// When we last updated the score. + latest_score_update: Instant, +} + +impl Addr { + /// Sets the addr to connected. If the score is lower than the given value, raises it to this + /// value. + fn connected_now(&self, raise_to_min: u32) { + let mut score = self.score.lock(); + let now = Instant::now(); + Addr::flush(&mut score, now); + score.connected_since = Some(now); + if score.score < raise_to_min { + score.score = raise_to_min; + } + } + + /// Applies a modification to the score. + fn adjust_score(&self, score_diff: i32) { + let mut score = self.score.lock(); + Addr::flush(&mut score, Instant::now()); + if score_diff >= 0 { + score.score = cmp::min(MAX_SCORE, score.score + score_diff as u32); + } else { + score.score = score.score.saturating_sub(-score_diff as u32); + } + } + + /// Sets the addr to disconnected and applies a modification to the score. + fn disconnected_now(&self, score_diff: i32) { + let mut score = self.score.lock(); + Addr::flush(&mut score, Instant::now()); + score.connected_since = None; + if score_diff >= 0 { + score.score = cmp::min(MAX_SCORE, score.score + score_diff as u32); + } else { + score.score = score.score.saturating_sub(-score_diff as u32); + } + } + + /// Returns true if we are connected to this addr. + fn is_connected(&self) -> bool { + let score = self.score.lock(); + score.connected_since.is_some() + } + + /// Returns the score, and true if we are connected to this addr. + fn score_and_is_connected(&self) -> (u32, bool) { + let mut score = self.score.lock(); + Addr::flush(&mut score, Instant::now()); + let is_connected = score.connected_since.is_some(); + (score.score, is_connected) + } + + /// Updates `score` and `latest_score_update`, and returns the score. + fn score(&self) -> u32 { + let mut score = self.score.lock(); + Addr::flush(&mut score, Instant::now()); + score.score + } + + fn flush(score: &mut AddrScore, now: Instant) { + if let Some(connected_since) = score.connected_since { + let potential_score: u32 = div_dur_with_dur(now - connected_since, CONNEC_DURATION_PER_SCORE); + // We flush when we connect to an address. + debug_assert!(score.latest_score_update >= connected_since); + let effective_score: u32 = + div_dur_with_dur(score.latest_score_update - connected_since, CONNEC_DURATION_PER_SCORE); + let to_add = potential_score.saturating_sub(effective_score); + score.score = cmp::min(MAX_SCORE, score.score + to_add); + } + + score.latest_score_update = now; + } +} + +/// Divides a `Duration` with a `Duration`. This exists in the stdlib but isn't stable yet. +// TODO: remove this function once stable +fn div_dur_with_dur(a: Duration, b: Duration) -> u32 { + let a_ms = a.as_secs() * 1_000_000 + (a.subsec_nanos() / 1_000) as u64; + let b_ms = b.as_secs() * 1_000_000 + (b.subsec_nanos() / 1_000) as u64; + (a_ms / b_ms) as u32 +} + +/// Serialized version of a `PeerInfo`. Suitable for storage in the cache file. +#[derive(Debug, Clone, Serialize, Deserialize)] +struct SerializedPeerInfo { + addrs: Vec, +} + +/// Serialized version of an `Addr`. Suitable for storage in the cache file. +#[derive(Debug, Clone, Serialize, Deserialize)] +struct SerializedAddr { + addr: String, + expires: SystemTime, + score: u32, +} + +impl<'a> From<&'a Addr> for SerializedAddr { + fn from(addr: &'a Addr) -> SerializedAddr { + SerializedAddr { + addr: addr.addr.to_string(), + expires: addr.expires, + score: addr.score(), + } + } +} + +/// Attempts to load storage from a file. +/// Deletes the file and returns an empty map if the file doesn't exist, cannot be opened +/// or is corrupted. +fn try_load(path: impl AsRef) -> FnvHashMap { + let path = path.as_ref(); + if !path.exists() { + debug!(target: "sub-libp2p", "Peer storage file {:?} doesn't exist", path); + return Default::default() + } + + let mut file = match fs::File::open(path) { + // TODO: the capacity of the BufReader is kind of arbitrary ; decide better + Ok(f) => BufReader::with_capacity(1024 * 1024, f), + Err(err) => { + warn!(target: "sub-libp2p", "Failed to open peer storage file: {:?}", err); + info!(target: "sub-libp2p", "Deleting peer storage file {:?}", path); + let _ = fs::remove_file(path); + return Default::default() + } + }; + + // We want to support empty files (and treat them as an empty recordset). Unfortunately + // `serde_json` will always produce an error if we do this ("unexpected EOF at line 0 + // column 0"). Therefore we start by reading one byte from the file in order to check + // for EOF. + + let mut first_byte = [0]; + let num_read = match file.read(&mut first_byte) { + Ok(f) => f, + Err(err) => { + // TODO: DRY + warn!(target: "sub-libp2p", "Failed to read peer storage file: {:?}", err); + info!(target: "sub-libp2p", "Deleting peer storage file {:?}", path); + let _ = fs::remove_file(path); + return Default::default() + } + }; + + if num_read == 0 { + // File is empty. + debug!(target: "sub-libp2p", "Peer storage file {:?} is empty", path); + Default::default() + + } else { + let data = Cursor::new(first_byte).chain(file); + match serde_json::from_reader::<_, serde_json::Value>(data) { + Ok(serde_json::Value::Null) => Default::default(), + Ok(serde_json::Value::Object(map)) => deserialize_tolerant(map.into_iter()), + Ok(_) | Err(_) => { + // The `Ok(_)` case means that the file doesn't contain a map. + let _ = fs::remove_file(path); + Default::default() + }, + } + } +} + +/// Attempts to turn a deserialized version of the storage into the final version. +/// +/// Skips entries that are invalid. +fn deserialize_tolerant( + iter: impl Iterator +) -> FnvHashMap { + let now = Instant::now(); + let now_systime = SystemTime::now(); + + let mut out = FnvHashMap::default(); + for (peer, info) in iter { + let peer: PeerId = match peer.parse() { + Ok(p) => p, + Err(_) => continue, + }; + + let info: SerializedPeerInfo = match serde_json::from_value(info) { + Ok(i) => i, + Err(_) => continue, + }; + + let mut addrs = Vec::with_capacity(info.addrs.len()); + for addr in info.addrs { + let multiaddr = match addr.addr.parse() { + Ok(a) => a, + Err(_) => continue, + }; + + if addr.expires < now_systime { + continue + } + + addrs.push(Addr { + addr: multiaddr, + expires: addr.expires, + next_back_off: FIRST_CONNECT_FAIL_BACKOFF, + back_off_until: now, + score: Mutex::new(AddrScore { + connected_since: None, + score: addr.score, + latest_score_update: now, + }), + }); + } + + if addrs.is_empty() { + continue; + } + + out.insert(peer, PeerInfo { addrs }); + } + + out +} + +/// Attempts to turn a deserialized version of the storage into the final version. +/// +/// Skips entries that are invalid or expired. +fn serialize(out: W, map: &FnvHashMap) -> Result<(), IoError> { + let now = SystemTime::now(); + let array: FnvHashMap<_, _> = map.iter().filter_map(|(peer, info)| { + if info.addrs.is_empty() { + return None + } + + let peer = peer.to_base58(); + let info = SerializedPeerInfo { + addrs: info.addrs.iter() + .filter(|a| a.expires > now || a.is_connected()) + .map(Into::into) + .collect(), + }; + + Some((peer, info)) + }).collect(); + + serde_json::to_writer_pretty(out, &array) + .map_err(|err| IoError::new(IoErrorKind::Other, err)) +} diff --git a/core/network-libp2p/src/traits.rs b/core/network-libp2p/src/traits.rs new file mode 100644 index 0000000000000..de18f083c559d --- /dev/null +++ b/core/network-libp2p/src/traits.rs @@ -0,0 +1,299 @@ +// Copyright 2015-2018 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::fmt; +use std::cmp::Ordering; +use std::iter; +use std::net::Ipv4Addr; +use std::str; +use std::time::Duration; +use io::TimerToken; +use libp2p::{multiaddr::AddrComponent, Multiaddr}; +use error::Error; +use ethkey::Secret; +use ethereum_types::H512; + +/// Protocol handler level packet id +pub type PacketId = u8; +/// Protocol / handler id +pub type ProtocolId = [u8; 3]; + +/// Node public key +pub type NodeId = H512; + +/// Local (temporary) peer session ID. +pub type NodeIndex = usize; + +/// Shared session information +#[derive(Debug, Clone)] +pub struct SessionInfo { + /// Peer public key + pub id: Option, + /// Peer client ID + pub client_version: String, + /// Peer RLPx protocol version + pub protocol_version: u32, + /// Session protocol capabilities + pub capabilities: Vec, + /// Peer protocol capabilities + pub peer_capabilities: Vec, + /// Peer ping delay + pub ping: Option, + /// True if this session was originated by us. + pub originated: bool, + /// Remote endpoint address of the session + pub remote_address: String, + /// Local endpoint address of the session + pub local_address: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PeerCapabilityInfo { + pub protocol: ProtocolId, + pub version: u8, +} + +impl ToString for PeerCapabilityInfo { + fn to_string(&self) -> String { + format!("{}/{}", str::from_utf8(&self.protocol[..]).unwrap_or("???"), self.version) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SessionCapabilityInfo { + pub protocol: [u8; 3], + pub version: u8, + pub packet_count: u8, + pub id_offset: u8, +} + +impl PartialOrd for SessionCapabilityInfo { + fn partial_cmp(&self, other: &SessionCapabilityInfo) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for SessionCapabilityInfo { + fn cmp(&self, b: &SessionCapabilityInfo) -> Ordering { + // By protocol id first + if self.protocol != b.protocol { + return self.protocol.cmp(&b.protocol); + } + // By version + self.version.cmp(&b.version) + } +} + +/// Network service configuration +#[derive(Debug, PartialEq, Clone)] +pub struct NetworkConfiguration { + /// Directory path to store general network configuration. None means nothing will be saved + pub config_path: Option, + /// Directory path to store network-specific configuration. None means nothing will be saved + pub net_config_path: Option, + /// Multiaddresses to listen for incoming connections. + pub listen_addresses: Vec, + /// Multiaddresses to advertise. Detected automatically if empty. + pub public_addresses: Vec, + /// List of initial node addresses + pub boot_nodes: Vec, + /// Use provided node key instead of default + pub use_secret: Option, + /// Minimum number of connected peers to maintain + pub min_peers: u32, + /// Maximum allowed number of peers + pub max_peers: u32, + /// List of reserved node addresses. + pub reserved_nodes: Vec, + /// The non-reserved peer mode. + pub non_reserved_mode: NonReservedPeerMode, + /// Client identifier + pub client_version: String, +} + +impl Default for NetworkConfiguration { + fn default() -> Self { + NetworkConfiguration::new() + } +} + +impl NetworkConfiguration { + /// Create a new instance of default settings. + pub fn new() -> Self { + NetworkConfiguration { + config_path: None, + net_config_path: None, + listen_addresses: vec![ + iter::once(AddrComponent::IP4(Ipv4Addr::new(0, 0, 0, 0))) + .chain(iter::once(AddrComponent::TCP(30333))) + .collect() + ], + public_addresses: Vec::new(), + boot_nodes: Vec::new(), + use_secret: None, + min_peers: 25, + max_peers: 50, + reserved_nodes: Vec::new(), + non_reserved_mode: NonReservedPeerMode::Accept, + client_version: "Parity-network".into(), + } + } + + /// Create new default configuration for localhost-only connection with random port (useful for testing) + pub fn new_local() -> NetworkConfiguration { + let mut config = NetworkConfiguration::new(); + config.listen_addresses = vec![ + iter::once(AddrComponent::IP4(Ipv4Addr::new(127, 0, 0, 1))) + .chain(iter::once(AddrComponent::TCP(0))) + .collect() + ]; + config + } +} + +/// The severity of misbehaviour of a peer that is reported. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum Severity<'a> { + /// Peer is timing out. Could be bad connectivity of overload of work on either of our sides. + Timeout, + /// Peer has been notably useless. E.g. unable to answer a request that we might reasonably consider + /// it could answer. + Useless(&'a str), + /// Peer has behaved in an invalid manner. This doesn't necessarily need to be Byzantine, but peer + /// must have taken concrete action in order to behave in such a way which is wantanly invalid. + Bad(&'a str), +} + +impl<'a> fmt::Display for Severity<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match *self { + Severity::Timeout => write!(fmt, "Timeout"), + Severity::Useless(r) => write!(fmt, "Useless ({})", r), + Severity::Bad(r) => write!(fmt, "Bad ({})", r), + } + } +} + +/// IO access point. This is passed to all IO handlers and provides an interface to the IO subsystem. +pub trait NetworkContext { + /// Send a packet over the network to another peer. + fn send(&self, peer: NodeIndex, packet_id: PacketId, data: Vec); + + /// Send a packet over the network to another peer using specified protocol. + fn send_protocol(&self, protocol: ProtocolId, peer: NodeIndex, packet_id: PacketId, data: Vec); + + /// Respond to a current network message. Panics if no there is no packet in the context. If the session is expired returns nothing. + fn respond(&self, packet_id: PacketId, data: Vec); + + /// Report peer. Depending on the report, peer may be disconnected and possibly banned. + fn report_peer(&self, peer: NodeIndex, reason: Severity); + + /// Check if the session is still active. + fn is_expired(&self) -> bool; + + /// Register a new IO timer. 'IoHandler::timeout' will be called with the token. + fn register_timer(&self, token: TimerToken, delay: Duration) -> Result<(), Error>; + + /// Returns peer identification string + fn peer_client_version(&self, peer: NodeIndex) -> String; + + /// Returns information on p2p session + fn session_info(&self, peer: NodeIndex) -> Option; + + /// Returns max version for a given protocol. + fn protocol_version(&self, protocol: ProtocolId, peer: NodeIndex) -> Option; + + /// Returns this object's subprotocol name. + fn subprotocol_name(&self) -> ProtocolId; +} + +impl<'a, T> NetworkContext for &'a T where T: ?Sized + NetworkContext { + fn send(&self, peer: NodeIndex, packet_id: PacketId, data: Vec) { + (**self).send(peer, packet_id, data) + } + + fn send_protocol(&self, protocol: ProtocolId, peer: NodeIndex, packet_id: PacketId, data: Vec) { + (**self).send_protocol(protocol, peer, packet_id, data) + } + + fn respond(&self, packet_id: PacketId, data: Vec) { + (**self).respond(packet_id, data) + } + + fn report_peer(&self, peer: NodeIndex, reason: Severity) { + (**self).report_peer(peer, reason) + } + + fn is_expired(&self) -> bool { + (**self).is_expired() + } + + fn register_timer(&self, token: TimerToken, delay: Duration) -> Result<(), Error> { + (**self).register_timer(token, delay) + } + + fn peer_client_version(&self, peer: NodeIndex) -> String { + (**self).peer_client_version(peer) + } + + fn session_info(&self, peer: NodeIndex) -> Option { + (**self).session_info(peer) + } + + fn protocol_version(&self, protocol: ProtocolId, peer: NodeIndex) -> Option { + (**self).protocol_version(protocol, peer) + } + + fn subprotocol_name(&self) -> ProtocolId { + (**self).subprotocol_name() + } +} + +/// Network IO protocol handler. This needs to be implemented for each new subprotocol. +/// All the handler function are called from within IO event loop. +/// `Message` is the type for message data. +pub trait NetworkProtocolHandler: Sync + Send { + /// Initialize the handler + fn initialize(&self, _io: &NetworkContext) {} + /// Called when new network packet received. + fn read(&self, io: &NetworkContext, peer: &NodeIndex, packet_id: u8, data: &[u8]); + /// Called when new peer is connected. Only called when peer supports the same protocol. + fn connected(&self, io: &NetworkContext, peer: &NodeIndex); + /// Called when a previously connected peer disconnects. + fn disconnected(&self, io: &NetworkContext, peer: &NodeIndex); + /// Timer function called after a timeout created with `NetworkContext::timeout`. + fn timeout(&self, _io: &NetworkContext, _timer: TimerToken) {} +} + +/// Non-reserved peer modes. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum NonReservedPeerMode { + /// Accept them. This is the default. + Accept, + /// Deny them. + Deny, +} + +impl NonReservedPeerMode { + /// Attempt to parse the peer mode from a string. + pub fn parse(s: &str) -> Option { + match s { + "accept" => Some(NonReservedPeerMode::Accept), + "deny" => Some(NonReservedPeerMode::Deny), + _ => None, + } + } +} diff --git a/core/network-libp2p/src/transport.rs b/core/network-libp2p/src/transport.rs new file mode 100644 index 0000000000000..8acb9ecab0828 --- /dev/null +++ b/core/network-libp2p/src/transport.rs @@ -0,0 +1,50 @@ +// Copyright 2018 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 libp2p::{self, PeerId, Transport, mplex, secio, yamux}; +use libp2p::core::{either, upgrade, transport::BoxedMuxed}; +use libp2p::transport_timeout::TransportTimeout; +use std::time::Duration; +use std::usize; +use tokio_io::{AsyncRead, AsyncWrite}; + +/// Builds the transport that serves as a common ground for all connections. +pub fn build_transport( + local_private_key: secio::SecioKeyPair +) -> BoxedMuxed<(PeerId, impl AsyncRead + AsyncWrite)> { + let mut mplex_config = mplex::MplexConfig::new(); + mplex_config.max_buffer_len_behaviour(mplex::MaxBufferBehaviour::Block); + mplex_config.max_buffer_len(usize::MAX); + + let base = libp2p::CommonTransport::new() + .with_upgrade(secio::SecioConfig { + key: local_private_key, + }) + .and_then(move |out, endpoint, client_addr| { + let upgrade = upgrade::or( + upgrade::map(mplex_config, either::EitherOutput::First), + upgrade::map(yamux::Config::default(), either::EitherOutput::Second), + ); + let key = out.remote_key; + let upgrade = upgrade::map(upgrade, move |muxer| (key, muxer)); + upgrade::apply(out.stream, upgrade, endpoint, client_addr) + }) + .into_connection_reuse() + .map(|(key, substream), _| (key.into_peer_id(), substream)); + + TransportTimeout::new(base, Duration::from_secs(20)) + .boxed_muxed() +} diff --git a/core/network-libp2p/tests/tests.rs b/core/network-libp2p/tests/tests.rs new file mode 100644 index 0000000000000..a74772171950c --- /dev/null +++ b/core/network-libp2p/tests/tests.rs @@ -0,0 +1,130 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity 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. + +// Parity 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 Parity. If not, see . + +extern crate parking_lot; +extern crate parity_bytes; +extern crate ethcore_io as io; +extern crate ethcore_logger; +extern crate substrate_network_libp2p; +extern crate ethkey; + +use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering}; +use std::sync::Arc; +use std::thread; +use std::time::*; +use parking_lot::Mutex; +use parity_bytes::Bytes; +use substrate_network_libp2p::*; +use ethkey::{Random, Generator}; +use io::TimerToken; + +pub struct TestProtocol { + drop_session: bool, + pub packet: Mutex, + pub got_timeout: AtomicBool, + pub got_disconnect: AtomicBool, +} + +impl TestProtocol { + pub fn new(drop_session: bool) -> Self { + TestProtocol { + packet: Mutex::new(Vec::new()), + got_timeout: AtomicBool::new(false), + got_disconnect: AtomicBool::new(false), + drop_session: drop_session, + } + } + + pub fn got_packet(&self) -> bool { + self.packet.lock()[..] == b"hello"[..] + } + + pub fn got_timeout(&self) -> bool { + self.got_timeout.load(AtomicOrdering::Relaxed) + } + + pub fn got_disconnect(&self) -> bool { + self.got_disconnect.load(AtomicOrdering::Relaxed) + } +} + +impl NetworkProtocolHandler for TestProtocol { + fn initialize(&self, io: &NetworkContext) { + io.register_timer(0, Duration::from_millis(10)).unwrap(); + } + + fn read(&self, _io: &NetworkContext, _peer: &NodeIndex, packet_id: u8, data: &[u8]) { + assert_eq!(packet_id, 33); + self.packet.lock().extend(data); + } + + fn connected(&self, io: &NetworkContext, peer: &NodeIndex) { + if self.drop_session { + io.report_peer(*peer, Severity::Bad("We are evil and just want to drop")) + } else { + io.respond(33, "hello".to_owned().into_bytes()); + } + } + + fn disconnected(&self, _io: &NetworkContext, _peer: &NodeIndex) { + self.got_disconnect.store(true, AtomicOrdering::Relaxed); + } + + /// Timer function called after a timeout created with `NetworkContext::timeout`. + fn timeout(&self, _io: &NetworkContext, timer: TimerToken) { + assert_eq!(timer, 0); + self.got_timeout.store(true, AtomicOrdering::Relaxed); + } +} + + +#[test] +fn net_service() { + let _service = NetworkService::new( + NetworkConfiguration::new_local(), + vec![(Arc::new(TestProtocol::new(false)), *b"myp", &[(1u8, 1)])] + ).expect("Error creating network service"); +} + +#[test] +#[ignore] // TODO: how is this test even supposed to work? +fn net_disconnect() { + let key1 = Random.generate().unwrap(); + let mut config1 = NetworkConfiguration::new_local(); + config1.use_secret = Some(key1.secret().clone()); + config1.boot_nodes = vec![ ]; + let handler1 = Arc::new(TestProtocol::new(false)); + let service1 = NetworkService::new(config1, vec![(handler1.clone(), *b"tst", &[(42u8, 1), (43u8, 1)])]).unwrap(); + let mut config2 = NetworkConfiguration::new_local(); + config2.boot_nodes = vec![ service1.external_url().unwrap() ]; + let handler2 = Arc::new(TestProtocol::new(true)); + let _service2 = NetworkService::new(config2, vec![(handler2.clone(), *b"tst", &[(42u8, 1), (43u8, 1)])]).unwrap(); + while !(handler1.got_disconnect() && handler2.got_disconnect()) { + thread::sleep(Duration::from_millis(50)); + } + assert!(handler1.got_disconnect()); + assert!(handler2.got_disconnect()); +} + +#[test] +fn net_timeout() { + let config = NetworkConfiguration::new_local(); + let handler = Arc::new(TestProtocol::new(false)); + let _service = NetworkService::new(config, vec![(handler.clone(), *b"tst", &[(42u8, 1), (43u8, 1)])]).unwrap(); + while !handler.got_timeout() { + thread::sleep(Duration::from_millis(50)); + } +} diff --git a/core/network/Cargo.toml b/core/network/Cargo.toml new file mode 100644 index 0000000000000..fdd31aa7d02a0 --- /dev/null +++ b/core/network/Cargo.toml @@ -0,0 +1,30 @@ +[package] +description = "Polkadot network protocol" +name = "substrate-network" +version = "0.1.0" +license = "GPL-3.0" +authors = ["Parity Technologies "] + +[lib] + +[dependencies] +log = "0.3" +parking_lot = "0.4" +error-chain = "0.12" +bitflags = "1.0" +futures = "0.1.17" +linked-hash-map = "0.5" +rustc-hex = "1.0" +ethcore-io = { git = "https://github.com/paritytech/parity.git" } + +substrate-primitives = { path = "../primitives" } +substrate-client = { path = "../client" } +substrate-runtime-primitives = { path = "../../runtime/primitives" } +substrate-codec = { path = "../codec" } +substrate-codec-derive = { path = "../codec/derive" } +substrate-network-libp2p = { path = "../network-libp2p" } + +[dev-dependencies] +env_logger = "0.4" +substrate-keyring = { path = "../keyring" } +substrate-test-client = { path = "../test-client" } diff --git a/core/network/README.adoc b/core/network/README.adoc new file mode 100644 index 0000000000000..ac29b0cd0bfbc --- /dev/null +++ b/core/network/README.adoc @@ -0,0 +1,13 @@ + += Network + +.Summary +[source, toml] +---- +include::Cargo.toml[lines=2..5] +---- + +.Description +---- +include::src/lib.rs[tag=description] +---- diff --git a/core/network/src/blocks.rs b/core/network/src/blocks.rs new file mode 100644 index 0000000000000..b078b9200b311 --- /dev/null +++ b/core/network/src/blocks.rs @@ -0,0 +1,282 @@ +// Copyright 2017 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::mem; +use std::cmp; +use std::ops::Range; +use std::collections::{HashMap, BTreeMap}; +use std::collections::hash_map::Entry; +use network_libp2p::NodeIndex; +use runtime_primitives::traits::{Block as BlockT, NumberFor, As}; +use message; + +const MAX_PARALLEL_DOWNLOADS: u32 = 1; + +/// Block data with origin. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BlockData { + pub block: message::BlockData, + pub origin: NodeIndex, +} + +#[derive(Debug)] +enum BlockRangeState { + Downloading { + len: NumberFor, + downloading: u32, + }, + Complete(Vec>), +} + +impl BlockRangeState { + pub fn len(&self) -> NumberFor { + match *self { + BlockRangeState::Downloading { len, .. } => len, + BlockRangeState::Complete(ref blocks) => As::sa(blocks.len() as u64), + } + } +} + +/// A collection of blocks being downloaded. +#[derive(Default)] +pub struct BlockCollection { + /// Downloaded blocks. + blocks: BTreeMap, BlockRangeState>, + peer_requests: HashMap>, +} + +impl BlockCollection { + /// Create a new instance. + pub fn new() -> Self { + BlockCollection { + blocks: BTreeMap::new(), + peer_requests: HashMap::new(), + } + } + + /// Clear everything. + pub fn clear(&mut self) { + self.blocks.clear(); + self.peer_requests.clear(); + } + + /// Insert a set of blocks into collection. + pub fn insert(&mut self, start: NumberFor, blocks: Vec>, who: NodeIndex) { + if blocks.is_empty() { + return; + } + + match self.blocks.get(&start) { + Some(&BlockRangeState::Downloading { .. }) => { + trace!(target: "sync", "Ignored block data still marked as being downloaded: {}", start); + debug_assert!(false); + return; + }, + Some(&BlockRangeState::Complete(ref existing)) if existing.len() >= blocks.len() => { + trace!(target: "sync", "Ignored block data already downloaded: {}", start); + return; + }, + _ => (), + } + + self.blocks.insert(start, BlockRangeState::Complete(blocks.into_iter().map(|b| BlockData { origin: who, block: b }).collect())); + } + + /// Returns a set of block hashes that require a header download. The returned set is marked as being downloaded. + pub fn needed_blocks(&mut self, who: NodeIndex, count: usize, peer_best: NumberFor, common: NumberFor) -> Option>> { + // First block number that we need to download + let first_different = common + As::sa(1); + let count = As::sa(count as u64); + let (mut range, downloading) = { + let mut downloading_iter = self.blocks.iter().peekable(); + let mut prev: Option<(&NumberFor, &BlockRangeState)> = None; + loop { + let next = downloading_iter.next(); + break match &(prev, next) { + &(Some((start, &BlockRangeState::Downloading { ref len, downloading })), _) if downloading < MAX_PARALLEL_DOWNLOADS => + (*start .. *start + *len, downloading), + &(Some((start, r)), Some((next_start, _))) if *start + r.len() < *next_start => + (*start + r.len() .. cmp::min(*next_start, *start + r.len() + count), 0), // gap + &(Some((start, r)), None) => + (*start + r.len() .. *start + r.len() + count, 0), // last range + &(None, None) => + (first_different .. first_different + count, 0), // empty + &(None, Some((start, _))) if *start > first_different => + (first_different .. cmp::min(first_different + count, *start), 0), // gap at the start + _ => { + prev = next; + continue + }, + } + } + }; + // crop to peers best + if range.start > peer_best { + trace!(target: "sync", "Out of range for peer {} ({} vs {})", who, range.start, peer_best); + return None; + } + range.end = cmp::min(peer_best + As::sa(1), range.end); + self.peer_requests.insert(who, range.start); + self.blocks.insert(range.start, BlockRangeState::Downloading { len: range.end - range.start, downloading: downloading + 1 }); + if range.end <= range.start { + panic!("Empty range {:?}, count={}, peer_best={}, common={}, blocks={:?}", range, count, peer_best, common, self.blocks); + } + Some(range) + } + + /// Get a valid chain of blocks ordered in descending order and ready for importing into blockchain. + pub fn drain(&mut self, from: NumberFor) -> Vec> { + let mut drained = Vec::new(); + let mut ranges = Vec::new(); + { + let mut prev = from; + for (start, range_data) in &mut self.blocks { + match range_data { + &mut BlockRangeState::Complete(ref mut blocks) if *start <= prev => { + prev = *start + As::sa(blocks.len() as u64); + let mut blocks = mem::replace(blocks, Vec::new()); + drained.append(&mut blocks); + ranges.push(*start); + }, + _ => break, + } + } + } + for r in ranges { + self.blocks.remove(&r); + } + trace!(target: "sync", "Drained {} blocks", drained.len()); + drained + } + + pub fn clear_peer_download(&mut self, who: NodeIndex) { + match self.peer_requests.entry(who) { + Entry::Occupied(entry) => { + let start = entry.remove(); + let remove = match self.blocks.get_mut(&start) { + Some(&mut BlockRangeState::Downloading { ref mut downloading, .. }) if *downloading > 1 => { + *downloading = *downloading - 1; + false + }, + Some(&mut BlockRangeState::Downloading { .. }) => { + true + }, + _ => { + debug_assert!(false); + false + } + }; + if remove { + self.blocks.remove(&start); + } + }, + _ => (), + } + } +} + +#[cfg(test)] +mod test { + use super::{BlockCollection, BlockData, BlockRangeState}; + use message; + use runtime_primitives::testing::Block as RawBlock; + use primitives::H256; + + type Block = RawBlock; + + fn is_empty(bc: &BlockCollection) -> bool { + bc.blocks.is_empty() && + bc.peer_requests.is_empty() + } + + fn generate_blocks(n: usize) -> Vec> { + (0 .. n).map(|_| message::generic::BlockData { + hash: H256::random(), + header: None, + body: None, + message_queue: None, + receipt: None, + justification: None, + }).collect() + } + + #[test] + fn create_clear() { + let mut bc = BlockCollection::new(); + assert!(is_empty(&bc)); + bc.insert(1, generate_blocks(100), 0); + assert!(!is_empty(&bc)); + bc.clear(); + assert!(is_empty(&bc)); + } + + #[test] + fn insert_blocks() { + let mut bc = BlockCollection::new(); + assert!(is_empty(&bc)); + let peer0 = 0; + let peer1 = 1; + let peer2 = 2; + + let blocks = generate_blocks(150); + assert_eq!(bc.needed_blocks(peer0, 40, 150, 0), Some(1 .. 41)); + assert_eq!(bc.needed_blocks(peer1, 40, 150, 0), Some(41 .. 81)); + assert_eq!(bc.needed_blocks(peer2, 40, 150, 0), Some(81 .. 121)); + + bc.clear_peer_download(peer1); + bc.insert(41, blocks[41..81].to_vec(), peer1); + assert_eq!(bc.drain(1), vec![]); + assert_eq!(bc.needed_blocks(peer1, 40, 150, 0), Some(121 .. 151)); + bc.clear_peer_download(peer0); + bc.insert(1, blocks[1..11].to_vec(), peer0); + + assert_eq!(bc.needed_blocks(peer0, 40, 150, 0), Some(11 .. 41)); + assert_eq!(bc.drain(1), blocks[1..11].iter().map(|b| BlockData { block: b.clone(), origin: 0 }).collect::>()); + + bc.clear_peer_download(peer0); + bc.insert(11, blocks[11..41].to_vec(), peer0); + + let drained = bc.drain(12); + assert_eq!(drained[..30], blocks[11..41].iter().map(|b| BlockData { block: b.clone(), origin: 0 }).collect::>()[..]); + assert_eq!(drained[30..], blocks[41..81].iter().map(|b| BlockData { block: b.clone(), origin: 1 }).collect::>()[..]); + + bc.clear_peer_download(peer2); + assert_eq!(bc.needed_blocks(peer2, 40, 150, 80), Some(81 .. 121)); + bc.clear_peer_download(peer2); + bc.insert(81, blocks[81..121].to_vec(), peer2); + bc.clear_peer_download(peer1); + bc.insert(121, blocks[121..150].to_vec(), peer1); + + assert_eq!(bc.drain(80), vec![]); + let drained = bc.drain(81); + assert_eq!(drained[..40], blocks[81..121].iter().map(|b| BlockData { block: b.clone(), origin: 2 }).collect::>()[..]); + assert_eq!(drained[40..], blocks[121..150].iter().map(|b| BlockData { block: b.clone(), origin: 1 }).collect::>()[..]); + } + + #[test] + fn large_gap() { + let mut bc: BlockCollection = BlockCollection::new(); + bc.blocks.insert(100, BlockRangeState::Downloading { + len: 128, + downloading: 1, + }); + let blocks = generate_blocks(10).into_iter().map(|b| BlockData { block: b, origin: 0 }).collect(); + bc.blocks.insert(114305, BlockRangeState::Complete(blocks)); + + assert_eq!(bc.needed_blocks(0, 128, 10000, 000), Some(1 .. 100)); + assert_eq!(bc.needed_blocks(0, 128, 10000, 600), Some(100 + 128 .. 100 + 128 + 128)); + } +} diff --git a/core/network/src/chain.rs b/core/network/src/chain.rs new file mode 100644 index 0000000000000..704e32d55d117 --- /dev/null +++ b/core/network/src/chain.rs @@ -0,0 +1,105 @@ +// Copyright 2017 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 . + +//! Blockchain access trait + +use client::{self, Client as SubstrateClient, ImportResult, ClientInfo, BlockStatus, BlockOrigin, CallExecutor}; +use client::error::Error; +use runtime_primitives::traits::{Block as BlockT, Header as HeaderT}; +use runtime_primitives::generic::BlockId; +use runtime_primitives::bft::Justification; +use primitives::{Blake2Hasher, RlpCodec}; + +/// Local client abstraction for the network. +pub trait Client: Send + Sync { + /// Import a new block. Parent is supposed to be existing in the blockchain. + fn import(&self, origin: BlockOrigin, header: Block::Header, justification: Justification, body: Option>) -> Result; + + /// Get blockchain info. + fn info(&self) -> Result, Error>; + + /// Get block status. + fn block_status(&self, id: &BlockId) -> Result; + + /// Get block hash by number. + fn block_hash(&self, block_number: ::Number) -> Result, Error>; + + /// Get block header. + fn header(&self, id: &BlockId) -> Result, Error>; + + /// Get block body. + fn body(&self, id: &BlockId) -> Result>, Error>; + + /// Get block justification. + fn justification(&self, id: &BlockId) -> Result>, Error>; + + /// Get block header proof. + fn header_proof(&self, block_number: ::Number) -> Result<(Block::Header, Vec>), Error>; + + /// Get storage read execution proof. + fn read_proof(&self, block: &Block::Hash, key: &[u8]) -> Result>, Error>; + + /// Get method execution proof. + fn execution_proof(&self, block: &Block::Hash, method: &str, data: &[u8]) -> Result<(Vec, Vec>), Error>; +} + +impl Client for SubstrateClient where + B: client::backend::Backend + Send + Sync + 'static, + E: CallExecutor + Send + Sync + 'static, + Block: BlockT, +{ + fn import(&self, origin: BlockOrigin, header: Block::Header, justification: Justification, body: Option>) -> Result { + // TODO: defer justification check. + let justified_header = self.check_justification(header, justification.into())?; + (self as &SubstrateClient).import_block(origin, justified_header, body) + } + + fn info(&self) -> Result, Error> { + (self as &SubstrateClient).info() + } + + fn block_status(&self, id: &BlockId) -> Result { + (self as &SubstrateClient).block_status(id) + } + + fn block_hash(&self, block_number: ::Number) -> Result, Error> { + (self as &SubstrateClient).block_hash(block_number) + } + + fn header(&self, id: &BlockId) -> Result, Error> { + (self as &SubstrateClient).header(id) + } + + fn body(&self, id: &BlockId) -> Result>, Error> { + (self as &SubstrateClient).body(id) + } + + fn justification(&self, id: &BlockId) -> Result>, Error> { + (self as &SubstrateClient).justification(id) + } + + fn header_proof(&self, block_number: ::Number) -> Result<(Block::Header, Vec>), Error> { + (self as &SubstrateClient).header_proof(&BlockId::Number(block_number)) + } + + fn read_proof(&self, block: &Block::Hash, key: &[u8]) -> Result>, Error> { + (self as &SubstrateClient).read_proof(&BlockId::Hash(block.clone()), key) + } + + fn execution_proof(&self, block: &Block::Hash, method: &str, data: &[u8]) -> Result<(Vec, Vec>), Error> { + (self as &SubstrateClient).execution_proof(&BlockId::Hash(block.clone()), method, data) + } +} diff --git a/core/network/src/config.rs b/core/network/src/config.rs new file mode 100644 index 0000000000000..ad5de3957def3 --- /dev/null +++ b/core/network/src/config.rs @@ -0,0 +1,32 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +pub use service::Roles; + +/// Protocol configuration +#[derive(Clone)] +pub struct ProtocolConfig { + /// Assigned roles. + pub roles: Roles, +} + +impl Default for ProtocolConfig { + fn default() -> ProtocolConfig { + ProtocolConfig { + roles: Roles::FULL, + } + } +} diff --git a/core/network/src/consensus_gossip.rs b/core/network/src/consensus_gossip.rs new file mode 100644 index 0000000000000..7b2f72bd66923 --- /dev/null +++ b/core/network/src/consensus_gossip.rs @@ -0,0 +1,382 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Utility for gossip of network messages between authorities. +//! Handles chain-specific and standard BFT messages. + +use std::collections::{HashMap, HashSet}; +use futures::sync::mpsc; +use std::time::{Instant, Duration}; +use network_libp2p::NodeIndex; +use runtime_primitives::traits::{Block as BlockT, Header as HeaderT}; +use runtime_primitives::generic::BlockId; +use message::{self, generic::Message as GenericMessage}; +use protocol::Context; +use service::Roles; + +// TODO: Add additional spam/DoS attack protection. +const MESSAGE_LIFETIME: Duration = Duration::from_secs(600); + +struct PeerConsensus { + known_messages: HashSet, +} + +/// Consensus messages. +#[derive(Debug, Clone, PartialEq)] +pub enum ConsensusMessage { + /// A message concerning BFT agreement + Bft(message::LocalizedBftMessage), + /// A message concerning some chain-specific aspect of consensus + ChainSpecific(Vec, B::Hash), +} + +struct MessageEntry { + hash: B::Hash, + message: ConsensusMessage, + instant: Instant, +} + +/// Consensus network protocol handler. Manages statements and candidate requests. +pub struct ConsensusGossip { + peers: HashMap>, + message_sink: Option<(mpsc::UnboundedSender>, B::Hash)>, + messages: Vec>, + message_hashes: HashSet, +} + +impl ConsensusGossip where B::Header: HeaderT { + /// Create a new instance. + pub fn new() -> Self { + ConsensusGossip { + peers: HashMap::new(), + message_sink: None, + messages: Default::default(), + message_hashes: Default::default(), + } + } + + /// Closes all notification streams. + pub fn abort(&mut self) { + self.message_sink = None; + } + + /// Handle new connected peer. + pub fn new_peer(&mut self, protocol: &mut Context, who: NodeIndex, roles: Roles) { + if roles.intersects(Roles::AUTHORITY | Roles::FULL) { + trace!(target:"gossip", "Registering {:?} {}", roles, who); + // Send out all known messages. + // TODO: limit by size + let mut known_messages = HashSet::new(); + for entry in self.messages.iter() { + known_messages.insert(entry.hash); + let message = match entry.message { + ConsensusMessage::Bft(ref bft) => GenericMessage::BftMessage(bft.clone()), + ConsensusMessage::ChainSpecific(ref msg, _) => GenericMessage::ChainSpecific(msg.clone()), + }; + + protocol.send_message(who, message); + } + self.peers.insert(who, PeerConsensus { + known_messages, + }); + } + } + + fn propagate(&mut self, protocol: &mut Context, message: message::Message, hash: B::Hash) { + for (id, ref mut peer) in self.peers.iter_mut() { + if peer.known_messages.insert(hash.clone()) { + trace!(target:"gossip", "Propagating to {}: {:?}", id, message); + protocol.send_message(*id, message.clone()); + } + } + } + + fn register_message(&mut self, hash: B::Hash, message: ConsensusMessage) { + if self.message_hashes.insert(hash) { + self.messages.push(MessageEntry { + hash, + instant: Instant::now(), + message, + }); + } + } + + /// Handles incoming BFT message, passing to stream and repropagating. + pub fn on_bft_message(&mut self, protocol: &mut Context, who: NodeIndex, message: message::LocalizedBftMessage) { + if let Some((hash, message)) = self.handle_incoming(protocol, who, ConsensusMessage::Bft(message)) { + // propagate to other peers. + self.multicast(protocol, message, Some(hash)); + } + } + + /// Handles incoming chain-specific message and repropagates + pub fn on_chain_specific(&mut self, protocol: &mut Context, who: NodeIndex, message: Vec, parent_hash: B::Hash) { + debug!(target: "gossip", "received chain-specific gossip message"); + if let Some((hash, message)) = self.handle_incoming(protocol, who, ConsensusMessage::ChainSpecific(message, parent_hash)) { + debug!(target: "gossip", "handled incoming chain-specific message"); + // propagate to other peers. + self.multicast(protocol, message, Some(hash)); + } + } + + /// Get a stream of messages relevant to consensus on top of a given parent hash. + pub fn messages_for(&mut self, parent_hash: B::Hash) -> mpsc::UnboundedReceiver> { + let (sink, stream) = mpsc::unbounded(); + + for entry in self.messages.iter() { + let message_matches = match entry.message { + ConsensusMessage::Bft(ref msg) => msg.parent_hash == parent_hash, + ConsensusMessage::ChainSpecific(_, ref h) => h == &parent_hash, + }; + + if message_matches { + sink.unbounded_send(entry.message.clone()).expect("receiving end known to be open; qed"); + } + } + + self.message_sink = Some((sink, parent_hash)); + stream + } + + /// Multicast a chain-specific message to other authorities. + pub fn multicast_chain_specific(&mut self, protocol: &mut Context, message: Vec, parent_hash: B::Hash) { + trace!(target:"gossip", "sending chain-specific message"); + self.multicast(protocol, ConsensusMessage::ChainSpecific(message, parent_hash), None); + } + + /// Multicast a BFT message to other authorities + pub fn multicast_bft_message(&mut self, protocol: &mut Context, message: message::LocalizedBftMessage) { + // Broadcast message to all authorities. + trace!(target:"gossip", "Broadcasting BFT message {:?}", message); + self.multicast(protocol, ConsensusMessage::Bft(message), None); + } + + /// Call when a peer has been disconnected to stop tracking gossip status. + pub fn peer_disconnected(&mut self, _protocol: &mut Context, who: NodeIndex) { + self.peers.remove(&who); + } + + /// Prune old or no longer relevant consensus messages. + /// Supply an optional block hash where consensus is known to have concluded. + pub fn collect_garbage(&mut self, best_hash: Option<&B::Hash>) { + let hashes = &mut self.message_hashes; + let before = self.messages.len(); + let now = Instant::now(); + self.messages.retain(|entry| { + if entry.instant + MESSAGE_LIFETIME >= now && + best_hash.map_or(true, |parent_hash| + match entry.message { + ConsensusMessage::Bft(ref msg) => &msg.parent_hash != parent_hash, + ConsensusMessage::ChainSpecific(_, ref h) => h != parent_hash, + }) + { + true + } else { + hashes.remove(&entry.hash); + false + } + }); + if self.messages.len() != before { + trace!(target:"gossip", "Cleaned up {} stale messages", before - self.messages.len()); + } + for (_, ref mut peer) in self.peers.iter_mut() { + peer.known_messages.retain(|h| hashes.contains(h)); + } + } + + fn handle_incoming(&mut self, protocol: &mut Context, who: NodeIndex, message: ConsensusMessage) -> Option<(B::Hash, ConsensusMessage)> { + let (hash, parent, message) = match message { + ConsensusMessage::Bft(msg) => { + let parent = msg.parent_hash; + let generic = GenericMessage::BftMessage(msg); + ( + ::protocol::hash_message(&generic), + parent, + match generic { + GenericMessage::BftMessage(msg) => ConsensusMessage::Bft(msg), + _ => panic!("`generic` is known to be the `BftMessage` variant; qed"), + } + ) + } + ConsensusMessage::ChainSpecific(msg, parent) => { + let generic = GenericMessage::ChainSpecific(msg); + ( + ::protocol::hash_message::(&generic), + parent, + match generic { + GenericMessage::ChainSpecific(msg) => ConsensusMessage::ChainSpecific(msg, parent), + _ => panic!("`generic` is known to be the `ChainSpecific` variant; qed"), + } + ) + } + }; + + if self.message_hashes.contains(&hash) { + trace!(target:"gossip", "Ignored already known message from {}", who); + return None; + } + + match (protocol.client().info(), protocol.client().header(&BlockId::Hash(parent))) { + (_, Err(e)) | (Err(e), _) => { + debug!(target:"gossip", "Error reading blockchain: {:?}", e); + return None; + }, + (Ok(info), Ok(Some(header))) => { + if header.number() < &info.chain.best_number { + trace!(target:"gossip", "Ignored ancient message from {}, hash={}", who, parent); + return None; + } + }, + (Ok(_), Ok(None)) => {}, + } + + if let Some(ref mut peer) = self.peers.get_mut(&who) { + peer.known_messages.insert(hash); + if let Some((sink, parent_hash)) = self.message_sink.take() { + if parent == parent_hash { + debug!(target: "gossip", "Pushing relevant consensus message to sink."); + if let Err(e) = sink.unbounded_send(message.clone()) { + trace!(target:"gossip", "Error broadcasting message notification: {:?}", e); + } + } + + self.message_sink = Some((sink, parent_hash)); + } + } else { + trace!(target:"gossip", "Ignored statement from unregistered peer {}", who); + return None; + } + + Some((hash, message)) + } + + fn multicast(&mut self, protocol: &mut Context, message: ConsensusMessage, hash: Option) { + let generic = match message { + ConsensusMessage::Bft(ref message) => GenericMessage::BftMessage(message.clone()), + ConsensusMessage::ChainSpecific(ref message, _) => GenericMessage::ChainSpecific(message.clone()), + }; + + let hash = hash.unwrap_or_else(|| ::protocol::hash_message(&generic)); + self.register_message(hash, message); + self.propagate(protocol, generic, hash); + } +} + +#[cfg(test)] +mod tests { + use runtime_primitives::bft::Justification; + use runtime_primitives::testing::{H256, Header, Block as RawBlock}; + use std::time::Instant; + use message::{self, generic}; + use super::*; + + type Block = RawBlock; + + #[test] + fn collects_garbage() { + let prev_hash = H256::random(); + let best_hash = H256::random(); + let mut consensus = ConsensusGossip::::new(); + let now = Instant::now(); + let m1_hash = H256::random(); + let m2_hash = H256::random(); + let m1 = ConsensusMessage::Bft(message::LocalizedBftMessage { + parent_hash: prev_hash, + message: message::generic::BftMessage::Auxiliary(Justification { + round_number: 0, + hash: Default::default(), + signatures: Default::default(), + }), + }); + let m2 = ConsensusMessage::ChainSpecific(vec![1, 2, 3], best_hash); + + macro_rules! push_msg { + ($hash:expr, $now: expr, $m:expr) => { + consensus.messages.push(MessageEntry { + hash: $hash, + instant: $now, + message: $m, + }) + } + } + + push_msg!(m1_hash, now, m1); + push_msg!(m2_hash, now, m2.clone()); + consensus.message_hashes.insert(m1_hash); + consensus.message_hashes.insert(m2_hash); + + // nothing to collect + consensus.collect_garbage(None); + assert_eq!(consensus.messages.len(), 2); + assert_eq!(consensus.message_hashes.len(), 2); + + // random header, nothing should be cleared + let mut header = Header { + parent_hash: H256::default(), + number: 0, + state_root: H256::default(), + extrinsics_root: H256::default(), + digest: Default::default(), + }; + + consensus.collect_garbage(Some(&H256::default())); + assert_eq!(consensus.messages.len(), 2); + assert_eq!(consensus.message_hashes.len(), 2); + + // header that matches one of the messages + header.parent_hash = prev_hash; + consensus.collect_garbage(Some(&prev_hash)); + assert_eq!(consensus.messages.len(), 1); + assert_eq!(consensus.message_hashes.len(), 1); + assert!(consensus.message_hashes.contains(&m2_hash)); + + // make timestamp expired + consensus.messages.clear(); + push_msg!(m2_hash, now - MESSAGE_LIFETIME, m2); + consensus.collect_garbage(None); + assert!(consensus.messages.is_empty()); + assert!(consensus.message_hashes.is_empty()); + } + + #[test] + fn message_stream_include_those_sent_before_asking_for_stream() { + use futures::Stream; + + let mut consensus = ConsensusGossip::new(); + + let bft_message = generic::BftMessage::Consensus(generic::SignedConsensusMessage::Vote(generic::SignedConsensusVote { + vote: generic::ConsensusVote::AdvanceRound(0), + sender: [0; 32].into(), + signature: Default::default(), + })); + + let parent_hash = [1; 32].into(); + + let localized = ::message::LocalizedBftMessage:: { + message: bft_message, + parent_hash: parent_hash, + }; + + let message = generic::Message::BftMessage(localized.clone()); + let message_hash = ::protocol::hash_message::(&message); + + let message = ConsensusMessage::Bft(localized); + consensus.register_message(message_hash, message.clone()); + let stream = consensus.messages_for(parent_hash); + + assert_eq!(stream.wait().next(), Some(Ok(message))); + } +} diff --git a/core/network/src/error.rs b/core/network/src/error.rs new file mode 100644 index 0000000000000..5ad888eb573ca --- /dev/null +++ b/core/network/src/error.rs @@ -0,0 +1,35 @@ +// Copyright 2017 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 . + +//! Polkadot service possible errors. + +use std::io::Error as IoError; +use network_libp2p::Error as NetworkError; +use client; + +error_chain! { + foreign_links { + Network(NetworkError) #[doc = "Devp2p error."]; + Io(IoError) #[doc = "IO error."]; + } + + links { + Client(client::error::Error, client::error::ErrorKind) #[doc="Client error"]; + } + + errors { + } +} diff --git a/core/network/src/import_queue.rs b/core/network/src/import_queue.rs new file mode 100644 index 0000000000000..647070383b4ce --- /dev/null +++ b/core/network/src/import_queue.rs @@ -0,0 +1,593 @@ +// Copyright 2017 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 . + +//! Blocks import queue. + +use std::collections::{HashSet, VecDeque}; +use std::sync::{Arc, Weak}; +use std::sync::atomic::{AtomicBool, Ordering}; +use parking_lot::{Condvar, Mutex, RwLock}; + +use client::{BlockOrigin, ImportResult}; +use network_libp2p::{NodeIndex, Severity}; + +use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero}; + +use blocks::BlockData; +use chain::Client; +use error::{ErrorKind, Error}; +use protocol::Context; +use service::ExecuteInContext; +use sync::ChainSync; + +/// Blocks import queue API. +pub trait ImportQueue: Send + Sync { + /// Clear the queue when sync is restarting. + fn clear(&self); + /// Clears the import queue and stops importing. + fn stop(&self); + /// Get queue status. + fn status(&self) -> ImportQueueStatus; + /// Is block with given hash currently in the queue. + fn is_importing(&self, hash: &B::Hash) -> bool; + /// Import bunch of blocks. + fn import_blocks(&self, sync: &mut ChainSync, protocol: &mut Context, blocks: (BlockOrigin, Vec>)); +} + +/// Import queue status. It isn't completely accurate. +pub struct ImportQueueStatus { + /// Number of blocks that are currently in the queue. + pub importing_count: usize, + /// The number of the best block that was ever in the queue since start/last failure. + pub best_importing_number: <::Header as HeaderT>::Number, +} + +/// Blocks import queue that is importing blocks in the separate thread. +pub struct AsyncImportQueue { + handle: Mutex>>, + data: Arc>, +} + +/// Locks order: queue, queue_blocks, best_importing_number +struct AsyncImportQueueData { + signal: Condvar, + queue: Mutex>)>>, + queue_blocks: RwLock>, + best_importing_number: RwLock<<::Header as HeaderT>::Number>, + is_stopping: AtomicBool, +} + +impl AsyncImportQueue { + pub fn new() -> Self { + Self { + handle: Mutex::new(None), + data: Arc::new(AsyncImportQueueData::new()), + } + } + + pub fn start>(&self, sync: Weak>>, service: Weak, chain: Weak>) -> Result<(), Error> { + debug_assert!(self.handle.lock().is_none()); + + let qdata = self.data.clone(); + *self.handle.lock() = Some(::std::thread::Builder::new().name("ImportQueue".into()).spawn(move || { + import_thread(sync, service, chain, qdata) + }).map_err(|err| Error::from(ErrorKind::Io(err)))?); + Ok(()) + } +} + +impl AsyncImportQueueData { + pub fn new() -> Self { + Self { + signal: Default::default(), + queue: Mutex::new(VecDeque::new()), + queue_blocks: RwLock::new(HashSet::new()), + best_importing_number: RwLock::new(Zero::zero()), + is_stopping: Default::default(), + } + } +} + +impl ImportQueue for AsyncImportQueue { + fn clear(&self) { + let mut queue = self.data.queue.lock(); + let mut queue_blocks = self.data.queue_blocks.write(); + let mut best_importing_number = self.data.best_importing_number.write(); + queue_blocks.clear(); + queue.clear(); + *best_importing_number = Zero::zero(); + } + + fn stop(&self) { + self.clear(); + if let Some(handle) = self.handle.lock().take() { + self.data.is_stopping.store(true, Ordering::SeqCst); + self.data.signal.notify_one(); + + let _ = handle.join(); + } + } + + fn status(&self) -> ImportQueueStatus { + ImportQueueStatus { + importing_count: self.data.queue_blocks.read().len(), + best_importing_number: *self.data.best_importing_number.read(), + } + } + + fn is_importing(&self, hash: &B::Hash) -> bool { + self.data.queue_blocks.read().contains(hash) + } + + fn import_blocks(&self, _sync: &mut ChainSync, _protocol: &mut Context, blocks: (BlockOrigin, Vec>)) { + if blocks.1.is_empty() { + return; + } + + trace!(target:"sync", "Scheduling {} blocks for import", blocks.1.len()); + + let mut queue = self.data.queue.lock(); + let mut queue_blocks = self.data.queue_blocks.write(); + let mut best_importing_number = self.data.best_importing_number.write(); + let new_best_importing_number = blocks.1.last().and_then(|b| b.block.header.as_ref().map(|h| h.number().clone())).unwrap_or_else(|| Zero::zero()); + queue_blocks.extend(blocks.1.iter().map(|b| b.block.hash.clone())); + if new_best_importing_number > *best_importing_number { + *best_importing_number = new_best_importing_number; + } + queue.push_back(blocks); + self.data.signal.notify_one(); + } +} + +impl Drop for AsyncImportQueue { + fn drop(&mut self) { + self.stop(); + } +} + +/// Blocks import thread. +fn import_thread>(sync: Weak>>, service: Weak, chain: Weak>, qdata: Arc>) { + trace!(target: "sync", "Starting import thread"); + loop { + if qdata.is_stopping.load(Ordering::SeqCst) { + break; + } + + let new_blocks = { + let mut queue_lock = qdata.queue.lock(); + if queue_lock.is_empty() { + qdata.signal.wait(&mut queue_lock); + } + + match queue_lock.pop_front() { + Some(new_blocks) => new_blocks, + None => break, + } + }; + + match (sync.upgrade(), service.upgrade(), chain.upgrade()) { + (Some(sync), Some(service), Some(chain)) => { + let blocks_hashes: Vec = new_blocks.1.iter().map(|b| b.block.hash.clone()).collect(); + if !import_many_blocks(&mut SyncLink::Indirect(&sync, &*chain, &*service), Some(&*qdata), new_blocks) { + break; + } + + let mut queue_blocks = qdata.queue_blocks.write(); + for blocks_hash in blocks_hashes { + queue_blocks.remove(&blocks_hash); + } + }, + _ => break, + } + } + + trace!(target: "sync", "Stopping import thread"); +} + +/// ChainSync link trait. +trait SyncLinkApi { + /// Get chain reference. + fn chain(&self) -> &Client; + /// Block imported. + fn block_imported(&mut self, hash: &B::Hash, number: NumberFor); + /// Maintain sync. + fn maintain_sync(&mut self); + /// Disconnect from peer. + fn useless_peer(&mut self, who: NodeIndex, reason: &str); + /// Disconnect from peer and restart sync. + fn note_useless_and_restart_sync(&mut self, who: NodeIndex, reason: &str); + /// Restart sync. + fn restart(&mut self); +} + +/// Link with the ChainSync service. +enum SyncLink<'a, B: 'a + BlockT, E: 'a + ExecuteInContext> { + /// Indirect link (through service). + Indirect(&'a RwLock>, &'a Client, &'a E), + /// Direct references are given. + #[cfg(test)] + Direct(&'a mut ChainSync, &'a mut Context), +} + +/// Block import successful result. +#[derive(Debug, PartialEq)] +enum BlockImportResult { + /// Imported known block. + ImportedKnown(H, N), + /// Imported unknown block. + ImportedUnknown(H, N), +} + +/// Block import error. +#[derive(Debug, PartialEq)] +enum BlockImportError { + /// Disconnect from peer and continue import of next bunch of blocks. + Disconnect(NodeIndex), + /// Disconnect from peer and restart sync. + DisconnectAndRestart(NodeIndex), + /// Restart sync. + Restart, +} + +/// Import a bunch of blocks. +fn import_many_blocks<'a, B: BlockT>( + link: &mut SyncLinkApi, + qdata: Option<&AsyncImportQueueData>, + blocks: (BlockOrigin, Vec>) +) -> bool +{ + let (blocks_origin, blocks) = blocks; + let count = blocks.len(); + let mut imported = 0; + + let blocks_range = match ( + blocks.first().and_then(|b| b.block.header.as_ref().map(|h| h.number())), + blocks.last().and_then(|b| b.block.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); + + // Blocks in the response/drain should be in ascending order. + for block in blocks { + let import_result = import_single_block(link.chain(), blocks_origin.clone(), block); + let is_import_failed = import_result.is_err(); + imported += process_import_result(link, import_result); + if is_import_failed { + qdata.map(|qdata| *qdata.best_importing_number.write() = Zero::zero()); + return true; + } + + if qdata.map(|qdata| qdata.is_stopping.load(Ordering::SeqCst)).unwrap_or_default() { + return false; + } + } + + trace!(target: "sync", "Imported {} of {}", imported, count); + link.maintain_sync(); + true +} + +/// Single block import function. +fn import_single_block( + chain: &Client, + block_origin: BlockOrigin, + block: BlockData +) -> Result::Header as HeaderT>::Number>, BlockImportError> +{ + let origin = block.origin; + let block = block.block; + match (block.header, block.justification) { + (Some(header), Some(justification)) => { + let number = header.number().clone(); + let hash = header.hash(); + let parent = header.parent_hash().clone(); + + let result = chain.import( + block_origin, + header, + justification, + block.body, + ); + match result { + Ok(ImportResult::AlreadyInChain) => { + trace!(target: "sync", "Block already in chain {}: {:?}", number, hash); + Ok(BlockImportResult::ImportedKnown(hash, number)) + }, + Ok(ImportResult::AlreadyQueued) => { + trace!(target: "sync", "Block already queued {}: {:?}", number, hash); + Ok(BlockImportResult::ImportedKnown(hash, number)) + }, + Ok(ImportResult::Queued) => { + trace!(target: "sync", "Block queued {}: {:?}", number, hash); + Ok(BlockImportResult::ImportedUnknown(hash, number)) + }, + Ok(ImportResult::UnknownParent) => { + debug!(target: "sync", "Block with unknown parent {}: {:?}, parent: {:?}", number, hash, parent); + Err(BlockImportError::Restart) + }, + Ok(ImportResult::KnownBad) => { + debug!(target: "sync", "Peer gave us a bad block {}: {:?}", number, hash); + Err(BlockImportError::DisconnectAndRestart(origin)) //TODO: use persistent ID + } + Err(e) => { + debug!(target: "sync", "Error importing block {}: {:?}: {:?}", number, hash, e); + Err(BlockImportError::Restart) + } + } + }, + (None, _) => { + debug!(target: "sync", "Header {} was not provided by {} ", block.hash, origin); + Err(BlockImportError::Disconnect(origin)) //TODO: use persistent ID + }, + (_, None) => { + debug!(target: "sync", "Justification set for block {} was not provided by {} ", block.hash, origin); + Err(BlockImportError::Disconnect(origin)) //TODO: use persistent ID + } + } +} + +/// Process single block import result. +fn process_import_result<'a, B: BlockT>( + link: &mut SyncLinkApi, + result: Result::Header as HeaderT>::Number>, BlockImportError> +) -> usize +{ + match result { + Ok(BlockImportResult::ImportedKnown(hash, number)) => { + link.block_imported(&hash, number); + 1 + }, + Ok(BlockImportResult::ImportedUnknown(hash, number)) => { + link.block_imported(&hash, number); + 1 + }, + Err(BlockImportError::Disconnect(who)) => { + // TODO: FIXME: @arkpar BlockImport shouldn't be trying to manage the peer set. + // This should contain an actual reason. + link.useless_peer(who, "Import result was stated Disconnect"); + 0 + }, + Err(BlockImportError::DisconnectAndRestart(who)) => { + // TODO: FIXME: @arkpar BlockImport shouldn't be trying to manage the peer set. + // This should contain an actual reason. + link.note_useless_and_restart_sync(who, "Import result was stated DisconnectAndRestart"); + 0 + }, + Err(BlockImportError::Restart) => { + link.restart(); + 0 + }, + } +} + +impl<'a, B: 'static + BlockT, E: 'a + ExecuteInContext> SyncLink<'a, B, E> { + /// Execute closure with locked ChainSync. + fn with_sync, &mut Context)>(&mut self, closure: F) { + match *self { + #[cfg(test)] + SyncLink::Direct(ref mut sync, ref mut protocol) => + closure(*sync, *protocol), + SyncLink::Indirect(ref sync, _, ref service) => + service.execute_in_context(move |protocol| { + let mut sync = sync.write(); + closure(&mut *sync, protocol) + }), + } + } +} + +impl<'a, B: 'static + BlockT, E: ExecuteInContext> SyncLinkApi for SyncLink<'a, B, E> { + fn chain(&self) -> &Client { + match *self { + #[cfg(test)] + SyncLink::Direct(_, ref protocol) => protocol.client(), + SyncLink::Indirect(_, ref chain, _) => *chain, + } + } + + fn block_imported(&mut self, hash: &B::Hash, number: NumberFor) { + self.with_sync(|sync, _| sync.block_imported(&hash, number)) + } + + fn maintain_sync(&mut self) { + self.with_sync(|sync, protocol| sync.maintain_sync(protocol)) + } + + fn useless_peer(&mut self, who: NodeIndex, reason: &str) { + self.with_sync(|_, protocol| protocol.report_peer(who, Severity::Useless(reason))) + } + + fn note_useless_and_restart_sync(&mut self, who: NodeIndex, reason: &str) { + self.with_sync(|sync, protocol| { + protocol.report_peer(who, Severity::Useless(reason)); // is this actually malign or just useless? + sync.restart(protocol); + }) + } + + fn restart(&mut self) { + self.with_sync(|sync, protocol| sync.restart(protocol)) + } +} + +#[cfg(test)] +pub mod tests { + use client; + use message; + use test_client::{self, TestClient}; + use test_client::runtime::{Block, Hash}; + use on_demand::tests::DummyExecutor; + use runtime_primitives::generic::BlockId; + use super::*; + + /// Blocks import queue that is importing blocks in the same thread. + pub struct SyncImportQueue; + struct DummyExecuteInContext; + + impl ExecuteInContext for DummyExecuteInContext { + fn execute_in_context)>(&self, _closure: F) { } + } + + impl ImportQueue for SyncImportQueue { + fn clear(&self) { } + + fn stop(&self) { } + + fn status(&self) -> ImportQueueStatus { + ImportQueueStatus { + importing_count: 0, + best_importing_number: Zero::zero(), + } + } + + fn is_importing(&self, _hash: &B::Hash) -> bool { + false + } + + fn import_blocks(&self, sync: &mut ChainSync, protocol: &mut Context, blocks: (BlockOrigin, Vec>)) { + import_many_blocks(&mut SyncLink::Direct::<_, DummyExecuteInContext>(sync, protocol), None, blocks); + } + } + + struct TestLink { + chain: Arc>, + imported: usize, + maintains: usize, + disconnects: usize, + restarts: usize, + } + + impl TestLink { + fn new() -> TestLink { + TestLink { + chain: Arc::new(test_client::new()), + imported: 0, + maintains: 0, + disconnects: 0, + restarts: 0, + } + } + + fn total(&self) -> usize { + self.imported + self.maintains + self.disconnects + self.restarts + } + } + + impl SyncLinkApi for TestLink { + fn chain(&self) -> &Client { &*self.chain } + fn block_imported(&mut self, _hash: &Hash, _number: NumberFor) { self.imported += 1; } + fn maintain_sync(&mut self) { self.maintains += 1; } + fn useless_peer(&mut self, _: NodeIndex, _: &str) { self.disconnects += 1; } + fn note_useless_and_restart_sync(&mut self, _: NodeIndex, _: &str) { self.disconnects += 1; self.restarts += 1; } + fn restart(&mut self) { self.restarts += 1; } + } + + fn prepare_good_block() -> (client::Client, Hash, u64, BlockData) { + let client = test_client::new(); + let block = client.new_block().unwrap().bake().unwrap(); + client.justify_and_import(BlockOrigin::File, block).unwrap(); + + let (hash, number) = (client.block_hash(1).unwrap().unwrap(), 1); + let block = message::BlockData:: { + hash: client.block_hash(1).unwrap().unwrap(), + header: client.header(&BlockId::Number(1)).unwrap(), + body: None, + receipt: None, + message_queue: None, + justification: client.justification(&BlockId::Number(1)).unwrap(), + }; + + (client, hash, number, BlockData { block, origin: 0 }) + } + + #[test] + fn import_single_good_block_works() { + let (_, hash, number, block) = prepare_good_block(); + assert_eq!(import_single_block(&test_client::new(), BlockOrigin::File, block), Ok(BlockImportResult::ImportedUnknown(hash, number))); + } + + #[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), Ok(BlockImportResult::ImportedKnown(hash, number))); + } + + #[test] + fn import_single_good_block_without_header_fails() { + let (_, _, _, mut block) = prepare_good_block(); + block.block.header = None; + assert_eq!(import_single_block(&test_client::new(), BlockOrigin::File, block), Err(BlockImportError::Disconnect(0))); + } + + #[test] + fn import_single_good_block_without_justification_fails() { + let (_, _, _, mut block) = prepare_good_block(); + block.block.justification = None; + assert_eq!(import_single_block(&test_client::new(), BlockOrigin::File, block), Err(BlockImportError::Disconnect(0))); + } + + #[test] + fn process_import_result_works() { + let mut link = TestLink::new(); + assert_eq!(process_import_result::(&mut link, Ok(BlockImportResult::ImportedKnown(Default::default(), 0))), 1); + assert_eq!(link.total(), 1); + + let mut link = TestLink::new(); + assert_eq!(process_import_result::(&mut link, Ok(BlockImportResult::ImportedKnown(Default::default(), 0))), 1); + assert_eq!(link.total(), 1); + assert_eq!(link.imported, 1); + + let mut link = TestLink::new(); + assert_eq!(process_import_result::(&mut link, Ok(BlockImportResult::ImportedUnknown(Default::default(), 0))), 1); + assert_eq!(link.total(), 1); + assert_eq!(link.imported, 1); + + let mut link = TestLink::new(); + assert_eq!(process_import_result::(&mut link, Err(BlockImportError::Disconnect(0))), 0); + assert_eq!(link.total(), 1); + assert_eq!(link.disconnects, 1); + + let mut link = TestLink::new(); + assert_eq!(process_import_result::(&mut link, Err(BlockImportError::DisconnectAndRestart(0))), 0); + assert_eq!(link.total(), 2); + assert_eq!(link.disconnects, 1); + assert_eq!(link.restarts, 1); + + let mut link = TestLink::new(); + assert_eq!(process_import_result::(&mut link, Err(BlockImportError::Restart)), 0); + assert_eq!(link.total(), 1); + assert_eq!(link.restarts, 1); + } + + #[test] + fn import_many_blocks_stops_when_stopping() { + let (_, _, _, block) = prepare_good_block(); + let qdata = AsyncImportQueueData::new(); + qdata.is_stopping.store(true, Ordering::SeqCst); + assert!(!import_many_blocks(&mut TestLink::new(), Some(&qdata), (BlockOrigin::File, vec![block.clone(), block]))); + } + + #[test] + fn async_import_queue_drops() { + let queue = AsyncImportQueue::new(); + let service = Arc::new(DummyExecutor); + let chain = Arc::new(test_client::new()); + queue.start(Weak::new(), Arc::downgrade(&service), Arc::downgrade(&chain) as Weak>).unwrap(); + drop(queue); + } +} diff --git a/core/network/src/io.rs b/core/network/src/io.rs new file mode 100644 index 0000000000000..4a7d57b158955 --- /dev/null +++ b/core/network/src/io.rs @@ -0,0 +1,72 @@ +// Copyright 2017 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 network_libp2p::{NetworkContext, Severity, NodeIndex, SessionInfo}; + +/// IO interface for the syncing handler. +/// Provides peer connection management and an interface to the blockchain client. +pub trait SyncIo { + /// Report a peer for misbehaviour. + fn report_peer(&mut self, who: NodeIndex, reason: Severity); + /// Send a packet to a peer. + fn send(&mut self, who: NodeIndex, data: Vec); + /// Returns peer identifier string + fn peer_info(&self, who: NodeIndex) -> String { + who.to_string() + } + /// Returns information on p2p session + fn peer_session_info(&self, who: NodeIndex) -> Option; + /// Check if the session is expired + fn is_expired(&self) -> bool; +} + +/// Wraps `NetworkContext` and the blockchain client +pub struct NetSyncIo<'s> { + network: &'s NetworkContext, +} + +impl<'s> NetSyncIo<'s> { + /// Creates a new instance from the `NetworkContext` and the blockchain client reference. + pub fn new(network: &'s NetworkContext) -> NetSyncIo<'s> { + NetSyncIo { + network: network, + } + } +} + +impl<'s> SyncIo for NetSyncIo<'s> { + fn report_peer(&mut self, who: NodeIndex, reason: Severity) { + self.network.report_peer(who, reason); + } + + fn send(&mut self, who: NodeIndex, data: Vec) { + self.network.send(who, 0, data) + } + + fn peer_session_info(&self, who: NodeIndex) -> Option { + self.network.session_info(who) + } + + fn is_expired(&self) -> bool { + self.network.is_expired() + } + + fn peer_info(&self, who: NodeIndex) -> String { + self.network.peer_client_version(who) + } +} + + diff --git a/core/network/src/lib.rs b/core/network/src/lib.rs new file mode 100644 index 0000000000000..7f0817653eb59 --- /dev/null +++ b/core/network/src/lib.rs @@ -0,0 +1,69 @@ +// Copyright 2017 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 . + +#![warn(unused_extern_crates)] +#![warn(missing_docs)] + +// tag::description[] +//! Substrate-specific P2P networking: synchronizing blocks, propagating BFT messages. +//! Allows attachment of an optional subprotocol for chain-specific requests. +// end::description[] + +extern crate ethcore_io as core_io; +extern crate linked_hash_map; +extern crate parking_lot; +extern crate substrate_primitives as primitives; +extern crate substrate_client as client; +extern crate substrate_runtime_primitives as runtime_primitives; +extern crate substrate_network_libp2p as network_libp2p; +extern crate substrate_codec as codec; +extern crate futures; +extern crate rustc_hex; +#[macro_use] extern crate log; +#[macro_use] extern crate bitflags; +#[macro_use] extern crate error_chain; +#[macro_use] extern crate substrate_codec_derive; + +#[cfg(test)] extern crate env_logger; +#[cfg(test)] extern crate substrate_keyring as keyring; +#[cfg(test)] extern crate substrate_test_client as test_client; + +mod service; +mod sync; +mod protocol; +mod io; +mod config; +mod chain; +mod blocks; +mod on_demand; +mod import_queue; +pub mod consensus_gossip; +pub mod error; +pub mod message; +pub mod specialization; + +#[cfg(test)] mod test; + +pub use chain::Client as ClientHandle; +pub use service::{Service, FetchFuture, ConsensusService, BftMessageStream, + TransactionPool, Params, ManageNetwork, SyncProvider}; +pub use protocol::{ProtocolStatus, PeerInfo, Context}; +pub use sync::{Status as SyncStatus, SyncState}; +pub use network_libp2p::{NonReservedPeerMode, NetworkConfiguration, NodeIndex, ProtocolId, ConnectionFilter, ConnectionDirection, Severity}; +pub use message::{generic as generic_message, RequestId, BftMessage, LocalizedBftMessage, ConsensusVote, SignedConsensusVote, SignedConsensusMessage, SignedConsensusProposal, Status as StatusMessage}; +pub use error::Error; +pub use config::{Roles, ProtocolConfig}; +pub use on_demand::{OnDemand, OnDemandService, RemoteResponse}; diff --git a/core/network/src/message.rs b/core/network/src/message.rs new file mode 100644 index 0000000000000..3bb080e13354d --- /dev/null +++ b/core/network/src/message.rs @@ -0,0 +1,375 @@ +// Copyright 2017 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 packet message types. These get serialized and put into the lower level protocol payload. + +use runtime_primitives::traits::{Block as BlockT, Header as HeaderT}; +use codec::{Encode, Decode, Input, Output}; +pub use self::generic::{ + BlockAnnounce, RemoteCallRequest, RemoteReadRequest, + RemoteHeaderRequest, RemoteHeaderResponse, ConsensusVote, + SignedConsensusVote, FromBlock +}; + +/// A unique ID of a request. +pub type RequestId = u64; + +/// Type alias for using the message type using block type parameters. +pub type Message = generic::Message< + B, + ::Header, + ::Hash, + <::Header as HeaderT>::Number, + ::Extrinsic, +>; + +/// Type alias for using the status type using block type parameters. +pub type Status = generic::Status< + ::Hash, + <::Header as HeaderT>::Number, +>; + +/// Type alias for using the block request type using block type parameters. +pub type BlockRequest = generic::BlockRequest< + ::Hash, + <::Header as HeaderT>::Number, +>; + +/// Type alias for using the localized bft message type using block type parameters. +pub type LocalizedBftMessage = generic::LocalizedBftMessage< + B, + ::Hash, +>; + +/// Type alias for using the BlockData type using block type parameters. +pub type BlockData = generic::BlockData< + ::Header, + ::Hash, + ::Extrinsic, +>; + +/// Type alias for using the BlockResponse type using block type parameters. +pub type BlockResponse = generic::BlockResponse< + ::Header, + ::Hash, + ::Extrinsic, +>; + +/// Type alias for using the BftMessage type using block type parameters. +pub type BftMessage = generic::BftMessage< + B, + ::Hash, +>; + +/// Type alias for using the SignedConsensusProposal type using block type parameters. +pub type SignedConsensusProposal = generic::SignedConsensusProposal< + B, + ::Hash, +>; + +/// Type alias for using the SignedConsensusProposal type using block type parameters. +pub type SignedConsensusMessage = generic::SignedConsensusProposal< + B, + ::Hash, +>; + +/// A set of transactions. +pub type Transactions = Vec; + +/// Bits of block data and associated artefacts to request. +bitflags! { + /// Node roles bitmask. + pub struct BlockAttributes: u8 { + /// Include block header. + const HEADER = 0b00000001; + /// Include block body. + const BODY = 0b00000010; + /// Include block receipt. + const RECEIPT = 0b00000100; + /// Include block message queue. + const MESSAGE_QUEUE = 0b00001000; + /// Include a justification for the block. + const JUSTIFICATION = 0b00010000; + } +} + +impl Encode for BlockAttributes { + fn encode_to(&self, dest: &mut T) { + dest.push_byte(self.bits()) + } +} + +impl Decode for BlockAttributes { + fn decode(input: &mut I) -> Option { + Self::from_bits(input.read_byte()?) + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy, Encode, Decode)] +/// Block enumeration direction. +pub enum Direction { + /// Enumerate in ascending order (from child to parent). + Ascending = 0, + /// Enumerate in descendfing order (from parent to canonical child). + Descending = 1, +} + +/// Remote call response. +#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] +pub struct RemoteCallResponse { + /// Id of a request this response was made for. + pub id: RequestId, + /// Execution proof. + pub proof: Vec>, +} + +#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] +/// Remote read response. +pub struct RemoteReadResponse { + /// Id of a request this response was made for. + pub id: RequestId, + /// Read proof. + pub proof: Vec>, +} + +/// Generic types. +pub mod generic { + use primitives::{AuthorityId, ed25519}; + use runtime_primitives::bft::Justification; + use service::Roles; + use super::{ + BlockAttributes, RemoteCallResponse, RemoteReadResponse, + RequestId, Transactions, Direction + }; + + /// Block data sent in the response. + #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] + pub struct BlockData { + /// Block header hash. + pub hash: Hash, + /// Block header if requested. + pub header: Option

, + /// Block body if requested. + pub body: Option>, + /// Block receipt if requested. + pub receipt: Option>, + /// Block message queue if requested. + pub message_queue: Option>, + /// Justification if requested. + pub justification: Option>, + } + + /// Identifies starting point of a block sequence. + #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] + pub enum FromBlock { + /// Start with given hash. + Hash(Hash), + /// Start with given block number. + Number(Number), + } + + /// Communication that can occur between participants in consensus. + #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] + pub enum BftMessage { + /// A consensus message (proposal or vote) + Consensus(SignedConsensusMessage), + /// Auxiliary communication (just proof-of-lock for now). + Auxiliary(Justification), + } + + /// BFT Consensus message with parent header hash attached to it. + #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] + pub struct LocalizedBftMessage { + /// Consensus message. + pub message: BftMessage, + /// Parent header hash. + pub parent_hash: Hash, + } + + /// A localized proposal message. Contains two signed pieces of data. + #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] + pub struct SignedConsensusProposal { + /// The round number. + pub round_number: u32, + /// The proposal sent. + pub proposal: Block, + /// The digest of the proposal. + pub digest: Hash, + /// The sender of the proposal + pub sender: AuthorityId, + /// The signature on the message (propose, round number, digest) + pub digest_signature: ed25519::Signature, + /// The signature on the message (propose, round number, proposal) + pub full_signature: ed25519::Signature, + } + + /// A localized vote message, including the sender. + #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] + pub struct SignedConsensusVote { + /// The message sent. + pub vote: ConsensusVote, + /// The sender of the message + pub sender: AuthorityId, + /// The signature of the message. + pub signature: ed25519::Signature, + } + + /// Votes during a consensus round. + #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] + pub enum ConsensusVote { + /// Prepare to vote for proposal with digest D. + Prepare(u32, H), + /// Commit to proposal with digest D.. + Commit(u32, H), + /// Propose advancement to a new round. + AdvanceRound(u32), + } + + /// A localized message. + #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] + pub enum SignedConsensusMessage { + /// A proposal. + Propose(SignedConsensusProposal), + /// A vote. + Vote(SignedConsensusVote), + } + + /// A network message. + #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] + pub enum Message { + /// Status packet. + Status(Status), + /// Block request. + BlockRequest(BlockRequest), + /// Block response. + BlockResponse(BlockResponse), + /// Block announce. + BlockAnnounce(BlockAnnounce
), + /// Transactions. + Transactions(Transactions), + /// BFT Consensus statement. + BftMessage(LocalizedBftMessage), + /// Remote method call request. + RemoteCallRequest(RemoteCallRequest), + /// Remote method call response. + RemoteCallResponse(RemoteCallResponse), + /// Remote storage read request. + RemoteReadRequest(RemoteReadRequest), + /// Remote storage read response. + RemoteReadResponse(RemoteReadResponse), + /// Remote header request. + RemoteHeaderRequest(RemoteHeaderRequest), + /// Remote header response. + RemoteHeaderResponse(RemoteHeaderResponse
), + /// Chain-specific message + #[codec(index = "255")] + ChainSpecific(Vec), + } + + /// Status sent on connection. + #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] + pub struct Status { + /// Protocol version. + pub version: u32, + /// Supported roles. + pub roles: Roles, + /// Best block number. + pub best_number: Number, + /// Best block hash. + pub best_hash: Hash, + /// Genesis block hash. + pub genesis_hash: Hash, + /// Chain-specific status. + pub chain_status: Vec, + } + + /// Request block data from a peer. + #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] + pub struct BlockRequest { + /// Unique request id. + pub id: RequestId, + /// Bits of block data to request. + pub fields: BlockAttributes, + /// Start from this block. + pub from: FromBlock, + /// End at this block. An implementation defined maximum is used when unspecified. + pub to: Option, + /// Sequence direction. + pub direction: Direction, + /// Maximum number of blocks to return. An implementation defined maximum is used when unspecified. + pub max: Option, + } + + /// Response to `BlockRequest` + #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] + pub struct BlockResponse { + /// Id of a request this response was made for. + pub id: RequestId, + /// Block data for the requested sequence. + pub blocks: Vec>, + } + + /// Announce a new complete relay chain block on the network. + #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] + pub struct BlockAnnounce { + /// New block header. + pub header: H, + } + + #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] + /// Remote call request. + pub struct RemoteCallRequest { + /// Unique request id. + pub id: RequestId, + /// Block at which to perform call. + pub block: H, + /// Method name. + pub method: String, + /// Call data. + pub data: Vec, + } + + #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] + /// Remote storage read request. + pub struct RemoteReadRequest { + /// Unique request id. + pub id: RequestId, + /// Block at which to perform call. + pub block: H, + /// Storage key. + pub key: Vec, + } + + #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] + /// Remote header request. + pub struct RemoteHeaderRequest { + /// Unique request id. + pub id: RequestId, + /// Block number to request header for. + pub block: N, + } + + #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] + /// Remote header response. + pub struct RemoteHeaderResponse
{ + /// Id of a request this response was made for. + pub id: RequestId, + /// Header. None if proof generation has failed (e.g. header is unknown). + pub header: Option
, + /// Header proof. + pub proof: Vec>, + } +} diff --git a/core/network/src/on_demand.rs b/core/network/src/on_demand.rs new file mode 100644 index 0000000000000..7883b5fa14360 --- /dev/null +++ b/core/network/src/on_demand.rs @@ -0,0 +1,736 @@ +// Copyright 2017 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 . + +//! On-demand requests service. + +use std::collections::VecDeque; +use std::sync::{Arc, Weak}; +use std::time::{Instant, Duration}; +use futures::{Async, Future, Poll}; +use futures::sync::oneshot::{channel, Receiver, Sender}; +use linked_hash_map::LinkedHashMap; +use linked_hash_map::Entry; +use parking_lot::Mutex; +use client; +use client::light::fetcher::{Fetcher, FetchChecker, RemoteHeaderRequest, + RemoteCallRequest, RemoteReadRequest}; +use io::SyncIo; +use message; +use network_libp2p::{Severity, NodeIndex}; +use service; +use runtime_primitives::traits::{Block as BlockT, Header as HeaderT}; + +/// Remote request timeout. +const REQUEST_TIMEOUT: Duration = Duration::from_secs(15); +/// Default request retry count. +const RETRY_COUNT: usize = 1; + +/// On-demand service API. +pub trait OnDemandService: Send + Sync { + /// When new node is connected. + fn on_connect(&self, peer: NodeIndex, role: service::Roles); + + /// When node is disconnected. + fn on_disconnect(&self, peer: NodeIndex); + + /// Maintain peers requests. + fn maintain_peers(&self, io: &mut SyncIo); + + /// When header response is received from remote node. + fn on_remote_header_response( + &self, + io: &mut SyncIo, + peer: NodeIndex, + response: message::RemoteHeaderResponse + ); + + /// When read response is received from remote node. + fn on_remote_read_response(&self, io: &mut SyncIo, peer: NodeIndex, response: message::RemoteReadResponse); + + /// When call response is received from remote node. + fn on_remote_call_response(&self, io: &mut SyncIo, peer: NodeIndex, response: message::RemoteCallResponse); +} + +/// On-demand requests service. Dispatches requests to appropriate peers. +pub struct OnDemand> { + core: Mutex>, + checker: Arc>, +} + +/// On-demand remote call response. +pub struct RemoteResponse { + receiver: Receiver>, +} + +#[derive(Default)] +struct OnDemandCore> { + service: Weak, + next_request_id: u64, + pending_requests: VecDeque>, + active_peers: LinkedHashMap>, + idle_peers: VecDeque, +} + +struct Request { + id: u64, + timestamp: Instant, + retry_count: usize, + data: RequestData, +} + +enum RequestData { + RemoteHeader(RemoteHeaderRequest, Sender>), + RemoteRead(RemoteReadRequest, Sender>, client::error::Error>>), + RemoteCall(RemoteCallRequest, Sender>), +} + +enum Accept { + Ok, + CheckFailed(client::error::Error, RequestData), + Unexpected(RequestData), +} + +impl Future for RemoteResponse { + type Item = T; + type Error = client::error::Error; + + fn poll(&mut self) -> Poll { + self.receiver.poll() + .map_err(|_| client::error::ErrorKind::RemoteFetchCancelled.into()) + .and_then(|r| match r { + Async::Ready(Ok(ready)) => Ok(Async::Ready(ready)), + Async::Ready(Err(error)) => Err(error), + Async::NotReady => Ok(Async::NotReady), + }) + } +} + +impl OnDemand where + E: service::ExecuteInContext, + B::Header: HeaderT, +{ + /// Creates new on-demand service. + pub fn new(checker: Arc>) -> Self { + OnDemand { + checker, + core: Mutex::new(OnDemandCore { + service: Weak::new(), + next_request_id: 0, + pending_requests: VecDeque::new(), + active_peers: LinkedHashMap::new(), + idle_peers: VecDeque::new(), + }) + } + } + + /// Sets weak reference to network service. + pub fn set_service_link(&self, service: Weak) { + self.core.lock().service = service; + } + + /// Schedule && dispatch all scheduled requests. + fn schedule_request(&self, retry_count: Option, data: RequestData, result: R) -> R { + let mut core = self.core.lock(); + core.insert(retry_count.unwrap_or(RETRY_COUNT), data); + core.dispatch(); + result + } + + /// Try to accept response from given peer. + fn accept_response) -> Accept>(&self, rtype: &str, io: &mut SyncIo, peer: NodeIndex, request_id: u64, try_accept: F) { + let mut core = self.core.lock(); + let request = match core.remove(peer, request_id) { + Some(request) => request, + None => { + io.report_peer(peer, Severity::Bad(&format!("Invalid remote {} response from peer", rtype))); + core.remove_peer(peer); + return; + }, + }; + + let retry_count = request.retry_count; + let (retry_count, retry_request_data) = match try_accept(request) { + Accept::Ok => (retry_count, None), + Accept::CheckFailed(error, retry_request_data) => { + io.report_peer(peer, Severity::Bad(&format!("Failed to check remote {} response from peer: {}", rtype, error))); + core.remove_peer(peer); + + if retry_count > 0 { + (retry_count - 1, Some(retry_request_data)) + } else { + trace!(target: "sync", "Failed to get remote {} response for given number of retries", rtype); + retry_request_data.fail(client::error::ErrorKind::RemoteFetchFailed.into()); + (0, None) + } + }, + Accept::Unexpected(retry_request_data) => { + io.report_peer(peer, Severity::Bad(&format!("Unexpected response to remote {} from peer", rtype))); + core.remove_peer(peer); + + (retry_count, Some(retry_request_data)) + }, + }; + + if let Some(request_data) = retry_request_data { + core.insert(retry_count, request_data); + } + + core.dispatch(); + } +} + +impl OnDemandService for OnDemand where + B: BlockT, + E: service::ExecuteInContext, + B::Header: HeaderT, +{ + fn on_connect(&self, peer: NodeIndex, role: service::Roles) { + if !role.intersects(service::Roles::FULL | service::Roles::AUTHORITY) { // TODO: correct? + return; + } + + let mut core = self.core.lock(); + core.add_peer(peer); + core.dispatch(); + } + + fn on_disconnect(&self, peer: NodeIndex) { + let mut core = self.core.lock(); + core.remove_peer(peer); + core.dispatch(); + } + + fn maintain_peers(&self, io: &mut SyncIo) { + let mut core = self.core.lock(); + for bad_peer in core.maintain_peers() { + io.report_peer(bad_peer, Severity::Timeout); + } + core.dispatch(); + } + + fn on_remote_header_response(&self, io: &mut SyncIo, peer: NodeIndex, response: message::RemoteHeaderResponse) { + self.accept_response("header", io, peer, response.id, |request| match request.data { + RequestData::RemoteHeader(request, sender) => match self.checker.check_header_proof(&request, response.header, response.proof) { + Ok(response) => { + // we do not bother if receiver has been dropped already + let _ = sender.send(Ok(response)); + Accept::Ok + }, + Err(error) => Accept::CheckFailed(error, RequestData::RemoteHeader(request, sender)), + }, + data @ _ => Accept::Unexpected(data), + }) + } + + fn on_remote_read_response(&self, io: &mut SyncIo, peer: NodeIndex, response: message::RemoteReadResponse) { + self.accept_response("read", io, peer, response.id, |request| match request.data { + RequestData::RemoteRead(request, sender) => match self.checker.check_read_proof(&request, response.proof) { + Ok(response) => { + // we do not bother if receiver has been dropped already + let _ = sender.send(Ok(response)); + Accept::Ok + }, + Err(error) => Accept::CheckFailed(error, RequestData::RemoteRead(request, sender)), + }, + data @ _ => Accept::Unexpected(data), + }) + } + + fn on_remote_call_response(&self, io: &mut SyncIo, peer: NodeIndex, response: message::RemoteCallResponse) { + self.accept_response("call", io, peer, response.id, |request| match request.data { + RequestData::RemoteCall(request, sender) => match self.checker.check_execution_proof(&request, response.proof) { + Ok(response) => { + // we do not bother if receiver has been dropped already + let _ = sender.send(Ok(response)); + Accept::Ok + }, + Err(error) => Accept::CheckFailed(error, RequestData::RemoteCall(request, sender)), + }, + data @ _ => Accept::Unexpected(data), + }) + } +} + +impl Fetcher for OnDemand where + B: BlockT, + E: service::ExecuteInContext, + B::Header: HeaderT, +{ + type RemoteHeaderResult = RemoteResponse; + type RemoteReadResult = RemoteResponse>>; + type RemoteCallResult = RemoteResponse; + + fn remote_header(&self, request: RemoteHeaderRequest) -> Self::RemoteHeaderResult { + let (sender, receiver) = channel(); + self.schedule_request(request.retry_count.clone(), RequestData::RemoteHeader(request, sender), + RemoteResponse { receiver }) + } + + fn remote_read(&self, request: RemoteReadRequest) -> Self::RemoteReadResult { + let (sender, receiver) = channel(); + self.schedule_request(request.retry_count.clone(), RequestData::RemoteRead(request, sender), + RemoteResponse { receiver }) + } + + fn remote_call(&self, request: RemoteCallRequest) -> Self::RemoteCallResult { + let (sender, receiver) = channel(); + self.schedule_request(request.retry_count.clone(), RequestData::RemoteCall(request, sender), + RemoteResponse { receiver }) + } +} + +impl OnDemandCore where + B: BlockT, + E: service::ExecuteInContext, + B::Header: HeaderT, +{ + pub fn add_peer(&mut self, peer: NodeIndex) { + self.idle_peers.push_back(peer); + } + + pub fn remove_peer(&mut self, peer: NodeIndex) { + if let Some(request) = self.active_peers.remove(&peer) { + self.pending_requests.push_front(request); + return; + } + + if let Some(idle_index) = self.idle_peers.iter().position(|i| *i == peer) { + self.idle_peers.swap_remove_back(idle_index); + } + } + + pub fn maintain_peers(&mut self) -> Vec { + let now = Instant::now(); + let mut bad_peers = Vec::new(); + loop { + match self.active_peers.front() { + Some((_, request)) if now - request.timestamp >= REQUEST_TIMEOUT => (), + _ => return bad_peers, + } + + let (bad_peer, request) = self.active_peers.pop_front().expect("front() is Some as checked above"); + self.pending_requests.push_front(request); + bad_peers.push(bad_peer); + } + } + + pub fn insert(&mut self, retry_count: usize, data: RequestData) { + let request_id = self.next_request_id; + self.next_request_id += 1; + + self.pending_requests.push_back(Request { + id: request_id, + timestamp: Instant::now(), + retry_count, + data, + }); + } + + pub fn remove(&mut self, peer: NodeIndex, id: u64) -> Option> { + match self.active_peers.entry(peer) { + Entry::Occupied(entry) => match entry.get().id == id { + true => { + self.idle_peers.push_back(peer); + Some(entry.remove()) + }, + false => None, + }, + Entry::Vacant(_) => None, + } + } + + pub fn dispatch(&mut self) { + let service = match self.service.upgrade() { + Some(service) => service, + None => return, + }; + + while !self.pending_requests.is_empty() { + let peer = match self.idle_peers.pop_front() { + Some(peer) => peer, + None => return, + }; + + let mut request = self.pending_requests.pop_front().expect("checked in loop condition; qed"); + request.timestamp = Instant::now(); + trace!(target: "sync", "Dispatching remote request {} to peer {}", request.id, peer); + + service.execute_in_context(|ctx| ctx.send_message(peer, request.message())); + self.active_peers.insert(peer, request); + } + } +} + +impl Request { + pub fn message(&self) -> message::Message { + match self.data { + RequestData::RemoteHeader(ref data, _) => message::generic::Message::RemoteHeaderRequest( + message::RemoteHeaderRequest { + id: self.id, + block: data.block, + }), + RequestData::RemoteRead(ref data, _) => message::generic::Message::RemoteReadRequest( + message::RemoteReadRequest { + id: self.id, + block: data.block, + key: data.key.clone(), + }), + RequestData::RemoteCall(ref data, _) => message::generic::Message::RemoteCallRequest( + message::RemoteCallRequest { + id: self.id, + block: data.block, + method: data.method.clone(), + data: data.call_data.clone(), + }), + } + } +} + +impl RequestData { + pub fn fail(self, error: client::error::Error) { + // don't care if anyone is listening + match self { + RequestData::RemoteHeader(_, sender) => { let _ = sender.send(Err(error)); }, + RequestData::RemoteCall(_, sender) => { let _ = sender.send(Err(error)); }, + RequestData::RemoteRead(_, sender) => { let _ = sender.send(Err(error)); }, + } + } +} + +#[cfg(test)] +pub mod tests { + use std::collections::VecDeque; + use std::sync::Arc; + use std::time::Instant; + use futures::Future; + use parking_lot::RwLock; + use client; + use client::light::fetcher::{Fetcher, FetchChecker, RemoteHeaderRequest, + RemoteCallRequest, RemoteReadRequest}; + use message; + use network_libp2p::NodeIndex; + use service::{Roles, ExecuteInContext}; + use test::TestIo; + use super::{REQUEST_TIMEOUT, OnDemand, OnDemandService}; + use test_client::runtime::{Block, Header}; + + pub struct DummyExecutor; + struct DummyFetchChecker { ok: bool } + + impl ExecuteInContext for DummyExecutor { + fn execute_in_context)>(&self, _closure: F) {} + } + + impl FetchChecker for DummyFetchChecker { + fn check_header_proof( + &self, + _request: &RemoteHeaderRequest
, + header: Option
, + _remote_proof: Vec> + ) -> client::error::Result
{ + match self.ok { + true if header.is_some() => Ok(header.unwrap()), + _ => Err(client::error::ErrorKind::Backend("Test error".into()).into()), + } + } + + fn check_read_proof(&self, _request: &RemoteReadRequest
, _remote_proof: Vec>) -> client::error::Result>> { + match self.ok { + true => Ok(Some(vec![42])), + false => Err(client::error::ErrorKind::Backend("Test error".into()).into()), + } + } + + fn check_execution_proof(&self, _request: &RemoteCallRequest
, _remote_proof: Vec>) -> client::error::Result { + match self.ok { + true => Ok(client::CallResult { + return_data: vec![42], + changes: Default::default(), + }), + false => Err(client::error::ErrorKind::Backend("Test error".into()).into()), + } + } + } + + fn dummy(ok: bool) -> (Arc, Arc>) { + let executor = Arc::new(DummyExecutor); + let service = Arc::new(OnDemand::new(Arc::new(DummyFetchChecker { ok }))); + service.set_service_link(Arc::downgrade(&executor)); + (executor, service) + } + + fn total_peers(on_demand: &OnDemand) -> usize { + let core = on_demand.core.lock(); + core.idle_peers.len() + core.active_peers.len() + } + + fn receive_call_response(on_demand: &OnDemand, network: &mut TestIo, peer: NodeIndex, id: message::RequestId) { + on_demand.on_remote_call_response(network, peer, message::RemoteCallResponse { + id: id, + proof: vec![vec![2]], + }); + } + + fn dummy_header() -> Header { + Header { + parent_hash: Default::default(), + number: 0, + state_root: Default::default(), + extrinsics_root: Default::default(), + digest: Default::default(), + } + } + + #[test] + fn knows_about_peers_roles() { + let (_, on_demand) = dummy(true); + on_demand.on_connect(0, Roles::LIGHT); + on_demand.on_connect(1, Roles::FULL); + on_demand.on_connect(2, Roles::AUTHORITY); + assert_eq!(vec![1, 2], on_demand.core.lock().idle_peers.iter().cloned().collect::>()); + } + + #[test] + fn disconnects_from_idle_peer() { + let (_, on_demand) = dummy(true); + on_demand.on_connect(0, Roles::FULL); + assert_eq!(1, total_peers(&*on_demand)); + on_demand.on_disconnect(0); + assert_eq!(0, total_peers(&*on_demand)); + } + + #[test] + fn disconnects_from_timeouted_peer() { + let (_x, on_demand) = dummy(true); + let queue = RwLock::new(VecDeque::new()); + let mut network = TestIo::new(&queue, None); + + on_demand.on_connect(0, Roles::FULL); + on_demand.on_connect(1, Roles::FULL); + assert_eq!(vec![0, 1], on_demand.core.lock().idle_peers.iter().cloned().collect::>()); + assert!(on_demand.core.lock().active_peers.is_empty()); + + on_demand.remote_call(RemoteCallRequest { + block: Default::default(), + header: dummy_header(), + method: "test".into(), + call_data: vec![], + retry_count: None, + }); + assert_eq!(vec![1], on_demand.core.lock().idle_peers.iter().cloned().collect::>()); + assert_eq!(vec![0], on_demand.core.lock().active_peers.keys().cloned().collect::>()); + + on_demand.core.lock().active_peers[&0].timestamp = Instant::now() - REQUEST_TIMEOUT - REQUEST_TIMEOUT; + on_demand.maintain_peers(&mut network); + assert!(on_demand.core.lock().idle_peers.is_empty()); + assert_eq!(vec![1], on_demand.core.lock().active_peers.keys().cloned().collect::>()); + assert!(network.to_disconnect.contains(&0)); + } + + #[test] + fn disconnects_from_peer_on_response_with_wrong_id() { + let (_x, on_demand) = dummy(true); + let queue = RwLock::new(VecDeque::new()); + let mut network = TestIo::new(&queue, None); + on_demand.on_connect(0, Roles::FULL); + + on_demand.remote_call(RemoteCallRequest { + block: Default::default(), + header: dummy_header(), + method: "test".into(), + call_data: vec![], + retry_count: None, + }); + receive_call_response(&*on_demand, &mut network, 0, 1); + assert!(network.to_disconnect.contains(&0)); + assert_eq!(on_demand.core.lock().pending_requests.len(), 1); + } + + #[test] + fn disconnects_from_peer_on_incorrect_response() { + let (_x, on_demand) = dummy(false); + let queue = RwLock::new(VecDeque::new()); + let mut network = TestIo::new(&queue, None); + on_demand.remote_call(RemoteCallRequest { + block: Default::default(), + header: dummy_header(), + method: "test".into(), + call_data: vec![], + retry_count: Some(1), + }); + + on_demand.on_connect(0, Roles::FULL); + receive_call_response(&*on_demand, &mut network, 0, 0); + assert!(network.to_disconnect.contains(&0)); + assert_eq!(on_demand.core.lock().pending_requests.len(), 1); + } + + #[test] + fn disconnects_from_peer_on_unexpected_response() { + let (_x, on_demand) = dummy(true); + let queue = RwLock::new(VecDeque::new()); + let mut network = TestIo::new(&queue, None); + on_demand.on_connect(0, Roles::FULL); + + receive_call_response(&*on_demand, &mut network, 0, 0); + assert!(network.to_disconnect.contains(&0)); + } + + #[test] + fn disconnects_from_peer_on_wrong_response_type() { + let (_x, on_demand) = dummy(false); + let queue = RwLock::new(VecDeque::new()); + let mut network = TestIo::new(&queue, None); + on_demand.on_connect(0, Roles::FULL); + + on_demand.remote_call(RemoteCallRequest { + block: Default::default(), + header: dummy_header(), + method: "test".into(), + call_data: vec![], + retry_count: Some(1), + }); + + on_demand.on_remote_read_response(&mut network, 0, message::RemoteReadResponse { + id: 0, + proof: vec![vec![2]], + }); + assert!(network.to_disconnect.contains(&0)); + assert_eq!(on_demand.core.lock().pending_requests.len(), 1); + } + + #[test] + fn receives_remote_failure_after_retry_count_failures() { + use parking_lot::{Condvar, Mutex}; + + let retry_count = 2; + let (_x, on_demand) = dummy(false); + let queue = RwLock::new(VecDeque::new()); + let mut network = TestIo::new(&queue, None); + for i in 0..retry_count+1 { + on_demand.on_connect(i, Roles::FULL); + } + + let sync = Arc::new((Mutex::new(0), Mutex::new(0), Condvar::new())); + let thread_sync = sync.clone(); + + let response = on_demand.remote_call(RemoteCallRequest { + block: Default::default(), + header: dummy_header(), + method: "test".into(), + call_data: vec![], + retry_count: Some(retry_count) + }); + let thread = ::std::thread::spawn(move || { + let &(ref current, ref finished_at, ref finished) = &*thread_sync; + let _ = response.wait().unwrap_err(); + *finished_at.lock() = *current.lock(); + finished.notify_one(); + }); + + let &(ref current, ref finished_at, ref finished) = &*sync; + for i in 0..retry_count+1 { + let mut current = current.lock(); + *current = *current + 1; + receive_call_response(&*on_demand, &mut network, i, i as u64); + } + + let mut finished_at = finished_at.lock(); + assert!(!finished.wait_for(&mut finished_at, ::std::time::Duration::from_millis(1000)).timed_out()); + assert_eq!(*finished_at, retry_count + 1); + + thread.join().unwrap(); + } + + #[test] + fn receives_remote_call_response() { + let (_x, on_demand) = dummy(true); + let queue = RwLock::new(VecDeque::new()); + let mut network = TestIo::new(&queue, None); + on_demand.on_connect(0, Roles::FULL); + + let response = on_demand.remote_call(RemoteCallRequest { + block: Default::default(), + header: dummy_header(), + method: "test".into(), + call_data: vec![], + retry_count: None, + }); + let thread = ::std::thread::spawn(move || { + let result = response.wait().unwrap(); + assert_eq!(result.return_data, vec![42]); + }); + + receive_call_response(&*on_demand, &mut network, 0, 0); + thread.join().unwrap(); + } + + #[test] + fn receives_remote_read_response() { + let (_x, on_demand) = dummy(true); + let queue = RwLock::new(VecDeque::new()); + let mut network = TestIo::new(&queue, None); + on_demand.on_connect(0, Roles::FULL); + + let response = on_demand.remote_read(RemoteReadRequest { + header: dummy_header(), + block: Default::default(), + key: b":key".to_vec(), + retry_count: None, + }); + let thread = ::std::thread::spawn(move || { + let result = response.wait().unwrap(); + assert_eq!(result, Some(vec![42])); + }); + + on_demand.on_remote_read_response(&mut network, 0, message::RemoteReadResponse { + id: 0, + proof: vec![vec![2]], + }); + thread.join().unwrap(); + } + + #[test] + fn receives_remote_header_response() { + let (_x, on_demand) = dummy(true); + let queue = RwLock::new(VecDeque::new()); + let mut network = TestIo::new(&queue, None); + on_demand.on_connect(0, Roles::FULL); + + let response = on_demand.remote_header(RemoteHeaderRequest { + cht_root: Default::default(), + block: 1, + retry_count: None, + }); + let thread = ::std::thread::spawn(move || { + let result = response.wait().unwrap(); + assert_eq!(result.hash(), "80729accb7bb10ff9c637a10e8bb59f21c52571aa7b46544c5885ca89ed190f4".into()); + }); + + on_demand.on_remote_header_response(&mut network, 0, message::RemoteHeaderResponse { + id: 0, + header: Some(Header { + parent_hash: Default::default(), + number: 1, + state_root: Default::default(), + extrinsics_root: Default::default(), + digest: Default::default(), + }), + proof: vec![vec![2]], + }); + thread.join().unwrap(); + } +} diff --git a/core/network/src/protocol.rs b/core/network/src/protocol.rs new file mode 100644 index 0000000000000..8866a167a2855 --- /dev/null +++ b/core/network/src/protocol.rs @@ -0,0 +1,679 @@ +// Copyright 2017 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::collections::{HashMap, HashSet}; +use std::{mem, cmp}; +use std::sync::Arc; +use std::time; +use parking_lot::RwLock; +use rustc_hex::ToHex; +use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, Hash, HashFor, NumberFor, As}; +use runtime_primitives::generic::BlockId; +use network_libp2p::{NodeIndex, Severity}; +use codec::{Encode, Decode}; + +use message::{self, Message}; +use message::generic::Message as GenericMessage; +use specialization::Specialization; +use sync::{ChainSync, Status as SyncStatus, SyncState}; +use service::{Roles, TransactionPool, ExHashT}; +use import_queue::ImportQueue; +use config::ProtocolConfig; +use chain::Client; +use on_demand::OnDemandService; +use io::SyncIo; +use error; + +const REQUEST_TIMEOUT_SEC: u64 = 40; + +/// Current protocol version. +pub (crate) const CURRENT_VERSION: u32 = 1; +/// Current packet count. +pub (crate) const CURRENT_PACKET_COUNT: u8 = 1; + +// Maximum allowed entries in `BlockResponse` +const MAX_BLOCK_DATA_RESPONSE: u32 = 128; + +// Lock must always be taken in order declared here. +pub struct Protocol, H: ExHashT> { + config: ProtocolConfig, + on_demand: Option>>, + genesis_hash: B::Hash, + sync: Arc>>, + specialization: RwLock, + context_data: ContextData, + // Connected peers pending Status message. + handshaking_peers: RwLock>, + transaction_pool: Arc>, +} +/// Syncing status and statistics +#[derive(Clone)] +pub struct ProtocolStatus { + /// Sync status. + pub sync: SyncStatus, + /// Total number of connected peers + pub num_peers: usize, + /// Total number of active peers. + pub num_active_peers: usize, +} + +/// Peer information +struct Peer { + /// Protocol version + protocol_version: u32, + /// Roles + roles: Roles, + /// Peer best block hash + best_hash: B::Hash, + /// Peer best block number + best_number: ::Number, + /// Pending block request if any + block_request: Option>, + /// Request timestamp + request_timestamp: Option, + /// Holds a set of transactions known to this peer. + known_extrinsics: HashSet, + /// Holds a set of blocks known to this peer. + known_blocks: HashSet, + /// Request counter, + next_request_id: message::RequestId, +} + +/// Info about a peer's known state. +#[derive(Debug)] +pub struct PeerInfo { + /// Roles + pub roles: Roles, + /// Protocol version + pub protocol_version: u32, + /// Peer best block hash + pub best_hash: B::Hash, + /// Peer best block number + pub best_number: ::Number, +} + +/// Context for a network-specific handler. +pub trait Context { + /// Get a reference to the client. + fn client(&self) -> &::chain::Client; + + /// Point out that a peer has been malign or irresponsible or appeared lazy. + fn report_peer(&mut self, who: NodeIndex, reason: Severity); + + /// Get peer info. + fn peer_info(&self, peer: NodeIndex) -> Option>; + + /// Send a message to a peer. + fn send_message(&mut self, who: NodeIndex, data: ::message::Message); +} + +/// Protocol context. +pub(crate) struct ProtocolContext<'a, B: 'a + BlockT, H: 'a + ExHashT> { + io: &'a mut SyncIo, + context_data: &'a ContextData, +} + +impl<'a, B: BlockT + 'a, H: 'a + ExHashT> ProtocolContext<'a, B, H> { + pub(crate) fn new(context_data: &'a ContextData, io: &'a mut SyncIo) -> Self { + ProtocolContext { + io, + context_data, + } + } + + /// Send a message to a peer. + pub fn send_message(&mut self, who: NodeIndex, message: Message) { + send_message(&self.context_data.peers, self.io, who, message) + } + + /// Point out that a peer has been malign or irresponsible or appeared lazy. + pub fn report_peer(&mut self, who: NodeIndex, reason: Severity) { + self.io.report_peer(who, reason); + } + + /// Get peer info. + pub fn peer_info(&self, peer: NodeIndex) -> Option> { + self.context_data.peers.read().get(&peer).map(|p| { + PeerInfo { + roles: p.roles, + protocol_version: p.protocol_version, + best_hash: p.best_hash, + best_number: p.best_number, + } + }) + } +} + +impl<'a, B: BlockT + 'a, H: ExHashT + 'a> Context for ProtocolContext<'a, B, H> { + fn send_message(&mut self, who: NodeIndex, message: Message) { + ProtocolContext::send_message(self, who, message); + } + + fn report_peer(&mut self, who: NodeIndex, reason: Severity) { + ProtocolContext::report_peer(self, who, reason); + } + + fn peer_info(&self, who: NodeIndex) -> Option> { + ProtocolContext::peer_info(self, who) + } + + fn client(&self) -> &Client { + &*self.context_data.chain + } +} + +/// Data necessary to create a context. +pub(crate) struct ContextData { + // All connected peers + peers: RwLock>>, + chain: Arc>, +} + +impl, H: ExHashT> Protocol { + /// Create a new instance. + pub fn new( + config: ProtocolConfig, + chain: Arc>, + import_queue: Arc>, + on_demand: Option>>, + transaction_pool: Arc>, + specialization: S, + ) -> error::Result { + let info = chain.info()?; + let sync = ChainSync::new(config.roles, &info, import_queue); + let protocol = Protocol { + config: config, + context_data: ContextData { + peers: RwLock::new(HashMap::new()), + chain, + }, + on_demand, + genesis_hash: info.chain.genesis_hash, + sync: Arc::new(RwLock::new(sync)), + specialization: RwLock::new(specialization), + handshaking_peers: RwLock::new(HashMap::new()), + transaction_pool: transaction_pool, + }; + Ok(protocol) + } + + pub(crate) fn context_data(&self) -> &ContextData { + &self.context_data + } + + pub(crate) fn sync(&self) -> &Arc>> { + &self.sync + } + + /// Returns protocol status + pub fn status(&self) -> ProtocolStatus { + let sync = self.sync.read(); + let peers = self.context_data.peers.read(); + ProtocolStatus { + sync: sync.status(), + num_peers: peers.values().count(), + num_active_peers: peers.values().filter(|p| p.block_request.is_some()).count(), + } + } + + pub fn handle_packet(&self, io: &mut SyncIo, who: NodeIndex, mut data: &[u8]) { + let message: Message = match Decode::decode(&mut data) { + Some(m) => m, + None => { + trace!(target: "sync", "Invalid packet from {}", who); + io.report_peer(who, Severity::Bad("Peer sent us a packet with invalid format")); + return; + } + }; + + match message { + GenericMessage::Status(s) => self.on_status_message(io, who, s), + GenericMessage::BlockRequest(r) => self.on_block_request(io, who, r), + GenericMessage::BlockResponse(r) => { + let request = { + let mut peers = self.context_data.peers.write(); + if let Some(ref mut peer) = peers.get_mut(&who) { + peer.request_timestamp = None; + match mem::replace(&mut peer.block_request, None) { + Some(r) => r, + None => { + io.report_peer(who, Severity::Bad("Unexpected response packet received from peer")); + return; + } + } + } else { + io.report_peer(who, Severity::Bad("Unexpected packet received from peer")); + return; + } + }; + if request.id != r.id { + trace!(target: "sync", "Ignoring mismatched response packet from {} (expected {} got {})", who, request.id, r.id); + return; + } + self.on_block_response(io, who, request, r); + }, + GenericMessage::BlockAnnounce(announce) => self.on_block_announce(io, who, announce), + GenericMessage::Transactions(m) => self.on_extrinsics(io, who, m), + GenericMessage::RemoteCallRequest(request) => self.on_remote_call_request(io, who, request), + GenericMessage::RemoteCallResponse(response) => self.on_remote_call_response(io, who, response), + GenericMessage::RemoteReadRequest(request) => self.on_remote_read_request(io, who, request), + GenericMessage::RemoteReadResponse(response) => self.on_remote_read_response(io, who, response), + GenericMessage::RemoteHeaderRequest(request) => self.on_remote_header_request(io, who, request), + GenericMessage::RemoteHeaderResponse(response) => self.on_remote_header_response(io, who, response), + other => self.specialization.write().on_message(&mut ProtocolContext::new(&self.context_data, io), who, other), + } + } + + pub fn send_message(&self, io: &mut SyncIo, who: NodeIndex, message: Message) { + send_message::(&self.context_data.peers, io, who, message) + } + + /// Called when a new peer is connected + pub fn on_peer_connected(&self, io: &mut SyncIo, who: NodeIndex) { + trace!(target: "sync", "Connected {}: {}", who, io.peer_info(who)); + self.handshaking_peers.write().insert(who, time::Instant::now()); + self.send_status(io, who); + } + + /// Called by peer when it is disconnecting + pub fn on_peer_disconnected(&self, io: &mut SyncIo, peer: NodeIndex) { + trace!(target: "sync", "Disconnecting {}: {}", peer, io.peer_info(peer)); + + // lock all the the peer lists so that add/remove peer events are in order + let mut sync = self.sync.write(); + let mut spec = self.specialization.write(); + + let removed = { + let mut peers = self.context_data.peers.write(); + let mut handshaking_peers = self.handshaking_peers.write(); + handshaking_peers.remove(&peer); + peers.remove(&peer).is_some() + }; + if removed { + let mut context = ProtocolContext::new(&self.context_data, io); + sync.peer_disconnected(&mut context, peer); + spec.on_disconnect(&mut context, peer); + self.on_demand.as_ref().map(|s| s.on_disconnect(peer)); + } + } + + fn on_block_request(&self, io: &mut SyncIo, peer: NodeIndex, request: message::BlockRequest) { + trace!(target: "sync", "BlockRequest {} from {}: from {:?} to {:?} max {:?}", request.id, peer, request.from, request.to, request.max); + let mut blocks = Vec::new(); + let mut id = match request.from { + message::FromBlock::Hash(h) => BlockId::Hash(h), + message::FromBlock::Number(n) => BlockId::Number(n), + }; + let max = cmp::min(request.max.unwrap_or(u32::max_value()), MAX_BLOCK_DATA_RESPONSE) as usize; + // TODO: receipts, etc. + let get_header = request.fields.contains(message::BlockAttributes::HEADER); + let get_body = request.fields.contains(message::BlockAttributes::BODY); + let get_justification = request.fields.contains(message::BlockAttributes::JUSTIFICATION); + while let Some(header) = self.context_data.chain.header(&id).unwrap_or(None) { + if blocks.len() >= max { + break; + } + let number = header.number().clone(); + let hash = header.hash(); + let justification = if get_justification { self.context_data.chain.justification(&BlockId::Hash(hash)).unwrap_or(None) } else { None }; + let block_data = message::generic::BlockData { + hash: hash, + header: if get_header { Some(header) } else { None }, + body: if get_body { self.context_data.chain.body(&BlockId::Hash(hash)).unwrap_or(None) } else { None }, + receipt: None, + message_queue: None, + justification, + }; + blocks.push(block_data); + match request.direction { + message::Direction::Ascending => id = BlockId::Number(number + As::sa(1)), + message::Direction::Descending => { + if number == As::sa(0) { + break; + } + id = BlockId::Number(number - As::sa(1)) + } + } + } + let response = message::generic::BlockResponse { + id: request.id, + blocks: blocks, + }; + trace!(target: "sync", "Sending BlockResponse with {} blocks", response.blocks.len()); + self.send_message(io, peer, GenericMessage::BlockResponse(response)) + } + + fn on_block_response(&self, io: &mut SyncIo, peer: NodeIndex, request: message::BlockRequest, response: message::BlockResponse) { + // TODO: validate response + let blocks_range = match ( + response.blocks.first().and_then(|b| b.header.as_ref().map(|h| h.number())), + response.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", "BlockResponse {} from {} with {} blocks{}", + response.id, peer, response.blocks.len(), blocks_range); + + self.sync.write().on_block_data(&mut ProtocolContext::new(&self.context_data, io), peer, request, response); + } + + /// Perform time based maintenance. + pub fn tick(&self, io: &mut SyncIo) { + self.maintain_peers(io); + self.on_demand.as_ref().map(|s| s.maintain_peers(io)); + } + + fn maintain_peers(&self, io: &mut SyncIo) { + let tick = time::Instant::now(); + let mut aborting = Vec::new(); + { + let peers = self.context_data.peers.read(); + let handshaking_peers = self.handshaking_peers.read(); + for (who, timestamp) in peers.iter() + .filter_map(|(id, peer)| peer.request_timestamp.as_ref().map(|r| (id, r))) + .chain(handshaking_peers.iter()) { + if (tick - *timestamp).as_secs() > REQUEST_TIMEOUT_SEC { + trace!(target: "sync", "Timeout {}", who); + aborting.push(*who); + } + } + } + + self.specialization.write().maintain_peers(&mut ProtocolContext::new(&self.context_data, io)); + for p in aborting { + io.report_peer(p, Severity::Timeout); + } + } + + pub fn peer_info(&self, peer: NodeIndex) -> Option> { + self.context_data.peers.read().get(&peer).map(|p| { + PeerInfo { + roles: p.roles, + protocol_version: p.protocol_version, + best_hash: p.best_hash, + best_number: p.best_number, + } + }) + } + + /// Called by peer to report status + fn on_status_message(&self, io: &mut SyncIo, who: NodeIndex, status: message::Status) { + trace!(target: "sync", "New peer {} {:?}", who, status); + if io.is_expired() { + trace!(target: "sync", "Status packet from expired session {}:{}", who, io.peer_info(who)); + return; + } + + { + let mut peers = self.context_data.peers.write(); + let mut handshaking_peers = self.handshaking_peers.write(); + if peers.contains_key(&who) { + debug!(target: "sync", "Unexpected status packet from {}:{}", who, io.peer_info(who)); + return; + } + if status.genesis_hash != self.genesis_hash { + io.report_peer(who, Severity::Bad(&format!("Peer is on different chain (our genesis: {} theirs: {})", self.genesis_hash, status.genesis_hash))); + return; + } + if status.version != CURRENT_VERSION { + io.report_peer(who, Severity::Bad(&format!("Peer using unsupported protocol version {}", status.version))); + return; + } + + let peer = Peer { + protocol_version: status.version, + roles: status.roles, + best_hash: status.best_hash, + best_number: status.best_number, + block_request: None, + request_timestamp: None, + known_extrinsics: HashSet::new(), + known_blocks: HashSet::new(), + next_request_id: 0, + }; + peers.insert(who.clone(), peer); + handshaking_peers.remove(&who); + debug!(target: "sync", "Connected {} {}", who, io.peer_info(who)); + } + + let mut context = ProtocolContext::new(&self.context_data, io); + self.sync.write().new_peer(&mut context, who); + self.specialization.write().on_connect(&mut context, who, status.clone()); + self.on_demand.as_ref().map(|s| s.on_connect(who, status.roles)); + } + + /// Called when peer sends us new extrinsics + fn on_extrinsics(&self, _io: &mut SyncIo, who: NodeIndex, extrinsics: message::Transactions) { + // Accept extrinsics only when fully synced + if self.sync.read().status().state != SyncState::Idle { + trace!(target: "sync", "{} Ignoring extrinsics while syncing", who); + return; + } + trace!(target: "sync", "Received {} extrinsics from {}", extrinsics.len(), who); + let mut peers = self.context_data.peers.write(); + if let Some(ref mut peer) = peers.get_mut(&who) { + for t in extrinsics { + if let Some(hash) = self.transaction_pool.import(&t) { + peer.known_extrinsics.insert(hash); + } + } + } + } + + /// Called when we propagate ready extrinsics to peers. + pub fn propagate_extrinsics(&self, io: &mut SyncIo) { + debug!(target: "sync", "Propagating extrinsics"); + + // Accept transactions only when fully synced + if self.sync.read().status().state != SyncState::Idle { + return; + } + + let extrinsics = self.transaction_pool.transactions(); + + let mut propagated_to = HashMap::new(); + let mut peers = self.context_data.peers.write(); + for (who, ref mut peer) in peers.iter_mut() { + let (hashes, to_send): (Vec<_>, Vec<_>) = extrinsics + .iter() + .cloned() + .filter(|&(ref hash, _)| peer.known_extrinsics.insert(hash.clone())) + .unzip(); + + if !to_send.is_empty() { + let node_id = io.peer_session_info(*who).map(|info| match info.id { + Some(id) => format!("{}@{:x}", info.remote_address, id), + None => info.remote_address.clone(), + }); + + if let Some(id) = node_id { + for hash in hashes { + propagated_to.entry(hash).or_insert_with(Vec::new).push(id.clone()); + } + } + trace!(target: "sync", "Sending {} transactions to {}", to_send.len(), who); + self.send_message(io, *who, GenericMessage::Transactions(to_send)); + } + } + self.transaction_pool.on_broadcasted(propagated_to); + } + + /// Send Status message + fn send_status(&self, io: &mut SyncIo, who: NodeIndex) { + if let Ok(info) = self.context_data.chain.info() { + let status = message::generic::Status { + version: CURRENT_VERSION, + genesis_hash: info.chain.genesis_hash, + roles: self.config.roles.into(), + best_number: info.chain.best_number, + best_hash: info.chain.best_hash, + chain_status: self.specialization.read().status(), + }; + self.send_message(io, who, GenericMessage::Status(status)) + } + } + + pub fn abort(&self) { + let mut sync = self.sync.write(); + let mut spec = self.specialization.write(); + let mut peers = self.context_data.peers.write(); + let mut handshaking_peers = self.handshaking_peers.write(); + sync.clear(); + spec.on_abort(); + peers.clear(); + handshaking_peers.clear(); + } + + pub fn stop(&self) { + // stop processing import requests first (without holding a sync lock) + let import_queue = self.sync.read().import_queue(); + import_queue.stop(); + + // and then clear all the sync data + self.abort(); + } + + pub fn on_block_announce(&self, io: &mut SyncIo, who: NodeIndex, announce: message::BlockAnnounce) { + let header = announce.header; + let hash = header.hash(); + { + let mut peers = self.context_data.peers.write(); + if let Some(ref mut peer) = peers.get_mut(&who) { + peer.known_blocks.insert(hash.clone()); + } + } + self.sync.write().on_block_announce(&mut ProtocolContext::new(&self.context_data, io), who, hash, &header); + } + + pub fn on_block_imported(&self, io: &mut SyncIo, hash: B::Hash, header: &B::Header) { + self.sync.write().update_chain_info(&header); + self.specialization.write().on_block_imported( + &mut ProtocolContext::new(&self.context_data, io), + hash.clone(), + header + ); + + // blocks are not announced by light clients + if self.config.roles & Roles::LIGHT == Roles::LIGHT { + return; + } + + // send out block announcements + let mut peers = self.context_data.peers.write(); + + for (who, ref mut peer) in peers.iter_mut() { + if peer.known_blocks.insert(hash.clone()) { + trace!(target: "sync", "Announcing block {:?} to {}", hash, who); + self.send_message(io, *who, GenericMessage::BlockAnnounce(message::BlockAnnounce { + header: header.clone() + })); + } + } + } + + fn on_remote_call_request(&self, io: &mut SyncIo, who: NodeIndex, request: message::RemoteCallRequest) { + trace!(target: "sync", "Remote call request {} from {} ({} at {})", request.id, who, request.method, request.block); + let proof = match self.context_data.chain.execution_proof(&request.block, &request.method, &request.data) { + Ok((_, proof)) => proof, + Err(error) => { + trace!(target: "sync", "Remote call request {} from {} ({} at {}) failed with: {}", + request.id, who, request.method, request.block, error); + Default::default() + }, + }; + + self.send_message(io, who, GenericMessage::RemoteCallResponse(message::RemoteCallResponse { + id: request.id, proof, + })); + } + + fn on_remote_call_response(&self, io: &mut SyncIo, who: NodeIndex, response: message::RemoteCallResponse) { + trace!(target: "sync", "Remote call response {} from {}", response.id, who); + self.on_demand.as_ref().map(|s| s.on_remote_call_response(io, who, response)); + } + + fn on_remote_read_request(&self, io: &mut SyncIo, who: NodeIndex, request: message::RemoteReadRequest) { + trace!(target: "sync", "Remote read request {} from {} ({} at {})", + request.id, who, request.key.to_hex(), request.block); + let proof = match self.context_data.chain.read_proof(&request.block, &request.key) { + Ok(proof) => proof, + Err(error) => { + trace!(target: "sync", "Remote read request {} from {} ({} at {}) failed with: {}", + request.id, who, request.key.to_hex(), request.block, error); + Default::default() + }, + }; + self.send_message(io, who, GenericMessage::RemoteReadResponse(message::RemoteReadResponse { + id: request.id, proof, + })); + } + fn on_remote_read_response(&self, io: &mut SyncIo, who: NodeIndex, response: message::RemoteReadResponse) { + trace!(target: "sync", "Remote read response {} from {}", response.id, who); + self.on_demand.as_ref().map(|s| s.on_remote_read_response(io, who, response)); + } + + fn on_remote_header_request(&self, io: &mut SyncIo, who: NodeIndex, request: message::RemoteHeaderRequest>) { + trace!(target: "sync", "Remote header proof request {} from {} ({})", + request.id, who, request.block); + let (header, proof) = match self.context_data.chain.header_proof(request.block) { + Ok((header, proof)) => (Some(header), proof), + Err(error) => { + trace!(target: "sync", "Remote header proof request {} from {} ({}) failed with: {}", + request.id, who, request.block, error); + (Default::default(), Default::default()) + }, + }; + self.send_message(io, who, GenericMessage::RemoteHeaderResponse(message::RemoteHeaderResponse { + id: request.id, header, proof, + })); + } + + fn on_remote_header_response(&self, io: &mut SyncIo, who: NodeIndex, response: message::RemoteHeaderResponse) { + trace!(target: "sync", "Remote header proof response {} from {}", response.id, who); + self.on_demand.as_ref().map(|s| s.on_remote_header_response(io, who, response)); + } + + /// Execute a closure with access to a network context and specialization. + pub fn with_spec(&self, io: &mut SyncIo, f: F) -> U + where F: FnOnce(&mut S, &mut Context) -> U + { + f(&mut* self.specialization.write(), &mut ProtocolContext::new(&self.context_data, io)) + } +} + +fn send_message(peers: &RwLock>>, io: &mut SyncIo, who: NodeIndex, mut message: Message) { + match &mut message { + &mut GenericMessage::BlockRequest(ref mut r) => { + let mut peers = peers.write(); + if let Some(ref mut peer) = peers.get_mut(&who) { + r.id = peer.next_request_id; + peer.next_request_id = peer.next_request_id + 1; + peer.block_request = Some(r.clone()); + peer.request_timestamp = Some(time::Instant::now()); + } + }, + _ => (), + } + io.send(who, message.encode()); +} + +/// Hash a message. +pub(crate) fn hash_message(message: &Message) -> B::Hash { + let data = message.encode(); + HashFor::::hash(&data) +} diff --git a/core/network/src/service.rs b/core/network/src/service.rs new file mode 100644 index 0000000000000..2260b86fb6d49 --- /dev/null +++ b/core/network/src/service.rs @@ -0,0 +1,340 @@ +// Copyright 2017 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::collections::HashMap; +use std::sync::Arc; +use std::io; +use std::time::Duration; +use futures::sync::{oneshot, mpsc}; +use network_libp2p::{NetworkProtocolHandler, NetworkContext, NodeIndex, ProtocolId, +NetworkConfiguration , NonReservedPeerMode, ErrorKind}; +use network_libp2p::{NetworkService}; +use core_io::{TimerToken}; +use io::NetSyncIo; +use protocol::{Protocol, ProtocolContext, Context, ProtocolStatus, PeerInfo as ProtocolPeerInfo}; +use config::{ProtocolConfig}; +use error::Error; +use chain::Client; +use message::LocalizedBftMessage; +use specialization::Specialization; +use on_demand::OnDemandService; +use import_queue::AsyncImportQueue; +use runtime_primitives::traits::{Block as BlockT}; + +/// Type that represents fetch completion future. +pub type FetchFuture = oneshot::Receiver>; +/// Type that represents bft messages stream. +pub type BftMessageStream = mpsc::UnboundedReceiver>; + +const TICK_TOKEN: TimerToken = 0; +const TICK_TIMEOUT: Duration = Duration::from_millis(1000); + +const PROPAGATE_TOKEN: TimerToken = 1; +const PROPAGATE_TIMEOUT: Duration = Duration::from_millis(5000); + +bitflags! { + /// Node roles bitmask. + pub struct Roles: u8 { + /// No network. + const NONE = 0b00000000; + /// Full node, does not participate in consensus. + const FULL = 0b00000001; + /// Light client node. + const LIGHT = 0b00000010; + /// Act as an authority + const AUTHORITY = 0b00000100; + } +} + +impl ::codec::Encode for Roles { + fn encode_to(&self, dest: &mut T) { + dest.push_byte(self.bits()) + } +} + +impl ::codec::Decode for Roles { + fn decode(input: &mut I) -> Option { + Self::from_bits(input.read_byte()?) + } +} + +/// Sync status +pub trait SyncProvider: Send + Sync { + /// Get sync status + fn status(&self) -> ProtocolStatus; + /// Get peers information + fn peers(&self) -> Vec>; + /// Get this node id if available. + fn node_id(&self) -> Option; +} + +pub trait ExHashT: ::std::hash::Hash + Eq + ::std::fmt::Debug + Clone + Send + Sync + 'static {} +impl ExHashT for T where T: ::std::hash::Hash + Eq + ::std::fmt::Debug + Clone + Send + Sync + 'static {} + +/// Transaction pool interface +pub trait TransactionPool: Send + Sync { + /// Get transactions from the pool that are ready to be propagated. + fn transactions(&self) -> Vec<(H, B::Extrinsic)>; + /// Import a transaction into the pool. + fn import(&self, transaction: &B::Extrinsic) -> Option; + /// Notify the pool about transactions broadcast. + fn on_broadcasted(&self, propagations: HashMap>); +} + +/// ConsensusService +pub trait ConsensusService: Send + Sync { + /// Maintain connectivity to given addresses. + fn connect_to_authorities(&self, addresses: &[String]); + + /// Get BFT message stream for messages corresponding to consensus on given + /// parent hash. + fn bft_messages(&self, parent_hash: B::Hash) -> BftMessageStream; + /// Send out a BFT message. + fn send_bft_message(&self, message: LocalizedBftMessage); +} + +/// Service able to execute closure in the network context. +pub trait ExecuteInContext: Send + Sync { + /// Execute closure in network context. + fn execute_in_context)>(&self, closure: F); +} + +/// Network protocol handler +struct ProtocolHandler, H: ExHashT> { + protocol: Protocol, +} + +/// Peer connection information +#[derive(Debug)] +pub struct PeerInfo { + /// Public node id + pub id: Option, + /// Node client ID + pub client_version: String, + /// Capabilities + pub capabilities: Vec, + /// Remote endpoint address + pub remote_address: String, + /// Local endpoint address + pub local_address: String, + /// Dot protocol info. + pub dot_info: Option>, +} + +/// Service initialization parameters. +pub struct Params { + /// Configuration. + pub config: ProtocolConfig, + /// Network layer configuration. + pub network_config: NetworkConfiguration, + /// Polkadot relay chain access point. + pub chain: Arc>, + /// On-demand service reference. + pub on_demand: Option>>, + /// Transaction pool. + pub transaction_pool: Arc>, + /// Protocol specialization. + pub specialization: S, +} + +/// Polkadot network service. Handles network IO and manages connectivity. +pub struct Service, H: ExHashT> { + /// Network service + network: NetworkService, + /// Devp2p protocol handler + handler: Arc>, + /// Devp2p protocol ID. + protocol_id: ProtocolId, +} + +impl, H: ExHashT> Service { + /// Creates and register protocol with the network service + pub fn new(params: Params, protocol_id: ProtocolId) -> Result>, Error> { + let chain = params.chain.clone(); + let import_queue = Arc::new(AsyncImportQueue::new()); + let handler = Arc::new(ProtocolHandler { + protocol: Protocol::new( + params.config, + params.chain, + import_queue.clone(), + params.on_demand, + params.transaction_pool, + params.specialization, + )?, + }); + let versions = [(::protocol::CURRENT_VERSION as u8, ::protocol::CURRENT_PACKET_COUNT)]; + let protocols = vec![(handler.clone() as Arc<_>, protocol_id, &versions[..])]; + let service = match NetworkService::new(params.network_config.clone(), protocols) { + Ok(service) => service, + Err(err) => { + match err.kind() { + ErrorKind::Io(ref e) if e.kind() == io::ErrorKind::AddrInUse => + warn!("Network port is already in use, make sure that another instance of Polkadot client is not running or change the port using the --port option."), + _ => warn!("Error starting network: {}", err), + }; + return Err(err.into()) + }, + }; + let sync = Arc::new(Service { + network: service, + protocol_id, + handler, + }); + + import_queue.start( + Arc::downgrade(sync.handler.protocol.sync()), + Arc::downgrade(&sync), + Arc::downgrade(&chain) + )?; + + Ok(sync) + } + + /// Called when a new block is imported by the client. + pub fn on_block_imported(&self, hash: B::Hash, header: &B::Header) { + self.network.with_context(self.protocol_id, |context| { + self.handler.protocol.on_block_imported(&mut NetSyncIo::new(context), hash, header) + }); + } + + /// Called when new transactons are imported by the client. + pub fn trigger_repropagate(&self) { + self.network.with_context(self.protocol_id, |context| { + self.handler.protocol.propagate_extrinsics(&mut NetSyncIo::new(context)); + }); + } + + /// Execute a closure with the chain-specific network specialization. + /// If the network is unavailable, this will return `None`. + pub fn with_spec(&self, f: F) -> Option + where F: FnOnce(&mut S, &mut Context) -> U + { + let mut res = None; + self.network.with_context(self.protocol_id, |context| { + res = Some(self.handler.protocol.with_spec(&mut NetSyncIo::new(context), f)) + }); + + res + } +} + +impl, H:ExHashT> Drop for Service { + fn drop(&mut self) { + self.handler.protocol.stop(); + } +} +impl, H: ExHashT> ExecuteInContext for Service { + fn execute_in_context)>(&self, closure: F) { + self.network.with_context(self.protocol_id, |context| { + closure(&mut ProtocolContext::new(self.handler.protocol.context_data(), &mut NetSyncIo::new(context))) + }); + } +} + +impl, H: ExHashT> SyncProvider for Service { + /// Get sync status + fn status(&self) -> ProtocolStatus { + self.handler.protocol.status() + } + + /// Get sync peers + fn peers(&self) -> Vec> { + self.network.with_context_eval(self.protocol_id, |ctx| { + let peer_ids = self.network.connected_peers(); + + peer_ids.into_iter().filter_map(|who| { + let session_info = match ctx.session_info(who) { + None => return None, + Some(info) => info, + }; + + Some(PeerInfo { + id: session_info.id.map(|id| format!("{:x}", id)), + client_version: session_info.client_version, + capabilities: session_info.peer_capabilities.into_iter().map(|c| c.to_string()).collect(), + remote_address: session_info.remote_address, + local_address: session_info.local_address, + dot_info: self.handler.protocol.peer_info(who), + }) + }).collect() + }).unwrap_or_else(Vec::new) + } + + fn node_id(&self) -> Option { + self.network.external_url() + } +} + +impl, H: ExHashT> NetworkProtocolHandler for ProtocolHandler { + fn initialize(&self, io: &NetworkContext) { + io.register_timer(TICK_TOKEN, TICK_TIMEOUT) + .expect("Error registering sync timer"); + + io.register_timer(PROPAGATE_TOKEN, PROPAGATE_TIMEOUT) + .expect("Error registering transaction propagation timer"); + } + + fn read(&self, io: &NetworkContext, peer: &NodeIndex, _packet_id: u8, data: &[u8]) { + self.protocol.handle_packet(&mut NetSyncIo::new(io), *peer, data); + } + + fn connected(&self, io: &NetworkContext, peer: &NodeIndex) { + self.protocol.on_peer_connected(&mut NetSyncIo::new(io), *peer); + } + + fn disconnected(&self, io: &NetworkContext, peer: &NodeIndex) { + self.protocol.on_peer_disconnected(&mut NetSyncIo::new(io), *peer); + } + + fn timeout(&self, io: &NetworkContext, timer: TimerToken) { + match timer { + TICK_TOKEN => self.protocol.tick(&mut NetSyncIo::new(io)), + PROPAGATE_TOKEN => self.protocol.propagate_extrinsics(&mut NetSyncIo::new(io)), + _ => {} + } + } +} + +/// Trait for managing network +pub trait ManageNetwork: Send + Sync { + /// 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: String) -> Result<(), String>; + /// Add reserved peer + fn add_reserved_peer(&self, peer: String) -> Result<(), String>; +} + + +impl, H: ExHashT> ManageNetwork for Service { + fn accept_unreserved_peers(&self) { + self.network.set_non_reserved_mode(NonReservedPeerMode::Accept); + } + + fn deny_unreserved_peers(&self) { + self.network.set_non_reserved_mode(NonReservedPeerMode::Deny); + } + + fn remove_reserved_peer(&self, peer: String) -> Result<(), String> { + self.network.remove_reserved_peer(&peer).map_err(|e| format!("{:?}", e)) + } + + fn add_reserved_peer(&self, peer: String) -> Result<(), String> { + self.network.add_reserved_peer(&peer).map_err(|e| format!("{:?}", e)) + } +} diff --git a/core/network/src/specialization.rs b/core/network/src/specialization.rs new file mode 100644 index 0000000000000..3c04a367059cd --- /dev/null +++ b/core/network/src/specialization.rs @@ -0,0 +1,48 @@ +// Copyright 2017 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 . + +//! Specializations of the substrate network protocol to allow more complex forms of communication. + +use ::NodeIndex; +use runtime_primitives::traits::Block as BlockT; +use protocol::Context; + +/// A specialization of the substrate network protocol. Handles events and sends messages. +pub trait Specialization: Send + Sync + 'static { + /// Get the current specialization-status. + fn status(&self) -> Vec; + + /// Called on start-up. + fn on_start(&mut self) { } + + /// Called when a peer successfully handshakes. + fn on_connect(&mut self, ctx: &mut Context, who: NodeIndex, status: ::message::Status); + + /// Called when a peer is disconnected. If the peer ID is unknown, it should be ignored. + fn on_disconnect(&mut self, ctx: &mut Context, who: NodeIndex); + + /// Called when a network-specific message arrives. + fn on_message(&mut self, ctx: &mut Context, who: NodeIndex, message: ::message::Message); + + /// Called on abort. + fn on_abort(&mut self) { } + + /// Called periodically to maintain peers and handle timeouts. + fn maintain_peers(&mut self, _ctx: &mut Context) { } + + /// Called when a block is _imported_ at the head of the chain (not during major sync). + fn on_block_imported(&mut self, _ctx: &mut Context, _hash: B::Hash, _header: &B::Header) { } +} diff --git a/core/network/src/sync.rs b/core/network/src/sync.rs new file mode 100644 index 0000000000000..33c3e57d7842a --- /dev/null +++ b/core/network/src/sync.rs @@ -0,0 +1,428 @@ +// Copyright 2017 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::collections::HashMap; +use std::sync::Arc; +use protocol::Context; +use network_libp2p::{Severity, NodeIndex}; +use client::{BlockStatus, BlockOrigin, ClientInfo}; +use client::error::Error as ClientError; +use blocks::{self, BlockCollection}; +use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, As, NumberFor}; +use runtime_primitives::generic::BlockId; +use message::{self, generic::Message as GenericMessage}; +use service::Roles; +use import_queue::ImportQueue; + +// 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; + +struct PeerSync { + pub common_hash: B::Hash, + pub common_number: NumberFor, + pub best_hash: B::Hash, + pub best_number: NumberFor, + pub state: PeerSyncState, +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +enum PeerSyncState { + AncestorSearch(NumberFor), + Available, + DownloadingNew(NumberFor), + DownloadingStale(B::Hash), +} + +/// Relay chain sync strategy. +pub struct ChainSync { + genesis_hash: B::Hash, + peers: HashMap>, + blocks: BlockCollection, + best_queued_number: NumberFor, + best_queued_hash: B::Hash, + required_block_attributes: message::BlockAttributes, + import_queue: Arc>, +} + +/// Reported sync state. +#[derive(Clone, Eq, PartialEq, Debug)] +pub enum SyncState { + /// Initial sync is complete, keep-up sync is active. + Idle, + /// Actively catching up with the chain. + Downloading +} + +/// Syncing status and statistics +#[derive(Clone)] +pub struct Status { + /// Current global sync state. + pub state: SyncState, + /// Target sync block number. + pub best_seen_block: Option>, +} + +impl ChainSync { + /// Create a new instance. + pub(crate) fn new(role: Roles, info: &ClientInfo, import_queue: Arc>) -> Self { + let mut required_block_attributes = message::BlockAttributes::HEADER | message::BlockAttributes::JUSTIFICATION; + if role.intersects(Roles::FULL | Roles::AUTHORITY) { + required_block_attributes |= message::BlockAttributes::BODY; + } + + ChainSync { + genesis_hash: info.chain.genesis_hash, + peers: HashMap::new(), + blocks: BlockCollection::new(), + best_queued_hash: info.best_queued_hash.unwrap_or(info.chain.best_hash), + best_queued_number: info.best_queued_number.unwrap_or(info.chain.best_number), + required_block_attributes, + import_queue, + } + } + + fn best_seen_block(&self) -> Option> { + self.peers.values().max_by_key(|p| p.best_number).map(|p| p.best_number) + } + + /// Returns import queue reference. + pub(crate) fn import_queue(&self) -> Arc> { + self.import_queue.clone() + } + + /// Returns sync status. + pub(crate) fn status(&self) -> Status { + let best_seen = self.best_seen_block(); + let state = match &best_seen { + &Some(n) if n > self.best_queued_number && n - self.best_queued_number > As::sa(5) => SyncState::Downloading, + _ => SyncState::Idle, + }; + Status { + state: state, + best_seen_block: best_seen, + } + } + + /// Handle new connected peer. + pub(crate) fn new_peer(&mut self, protocol: &mut Context, who: NodeIndex) { + if let Some(info) = protocol.peer_info(who) { + match (block_status(&*protocol.client(), &*self.import_queue, info.best_hash), info.best_number) { + (Err(e), _) => { + debug!(target:"sync", "Error reading blockchain: {:?}", e); + protocol.report_peer(who, Severity::Useless(&format!("Error legimimately reading blockchain status: {:?}", e))); + }, + (Ok(BlockStatus::KnownBad), _) => { + protocol.report_peer(who, Severity::Bad(&format!("New peer with known bad best block {} ({}).", info.best_hash, info.best_number))); + }, + (Ok(BlockStatus::Unknown), b) if b == As::sa(0) => { + protocol.report_peer(who, Severity::Bad(&format!("New peer with unknown genesis hash {} ({}).", info.best_hash, info.best_number))); + }, + (Ok(BlockStatus::Unknown), _) => { + let our_best = self.best_queued_number; + if our_best > As::sa(0) { + 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_hash: self.genesis_hash, + common_number: As::sa(0), + best_hash: info.best_hash, + best_number: info.best_number, + state: PeerSyncState::AncestorSearch(our_best), + }); + Self::request_ancestry(protocol, who, our_best) + } else { + // 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, PeerSync { + common_hash: self.genesis_hash, + common_number: As::sa(0), + best_hash: info.best_hash, + best_number: info.best_number, + state: PeerSyncState::Available, + }); + self.download_new(protocol, who) + } + }, + (Ok(BlockStatus::Queued), _) | (Ok(BlockStatus::InChain), _) => { + debug!(target:"sync", "New peer with known best hash {} ({}).", info.best_hash, info.best_number); + self.peers.insert(who, PeerSync { + common_hash: info.best_hash, + common_number: info.best_number, + best_hash: info.best_hash, + best_number: info.best_number, + state: PeerSyncState::Available, + }); + } + } + } + } + + pub(crate) fn on_block_data(&mut self, protocol: &mut Context, who: NodeIndex, _request: message::BlockRequest, response: message::BlockResponse) { + let new_blocks = if let Some(ref mut peer) = self.peers.get_mut(&who) { + match peer.state { + PeerSyncState::DownloadingNew(start_block) => { + self.blocks.clear_peer_download(who); + peer.state = PeerSyncState::Available; + + self.blocks.insert(start_block, response.blocks, who); + self.blocks.drain(self.best_queued_number + As::sa(1)) + }, + PeerSyncState::DownloadingStale(_) => { + peer.state = PeerSyncState::Available; + response.blocks.into_iter().map(|b| blocks::BlockData { + origin: who, + block: b + }).collect() + }, + PeerSyncState::AncestorSearch(n) => { + match response.blocks.get(0) { + Some(ref block) => { + trace!(target: "sync", "Got ancestry block #{} ({}) from peer {}", n, block.hash, who); + match protocol.client().block_hash(n) { + Ok(Some(block_hash)) if block_hash == block.hash => { + if peer.common_number < n { + peer.common_hash = block.hash; + peer.common_number = n; + } + peer.state = PeerSyncState::Available; + trace!(target:"sync", "Found common ancestor for peer {}: {} ({})", who, block.hash, n); + vec![] + }, + Ok(our_best) if n > As::sa(0) => { + trace!(target:"sync", "Ancestry block mismatch for peer {}: theirs: {} ({}), ours: {:?}", who, block.hash, n, our_best); + let n = n - As::sa(1); + peer.state = PeerSyncState::AncestorSearch(n); + Self::request_ancestry(protocol, who, n); + return; + }, + Ok(_) => { // genesis mismatch + trace!(target:"sync", "Ancestry search: genesis mismatch for peer {}", who); + protocol.report_peer(who, Severity::Bad("Ancestry search: genesis mismatch for peer")); + return; + }, + Err(e) => { + protocol.report_peer(who, Severity::Useless(&format!("Error answering legitimate blockchain query: {:?}", e))); + return; + } + } + }, + None => { + trace!(target:"sync", "Invalid response when searching for ancestor from {}", who); + protocol.report_peer(who, Severity::Bad("Invalid response when searching for ancestor")); + return; + } + } + }, + PeerSyncState::Available => Vec::new(), + } + } else { + vec![] + }; + + let best_seen = self.best_seen_block(); + let is_best = new_blocks.first().and_then(|b| b.block.header.as_ref()).map(|h| best_seen.as_ref().map_or(false, |n| h.number() >= n)); + let origin = if is_best.unwrap_or_default() { BlockOrigin::NetworkBroadcast } else { BlockOrigin::NetworkInitialSync }; + let import_queue = self.import_queue.clone(); + if let Some((hash, number)) = new_blocks.last() + .and_then(|b| b.block.header.as_ref().map(|h|(b.block.hash.clone(), *h.number()))) + { + if number > self.best_queued_number { + self.best_queued_number = number; + self.best_queued_hash = hash; + } + } + import_queue.import_blocks(self, protocol, (origin, new_blocks)); + self.maintain_sync(protocol); + } + + pub fn maintain_sync(&mut self, protocol: &mut Context) { + let peers: Vec = self.peers.keys().map(|p| *p).collect(); + for peer in peers { + self.download_new(protocol, peer); + } + } + + pub fn block_imported(&mut self, hash: &B::Hash, number: NumberFor) { + if number > self.best_queued_number { + self.best_queued_number = number; + self.best_queued_hash = *hash; + } + // Update common blocks + for (_, peer) in self.peers.iter_mut() { + trace!("Updating peer info ours={}, theirs={}", number, peer.best_number); + if peer.best_number >= number { + peer.common_number = number; + peer.common_hash = *hash; + } + } + } + + pub(crate) fn update_chain_info(&mut self, best_header: &B::Header) { + let hash = best_header.hash(); + self.block_imported(&hash, best_header.number().clone()) + } + + pub(crate) fn on_block_announce(&mut self, protocol: &mut Context, who: NodeIndex, hash: B::Hash, header: &B::Header) { + let number = *header.number(); + if let Some(ref mut peer) = self.peers.get_mut(&who) { + if number > peer.best_number { + peer.best_number = number; + peer.best_hash = hash; + } + if number <= self.best_queued_number && number > peer.common_number { + peer.common_number = number + } + } else { + return; + } + + if !self.is_known_or_already_downloading(protocol, &hash) { + let stale = number <= self.best_queued_number; + if stale { + if !self.is_known_or_already_downloading(protocol, header.parent_hash()) { + trace!(target: "sync", "Ignoring unknown stale block announce from {}: {} {:?}", who, hash, header); + } else { + trace!(target: "sync", "Downloading new stale block announced from {}: {} {:?}", who, hash, header); + self.download_stale(protocol, who, &hash); + } + } else { + trace!(target: "sync", "Downloading new block announced from {}: {} {:?}", who, hash, header); + self.download_new(protocol, who); + } + } else { + trace!(target: "sync", "Known block announce from {}: {}", who, hash); + } + } + + fn is_known_or_already_downloading(&self, protocol: &mut Context, hash: &B::Hash) -> bool { + self.peers.iter().any(|(_, p)| p.state == PeerSyncState::DownloadingStale(*hash)) + || block_status(&*protocol.client(), &*self.import_queue, *hash).ok().map_or(false, |s| s != BlockStatus::Unknown) + } + + pub(crate) fn peer_disconnected(&mut self, protocol: &mut Context, who: NodeIndex) { + self.blocks.clear_peer_download(who); + self.peers.remove(&who); + self.maintain_sync(protocol); + } + + pub(crate) fn restart(&mut self, protocol: &mut Context) { + self.import_queue.clear(); + self.blocks.clear(); + let ids: Vec = self.peers.keys().map(|p| *p).collect(); + for id in ids { + self.new_peer(protocol, id); + } + match protocol.client().info() { + Ok(info) => { + self.best_queued_hash = info.best_queued_hash.unwrap_or(info.chain.best_hash); + self.best_queued_number = info.best_queued_number.unwrap_or(info.chain.best_number); + }, + Err(e) => { + debug!(target:"sync", "Error reading blockchain: {:?}", e); + self.best_queued_hash = self.genesis_hash; + self.best_queued_number = As::sa(0); + } + } + } + + pub(crate) fn clear(&mut self) { + self.blocks.clear(); + self.peers.clear(); + } + + // Download old block. + fn download_stale(&mut self, protocol: &mut Context, who: NodeIndex, hash: &B::Hash) { + if let Some(ref mut peer) = self.peers.get_mut(&who) { + match peer.state { + PeerSyncState::Available => { + let request = 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), + }; + peer.state = PeerSyncState::DownloadingStale(*hash); + protocol.send_message(who, GenericMessage::BlockRequest(request)); + }, + _ => (), + } + } + } + + // Issue a request for a peer to download new blocks, if any are available + fn download_new(&mut self, protocol: &mut Context, who: NodeIndex) { + if let Some(ref mut peer) = self.peers.get_mut(&who) { + let import_status = self.import_queue.status(); + // when there are too many blocks in the queue => do not try to download new blocks + if import_status.importing_count > MAX_IMPORTING_BLOCKS { + return; + } + // we should not download already queued blocks + let common_number = ::std::cmp::max(peer.common_number, import_status.best_importing_number); + + trace!(target: "sync", "Considering new block download from {}, common block is {}, best is {:?}", who, common_number, peer.best_number); + match peer.state { + PeerSyncState::Available => { + if let Some(range) = self.blocks.needed_blocks(who, MAX_BLOCKS_TO_REQUEST, peer.best_number, common_number) { + trace!(target: "sync", "Requesting blocks from {}, ({} to {})", who, range.start, range.end); + let request = message::generic::BlockRequest { + id: 0, + fields: self.required_block_attributes.clone(), + from: message::FromBlock::Number(range.start), + to: None, + direction: message::Direction::Ascending, + max: Some((range.end - range.start).as_() as u32), + }; + peer.state = PeerSyncState::DownloadingNew(range.start); + protocol.send_message(who, GenericMessage::BlockRequest(request)); + } else { + trace!(target: "sync", "Nothing to request"); + } + }, + _ => (), + } + } + } + + fn request_ancestry(protocol: &mut Context, who: NodeIndex, block: NumberFor) { + trace!(target: "sync", "Requesting ancestry block #{} from {}", block, who); + let request = message::generic::BlockRequest { + id: 0, + fields: message::BlockAttributes::HEADER | message::BlockAttributes::JUSTIFICATION, + from: message::FromBlock::Number(block), + to: None, + direction: message::Direction::Ascending, + max: Some(1), + }; + protocol.send_message(who, GenericMessage::BlockRequest(request)); + } +} + +/// Get block status, taking into account import queue. +fn block_status( + chain: &::chain::Client, + queue: &ImportQueue, + hash: B::Hash) -> Result +{ + if queue.is_importing(&hash) { + return Ok(BlockStatus::Queued); + } + + chain.block_status(&BlockId::Hash(hash)) +} diff --git a/core/network/src/test/mod.rs b/core/network/src/test/mod.rs new file mode 100644 index 0000000000000..7a78dd28fc01b --- /dev/null +++ b/core/network/src/test/mod.rs @@ -0,0 +1,330 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +mod sync; + +use std::collections::{VecDeque, HashSet, HashMap}; +use std::sync::Arc; + +use parking_lot::RwLock; +use client; +use client::block_builder::BlockBuilder; +use runtime_primitives::traits::Block as BlockT; +use runtime_primitives::generic::BlockId; +use io::SyncIo; +use protocol::{Context, Protocol}; +use primitives::{Blake2Hasher, RlpCodec}; +use config::ProtocolConfig; +use service::TransactionPool; +use network_libp2p::{NodeIndex, SessionInfo, Severity}; +use keyring::Keyring; +use codec::Encode; +use import_queue::tests::SyncImportQueue; +use test_client::{self, TestClient}; +use test_client::runtime::{Block, Hash, Transfer, Extrinsic}; +use specialization::Specialization; + +pub struct DummySpecialization; + +impl Specialization for DummySpecialization { + fn status(&self) -> Vec { vec![] } + + fn on_connect(&mut self, _ctx: &mut Context, _peer_id: NodeIndex, _status: ::message::Status) { + + } + + fn on_disconnect(&mut self, _ctx: &mut Context, _peer_id: NodeIndex) { + + } + + fn on_message(&mut self, _ctx: &mut Context, _peer_id: NodeIndex, _message: ::message::Message) { + + } +} + +pub struct TestIo<'p> { + queue: &'p RwLock>, + pub to_disconnect: HashSet, + packets: Vec, + peers_info: HashMap, + _sender: Option, +} + +impl<'p> TestIo<'p> where { + pub fn new(queue: &'p RwLock>, sender: Option) -> TestIo<'p> { + TestIo { + queue: queue, + _sender: sender, + to_disconnect: HashSet::new(), + packets: Vec::new(), + peers_info: HashMap::new(), + } + } +} + +impl<'p> Drop for TestIo<'p> { + fn drop(&mut self) { + self.queue.write().extend(self.packets.drain(..)); + } +} + +impl<'p> SyncIo for TestIo<'p> { + fn report_peer(&mut self, who: NodeIndex, _reason: Severity) { + self.to_disconnect.insert(who); + } + + fn is_expired(&self) -> bool { + false + } + + fn send(&mut self, who: NodeIndex, data: Vec) { + self.packets.push(TestPacket { + data: data, + recipient: who, + }); + } + + fn peer_info(&self, who: NodeIndex) -> String { + self.peers_info.get(&who) + .cloned() + .unwrap_or_else(|| who.to_string()) + } + + fn peer_session_info(&self, _peer_id: NodeIndex) -> Option { + None + } +} + +/// Mocked subprotocol packet +pub struct TestPacket { + data: Vec, + recipient: NodeIndex, +} + +pub struct Peer { + client: Arc>, + pub sync: Protocol, + pub queue: RwLock>, +} + +impl Peer { + /// 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().expect("In-mem client does not fail"); + let header = self.client.header(&BlockId::Hash(info.chain.best_hash)).unwrap().unwrap(); + self.sync.on_block_imported(&mut TestIo::new(&self.queue, None), info.chain.best_hash, &header); + } + + /// Called on connection to other indicated peer. + fn on_connect(&self, other: NodeIndex) { + self.sync.on_peer_connected(&mut TestIo::new(&self.queue, Some(other)), other); + } + + /// Called on disconnect from other indicated peer. + fn on_disconnect(&self, other: NodeIndex) { + let mut io = TestIo::new(&self.queue, Some(other)); + self.sync.on_peer_disconnected(&mut io, other); + } + + /// Receive a message from another peer. Return a set of peers to disconnect. + fn receive_message(&self, from: NodeIndex, msg: TestPacket) -> HashSet { + let mut io = TestIo::new(&self.queue, Some(from)); + self.sync.handle_packet(&mut io, from, &msg.data); + self.flush(); + io.to_disconnect.clone() + } + + /// Produce the next pending message to send to another peer. + fn pending_message(&self) -> Option { + self.flush(); + self.queue.write().pop_front() + } + + /// Whether this peer is done syncing (has no messages to send). + fn is_done(&self) -> bool { + self.queue.read().is_empty() + } + + /// Execute a "sync step". This is called for each peer after it sends a packet. + fn sync_step(&self) { + self.flush(); + self.sync.tick(&mut TestIo::new(&self.queue, None)); + } + + /// Restart sync for a peer. + fn restart_sync(&self) { + self.sync.abort(); + } + + fn flush(&self) { + } + + fn generate_blocks(&self, count: usize, mut edit_block: F) + where F: FnMut(&mut BlockBuilder) + { + for _ in 0 .. count { + let mut builder = self.client.new_block().unwrap(); + edit_block(&mut builder); + let block = builder.bake().unwrap(); + trace!("Generating {}, (#{})", block.hash(), block.header.number); + self.client.justify_and_import(client::BlockOrigin::File, block).unwrap(); + } + } + + fn push_blocks(&self, count: usize, with_tx: bool) { + let mut nonce = 0; + if with_tx { + self.generate_blocks(count, |builder| { + let transfer = Transfer { + from: Keyring::Alice.to_raw_public().into(), + to: Keyring::Alice.to_raw_public().into(), + amount: 1, + nonce, + }; + let signature = Keyring::from_raw_public(transfer.from.0).unwrap().sign(&transfer.encode()).into(); + builder.push(Extrinsic { transfer, signature }).unwrap(); + nonce = nonce + 1; + }); + } else { + self.generate_blocks(count, |_| ()); + } + } +} + +pub struct EmptyTransactionPool; + +impl TransactionPool for EmptyTransactionPool { + fn transactions(&self) -> Vec<(Hash, Extrinsic)> { + Vec::new() + } + + fn import(&self, _transaction: &Extrinsic) -> Option { + None + } + + fn on_broadcasted(&self, _: HashMap>) {} +} + +pub struct TestNet { + peers: Vec>, + started: bool, + disconnect_events: Vec<(NodeIndex, NodeIndex)>, //disconnected (initiated by, to) +} + +impl TestNet { + fn new(n: usize) -> Self { + Self::new_with_config(n, ProtocolConfig::default()) + } + + fn new_with_config(n: usize, config: ProtocolConfig) -> Self { + let mut net = TestNet { + peers: Vec::new(), + started: false, + disconnect_events: Vec::new(), + }; + + for _ in 0..n { + net.add_peer(&config); + } + net + } + + pub fn add_peer(&mut self, config: &ProtocolConfig) { + let client = Arc::new(test_client::new()); + let tx_pool = Arc::new(EmptyTransactionPool); + let import_queue = Arc::new(SyncImportQueue); + let sync = Protocol::new(config.clone(), client.clone(), import_queue, None, tx_pool, DummySpecialization).unwrap(); + self.peers.push(Arc::new(Peer { + sync: sync, + client: client, + queue: RwLock::new(VecDeque::new()), + })); + } + + pub fn peer(&self, i: usize) -> &Peer { + &self.peers[i] + } + + fn start(&mut self) { + if self.started { + return; + } + for peer in 0..self.peers.len() { + self.peers[peer].start(); + for client in 0..self.peers.len() { + if peer != client { + self.peers[peer].on_connect(client as NodeIndex); + } + } + } + self.started = true; + } + + fn sync_step(&mut self) { + for peer in 0..self.peers.len() { + let packet = self.peers[peer].pending_message(); + if let Some(packet) = packet { + let disconnecting = { + let recipient = packet.recipient; + trace!("--- {} -> {} ---", peer, recipient); + let to_disconnect = self.peers[recipient].receive_message(peer as NodeIndex, packet); + for d in &to_disconnect { + // notify this that disconnecting peers are disconnecting + self.peers[recipient].on_disconnect(*d as NodeIndex); + self.disconnect_events.push((peer, *d)); + } + to_disconnect + }; + for d in &disconnecting { + // notify other peers that this peer is disconnecting + self.peers[*d].on_disconnect(peer as NodeIndex); + } + } + + self.sync_step_peer(peer); + } + } + + fn sync_step_peer(&mut self, peer_num: usize) { + self.peers[peer_num].sync_step(); + } + + fn restart_peer(&mut self, i: usize) { + self.peers[i].restart_sync(); + } + + fn sync(&mut self) -> u32 { + self.start(); + let mut total_steps = 0; + while !self.done() { + self.sync_step(); + total_steps += 1; + } + total_steps + } + + fn sync_steps(&mut self, count: usize) { + self.start(); + for _ in 0..count { + self.sync_step(); + } + } + + fn done(&self) -> bool { + self.peers.iter().all(|p| p.is_done()) + } +} diff --git a/core/network/src/test/sync.rs b/core/network/src/test/sync.rs new file mode 100644 index 0000000000000..77367a3df1389 --- /dev/null +++ b/core/network/src/test/sync.rs @@ -0,0 +1,121 @@ +// Copyright 2017 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 client::backend::Backend; +use client::blockchain::HeaderBackend as BlockchainHeaderBackend; +use sync::SyncState; +use Roles; +use super::*; + +#[test] +fn sync_from_two_peers_works() { + ::env_logger::init().ok(); + let mut net = TestNet::new(3); + net.peer(1).push_blocks(100, false); + net.peer(2).push_blocks(100, false); + net.sync(); + assert!(net.peer(0).client.backend().blockchain().equals_to(net.peer(1).client.backend().blockchain())); + let status = net.peer(0).sync.status(); + assert_eq!(status.sync.state, SyncState::Idle); +} + +#[test] +fn sync_from_two_peers_with_ancestry_search_works() { + ::env_logger::init().ok(); + 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.restart_peer(0); + net.sync(); + assert!(net.peer(0).client.backend().blockchain().canon_equals_to(net.peer(1).client.backend().blockchain())); +} + +#[test] +fn sync_long_chain_works() { + let mut net = TestNet::new(2); + net.peer(1).push_blocks(500, false); + net.sync_steps(3); + assert_eq!(net.peer(0).sync.status().sync.state, SyncState::Downloading); + net.sync(); + assert!(net.peer(0).client.backend().blockchain().equals_to(net.peer(1).client.backend().blockchain())); +} + +#[test] +fn sync_no_common_longer_chain_fails() { + ::env_logger::init().ok(); + let mut net = TestNet::new(3); + net.peer(0).push_blocks(20, true); + net.peer(1).push_blocks(20, false); + net.sync(); + assert!(!net.peer(0).client.backend().blockchain().canon_equals_to(net.peer(1).client.backend().blockchain())); +} + +#[test] +fn sync_after_fork_works() { + ::env_logger::init().ok(); + let mut net = TestNet::new(3); + net.peer(0).push_blocks(30, false); + net.peer(1).push_blocks(30, false); + net.peer(2).push_blocks(30, false); + + net.peer(0).push_blocks(10, true); + net.peer(1).push_blocks(20, false); + net.peer(2).push_blocks(20, false); + + net.peer(1).push_blocks(10, true); + net.peer(2).push_blocks(1, false); + + // peer 1 has the best chain + let peer1_chain = net.peer(1).client.backend().blockchain().clone(); + net.sync(); + assert!(net.peer(0).client.backend().blockchain().canon_equals_to(&peer1_chain)); + assert!(net.peer(1).client.backend().blockchain().canon_equals_to(&peer1_chain)); + assert!(net.peer(2).client.backend().blockchain().canon_equals_to(&peer1_chain)); +} + +#[test] +fn blocks_are_not_announced_by_light_nodes() { + ::env_logger::init().ok(); + let mut net = TestNet::new(0); + + // full peer0 is connected to light peer + // light peer1 is connected to full peer2 + let mut light_config = ProtocolConfig::default(); + light_config.roles = Roles::LIGHT; + net.add_peer(&ProtocolConfig::default()); + net.add_peer(&light_config); + net.add_peer(&ProtocolConfig::default()); + + net.peer(0).push_blocks(1, false); + net.peer(0).start(); + net.peer(1).start(); + net.peer(2).start(); + net.peer(0).on_connect(1); + net.peer(1).on_connect(2); + + // generate block at peer0 && run sync + while !net.done() { + net.sync_step(); + } + + // peer 0 has the best chain + // peer 1 has the best chain + // peer 2 has genesis-chain only + assert_eq!(net.peer(0).client.backend().blockchain().info().unwrap().best_number, 1); + assert_eq!(net.peer(1).client.backend().blockchain().info().unwrap().best_number, 1); + assert_eq!(net.peer(2).client.backend().blockchain().info().unwrap().best_number, 0); +} diff --git a/core/primitives/Cargo.toml b/core/primitives/Cargo.toml new file mode 100644 index 0000000000000..d14c3a172866a --- /dev/null +++ b/core/primitives/Cargo.toml @@ -0,0 +1,58 @@ +[package] +name = "substrate-primitives" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +crunchy = "0.1" +substrate-runtime-std = { path = "../runtime-std", default_features = false } +substrate-codec = { path = "../codec", default_features = false } +substrate-codec-derive = { path = "../codec/derive", default_features = false } +elastic-array = {version = "0.10", optional = true } +fixed-hash = { version = "0.2.2", default_features = false } +rustc-hex = { version = "2.0", default_features = false } +serde = { version = "1.0", default_features = false } +serde_derive = { version = "1.0", optional = true } +uint = { version = "0.4.1", default_features = false } +rlp = { version = "0.2.4", optional = true } +twox-hash = { version = "1.1.0", optional = true } +byteorder = { version = "1.1", default_features = false } +wasmi = { version = "0.4", optional = true } +hashdb = { version = "0.2.1", default_features = false } +patricia-trie = { version = "0.2.1", optional = true } +plain_hasher = { version = "0.2", default_features = false } +blake2-rfc = { version = "0.2.18", optional = true } +ring = { version = "0.12", optional = true } +untrusted = { version = "0.5", optional = true } +hex-literal = { version = "0.1", optional = true } +base58 = { version = "0.1", optional = true } + +[dev-dependencies] +substrate-serializer = { path = "../serializer" } +pretty_assertions = "0.4" +heapsize = "0.4" + +[features] +default = ["std"] +std = [ + "wasmi", + "uint/std", + "fixed-hash/std", + "fixed-hash/heapsizeof", + "fixed-hash/libc", + "substrate-codec/std", + "substrate-runtime-std/std", + "serde/std", + "rustc-hex/std", + "twox-hash", + "blake2-rfc", + "ring", + "untrusted", + "hex-literal", + "base58", + "serde_derive", + "byteorder/std", + "patricia-trie", + "rlp", + "elastic-array", +] diff --git a/core/primitives/README.adoc b/core/primitives/README.adoc new file mode 100644 index 0000000000000..ed98cf12adf1f --- /dev/null +++ b/core/primitives/README.adoc @@ -0,0 +1,13 @@ + += Primitives + +.Summary +[source, toml] +---- +include::Cargo.toml[lines=2..5] +---- + +.Description +---- +include::src/lib.rs[tag=description] +---- diff --git a/core/primitives/src/authority_id.rs b/core/primitives/src/authority_id.rs new file mode 100644 index 0000000000000..c82261bce58a0 --- /dev/null +++ b/core/primitives/src/authority_id.rs @@ -0,0 +1,106 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + + +#[cfg(feature = "std")] +use serde::{Serialize, Serializer, Deserialize, Deserializer}; +use H256; + +/// An identifier for an authority in the consensus algorithm. The same size as ed25519::Public. +#[derive(Clone, Copy, PartialEq, Eq, Default, Encode, Decode)] +pub struct AuthorityId(pub [u8; 32]); + +impl AuthorityId { + /// Create an id from a 32-byte slice. Panics with other lengths. + #[cfg(feature = "std")] + fn from_slice(data: &[u8]) -> Self { + let mut r = [0u8; 32]; + r.copy_from_slice(data); + AuthorityId(r) + } +} + +#[cfg(feature = "std")] +impl ::std::fmt::Display for AuthorityId { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "{}", ::hexdisplay::HexDisplay::from(&self.0)) + } +} + +#[cfg(feature = "std")] +impl ::std::fmt::Debug for AuthorityId { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "{}", ::hexdisplay::HexDisplay::from(&self.0)) + } +} + +#[cfg(feature = "std")] +impl ::std::hash::Hash for AuthorityId { + fn hash(&self, state: &mut H) { + self.0.hash(state); + } +} + +impl AsRef<[u8; 32]> for AuthorityId { + fn as_ref(&self) -> &[u8; 32] { + &self.0 + } +} + +impl AsRef<[u8]> for AuthorityId { + fn as_ref(&self) -> &[u8] { + &self.0[..] + } +} + +impl Into<[u8; 32]> for AuthorityId { + fn into(self) -> [u8; 32] { + self.0 + } +} + +impl From<[u8; 32]> for AuthorityId { + fn from(a: [u8; 32]) -> Self { + AuthorityId(a) + } +} + +impl AsRef for AuthorityId { + fn as_ref(&self) -> &AuthorityId { + &self + } +} + +impl Into for AuthorityId { + fn into(self) -> H256 { + self.0.into() + } +} + +#[cfg(feature = "std")] +impl Serialize for AuthorityId { + fn serialize(&self, serializer: S) -> Result where S: Serializer { + ::bytes::serialize(&self.0, serializer) + } +} + +#[cfg(feature = "std")] +impl<'de> Deserialize<'de> for AuthorityId { + fn deserialize(deserializer: D) -> Result where D: Deserializer<'de> { + ::bytes::deserialize_check_len(deserializer, ::bytes::ExpectedLen::Exact(32)) + .map(|x| AuthorityId::from_slice(&x)) + } +} diff --git a/core/primitives/src/bytes.rs b/core/primitives/src/bytes.rs new file mode 100644 index 0000000000000..04605bedf8b94 --- /dev/null +++ b/core/primitives/src/bytes.rs @@ -0,0 +1,158 @@ +// Copyright 2017 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 . + +//! Simple type for representing Vec with regards to serde. + +use core::fmt; + +use serde::{de, Serializer, Deserializer}; + +#[cfg(not(feature = "std"))] +mod alloc_types { + pub use ::alloc::string::String; + pub use ::alloc::vec::Vec; +} + +#[cfg(feature = "std")] +mod alloc_types { + pub use ::std::vec::Vec; + pub use ::std::string::String; +} + +pub use self::alloc_types::*; + +/// Serializes a slice of bytes. +pub fn serialize(bytes: &[u8], serializer: S) -> Result where + S: Serializer, +{ + let hex: String = ::rustc_hex::ToHex::to_hex(bytes); + serializer.serialize_str(&format!("0x{}", hex)) +} + +/// Serialize a slice of bytes as uint. +/// +/// The representation will have all leading zeros trimmed. +pub fn serialize_uint(bytes: &[u8], serializer: S) -> Result where + S: Serializer, +{ + let non_zero = bytes.iter().take_while(|b| **b == 0).count(); + let bytes = &bytes[non_zero..]; + if bytes.is_empty() { + return serializer.serialize_str("0x0"); + } + + let hex: String = ::rustc_hex::ToHex::to_hex(bytes); + let has_leading_zero = !hex.is_empty() && &hex[0..1] == "0"; + serializer.serialize_str( + &format!("0x{}", if has_leading_zero { &hex[1..] } else { &hex }) + ) +} + +/// Expected length of bytes vector. +#[derive(PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(Debug))] +pub enum ExpectedLen { + /// Any length in bytes. + #[cfg_attr(not(feature = "std"), allow(unused))] + Any, + /// Exact length in bytes. + Exact(usize), + /// A bytes length between (min; max]. + Between(usize, usize), +} + +impl fmt::Display for ExpectedLen { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match *self { + ExpectedLen::Any => write!(fmt, "even length"), + ExpectedLen::Exact(v) => write!(fmt, "length of {}", v * 2), + ExpectedLen::Between(min, max) => write!(fmt, "length between ({}; {}]", min * 2, max * 2), + } + } +} + +/// Deserialize into vector of bytes. +#[cfg(feature = "std")] +pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> where + D: Deserializer<'de>, +{ + deserialize_check_len(deserializer, ExpectedLen::Any) +} + +/// Deserialize into vector of bytes with additional size check. +pub fn deserialize_check_len<'de, D>(deserializer: D, len: ExpectedLen) -> Result, D::Error> where + D: Deserializer<'de>, +{ + struct Visitor { + len: ExpectedLen, + } + + impl<'a> de::Visitor<'a> for Visitor { + type Value = Vec; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a 0x-prefixed hex string with {}", self.len) + } + + fn visit_str(self, v: &str) -> Result { + if v.len() < 2 || &v[0..2] != "0x" { + return Err(E::custom("prefix is missing")) + } + + let is_len_valid = match self.len { + // just make sure that we have all nibbles + ExpectedLen::Any => v.len() % 2 == 0, + ExpectedLen::Exact(len) => v.len() == 2 * len + 2, + ExpectedLen::Between(min, max) => v.len() <= 2 * max + 2 && v.len() > 2 * min + 2, + }; + + if !is_len_valid { + return Err(E::invalid_length(v.len() - 2, &self)) + } + + let bytes = match self.len { + ExpectedLen::Between(..) if v.len() % 2 != 0 => { + ::rustc_hex::FromHex::from_hex(&*format!("0{}", &v[2..])) + }, + _ => ::rustc_hex::FromHex::from_hex(&v[2..]) + }; + + #[cfg(feature = "std")] + fn format_err(e: ::rustc_hex::FromHexError) -> String { + format!("invalid hex value: {:?}", e) + } + + #[cfg(not(feature = "std"))] + fn format_err(e: ::rustc_hex::FromHexError) -> String { + match e { + ::rustc_hex::InvalidHexLength => format!("invalid hex value: invalid length"), + ::rustc_hex::InvalidHexCharacter(c, p) => + format!("invalid hex value: invalid character {} at position {}", c, p), + } + } + + bytes.map_err(|e| E::custom(format_err(e))) + } + + #[cfg(feature = "std")] + fn visit_string(self, v: String) -> Result { + self.visit_str(&v) + } + } + // TODO [ToDr] Use raw bytes if we switch to RLP / binencoding + // (visit_bytes, visit_bytes_buf) + deserializer.deserialize_str(Visitor { len }) +} diff --git a/core/primitives/src/ed25519.rs b/core/primitives/src/ed25519.rs new file mode 100644 index 0000000000000..bc5b691d4d2b0 --- /dev/null +++ b/core/primitives/src/ed25519.rs @@ -0,0 +1,357 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +// tag::description[] +//! Simple Ed25519 API. +// end::description[] + +use untrusted; +use blake2_rfc; +use ring::{rand, signature}; +use hash::H512; +use AuthorityId; +use base58::{ToBase58, FromBase58}; + +#[cfg(test)] +#[macro_use] +extern crate hex_literal; + +/// Alias to 512-bit hash when used in the context of a signature on the relay chain. +pub type Signature = H512; + +/// Length of the PKCS#8 encoding of the key. +pub const PKCS_LEN: usize = 85; + +/// A localized signature also contains sender information. +#[derive(PartialEq, Eq, Clone, Debug)] +pub struct LocalizedSignature { + /// The signer of the signature. + pub signer: Public, + /// The signature itself. + pub signature: Signature, +} + +/// Verify a message without type checking the parameters' types for the right size. +pub fn verify>(sig: &[u8], message: &[u8], public: P) -> bool { + let public_key = untrusted::Input::from(public.as_ref()); + let msg = untrusted::Input::from(message); + let sig = untrusted::Input::from(sig); + + match signature::verify(&signature::ED25519, public_key, msg, sig) { + Ok(_) => true, + _ => false, + } +} + +/// A public key. +#[derive(PartialEq, Eq, Clone)] +pub struct Public(pub [u8; 32]); + +/// A key pair. +pub struct Pair(signature::Ed25519KeyPair); + +impl ::std::hash::Hash for Public { + fn hash(&self, state: &mut H) { + self.0.hash(state); + } +} + +/// Error with the SS58 encoding. +#[derive(Clone, Copy, Eq, PartialEq, Debug)] +pub enum PublicError { + /// Bad base-58 encoding. + BadBase58, + /// Bad length of key. + BadLength, + /// Unknown version identifier. + UnknownVersion, + /// Checksum is invalid. + InvalidChecksum, +} + +impl Public { + /// A new instance from the given 32-byte `data`. + pub fn from_raw(data: [u8; 32]) -> Self { + Public(data) + } + + /// A new instance from the given slice that should be 32 bytes long. + pub fn from_slice(data: &[u8]) -> Self { + let mut r = [0u8; 32]; + r.copy_from_slice(data); + Public(r) + } + + /// Some if the string is a properly encoded SS58Check address. + pub fn from_ss58check(s: &str) -> Result { + let d = s.from_base58().map_err(|_| PublicError::BadBase58)?; // failure here would be invalid encoding. + if d.len() != 35 { + // Invalid length. + return Err(PublicError::BadLength); + } + if d[0] != 42 { + // Invalid version. + return Err(PublicError::UnknownVersion); + } + if d[33..35] != blake2_rfc::blake2b::blake2b(64, &[], &d[0..33]).as_bytes()[0..2] { + // Invalid checksum. + return Err(PublicError::InvalidChecksum); + } + Ok(Self::from_slice(&d[1..33])) + } + + /// Return a `Vec` filled with raw data. + pub 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] { + 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() + } + + /// Return the ss58-check string for this key. + pub fn to_ss58check(&self) -> String { + let mut v = vec![42u8]; + v.extend(self.as_slice()); + let r = blake2_rfc::blake2b::blake2b(64, &[], &v); + v.extend(&r.as_bytes()[0..2]); + v.to_base58() + } +} + +impl AsRef<[u8; 32]> for Public { + fn as_ref(&self) -> &[u8; 32] { + &self.0 + } +} + +impl AsRef<[u8]> for Public { + fn as_ref(&self) -> &[u8] { + &self.0[..] + } +} + +impl Into<[u8; 32]> for Public { + fn into(self) -> [u8; 32] { + self.0 + } +} + +impl AsRef for Public { + fn as_ref(&self) -> &Public { + &self + } +} + +impl AsRef for Pair { + fn as_ref(&self) -> &Pair { + &self + } +} + +impl Into for Public { + fn into(self) -> AuthorityId { + AuthorityId(self.0) + } +} + +impl From for Public { + fn from(id: AuthorityId) -> Self { + Public(id.0) + } +} + +impl ::std::fmt::Display for Public { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "{}", self.to_ss58check()) + } +} + +impl ::std::fmt::Debug for Public { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + let s = self.to_ss58check(); + write!(f, "{} ({}...)", ::hexdisplay::HexDisplay::from(&self.0), &s[0..8]) + } +} + +impl Pair { + /// Generate new secure (random) key pair, yielding it and the corresponding pkcs#8 bytes. + pub fn generate_with_pkcs8() -> (Self, [u8; PKCS_LEN]) { + let rng = rand::SystemRandom::new(); + let pkcs8_bytes = signature::Ed25519KeyPair::generate_pkcs8(&rng).expect("system randomness is available; qed"); + let pair = Self::from_pkcs8(&pkcs8_bytes).expect("just-generated pkcs#8 data is valid; qed"); + + (pair, pkcs8_bytes) + } + + /// Generate new secure (random) key pair. + pub fn generate() -> Pair { + let (pair, _) = Self::generate_with_pkcs8(); + pair + } + + /// Generate from pkcs#8 bytes. + pub fn from_pkcs8(pkcs8_bytes: &[u8]) -> Result { + signature::Ed25519KeyPair::from_pkcs8(untrusted::Input::from(&pkcs8_bytes)).map(Pair) + } + + /// Make a new key pair from a seed phrase. + /// NOTE: prefer pkcs#8 unless security doesn't matter -- this is used primarily for tests. + pub fn from_seed(seed: &[u8; 32]) -> Pair { + let key = signature::Ed25519KeyPair::from_seed_unchecked(untrusted::Input::from(&seed[..])) + .expect("seed has valid length; qed"); + + Pair(key) + } + + /// Sign a message. + pub fn sign(&self, message: &[u8]) -> Signature { + let mut r = [0u8; 64]; + r.copy_from_slice(self.0.sign(message).as_ref()); + Signature::from(r) + } + + /// Get the public key. + pub fn public(&self) -> Public { + let mut r = [0u8; 32]; + let pk = self.0.public_key_bytes(); + r.copy_from_slice(pk); + Public(r) + } + + /// Derive a child key. Probably unsafe and broken. + // TODO: proper HD derivation https://cardanolaunch.com/assets/Ed25519_BIP.pdf + pub fn derive_child_probably_bad(&self, chain_data: &[u8]) -> Pair { + let sig = self.sign(chain_data); + let mut seed = [0u8; 32]; + seed.copy_from_slice(&sig.0[..32]); + + Pair::from_seed(&seed) + } +} + +/// Verify a signature on a message. +pub fn verify_strong>(sig: &Signature, message: &[u8], pubkey: P) -> bool { + let public_key = untrusted::Input::from(&pubkey.as_ref().0[..]); + let msg = untrusted::Input::from(message); + let sig = untrusted::Input::from(&sig.0[..]); + + match signature::verify(&signature::ED25519, public_key, msg, sig) { + Ok(_) => true, + _ => false, + } +} + +/// Something that can verify a message against a given public key. +pub trait Verifiable { + /// Verify something that acts like a signature. + fn verify>(&self, message: &[u8], pubkey: P) -> bool; +} + +impl Verifiable for Signature { + /// Verify something that acts like a signature. + fn verify>(&self, message: &[u8], pubkey: P) -> bool { + verify_strong(&self, message, pubkey) + } +} + +impl Verifiable for LocalizedSignature { + fn verify>(&self, message: &[u8], pubkey: P) -> bool { + pubkey.as_ref() == &self.signer && self.signature.verify(message, pubkey) + } +} + +#[cfg(test)] +mod test { + use super::*; + + fn _test_primitives_signature_and_local_the_same() { + fn takes_two(_: T, _: T) { } + takes_two(Signature::default(), primitives::Signature::default()) + } + + #[test] + fn test_vector_should_work() { + let pair: Pair = Pair::from_seed(&hex!("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60")); + let public = pair.public(); + assert_eq!(public, Public::from_raw(hex!("d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a"))); + let message = b""; + let signature: Signature = hex!("e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b").into(); + assert!(&pair.sign(&message[..]) == &signature); + assert!(verify_strong(&signature, &message[..], &public)); + } + + #[test] + fn generated_pair_should_work() { + let pair = Pair::generate(); + let public = pair.public(); + let message = b"Something important"; + let signature = pair.sign(&message[..]); + assert!(verify_strong(&signature, &message[..], &public)); + } + + #[test] + fn seeded_pair_should_work() { + use primitives::hexdisplay::HexDisplay; + + let pair = Pair::from_seed(b"12345678901234567890123456789012"); + let public = pair.public(); + assert_eq!(public, Public::from_raw(hex!("2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee"))); + let message = hex!("2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee00000000000000000200d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a4500000000000000"); + let signature = pair.sign(&message[..]); + println!("Correct signature: {}", HexDisplay::from(&signature.0)); + assert!(verify_strong(&signature, &message[..], &public)); + } + + #[test] + fn generate_with_pkcs8_recovery_possible() { + let (pair1, pkcs8) = Pair::generate_with_pkcs8(); + let pair2 = Pair::from_pkcs8(&pkcs8).unwrap(); + + assert_eq!(pair1.public(), pair2.public()); + } + + #[test] + fn derive_child() { + let pair = Pair::generate(); + let _pair2 = pair.derive_child_probably_bad(b"session_1234"); + } + + #[test] + fn ss58check_roundtrip_works() { + let pair = Pair::from_seed(b"12345678901234567890123456789012"); + let public = pair.public(); + let s = public.to_ss58check(); + println!("Correct: {}", s); + let cmp = Public::from_ss58check(&s).unwrap(); + assert_eq!(cmp, public); + } + + #[test] + fn ss58check_known_works() { + let k = "5CGavy93sZgPPjHyziRohwVumxiHXMGmQLyuqQP4ZFx5vRU9"; + let enc = hex!["090fa15cb5b1666222fff584b4cc2b1761fe1e238346b340491b37e25ea183ff"]; + assert_eq!(Public::from_ss58check(k).unwrap(), Public::from_raw(enc)); + } +} diff --git a/core/primitives/src/hash.rs b/core/primitives/src/hash.rs new file mode 100644 index 0000000000000..c8883da42d3d6 --- /dev/null +++ b/core/primitives/src/hash.rs @@ -0,0 +1,166 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! A fixed hash type. + +#[cfg(feature = "std")] +use serde::{Serialize, Serializer, Deserialize, Deserializer}; + +#[cfg(feature = "std")] +use bytes; +#[cfg(feature = "std")] +use core::cmp; +#[cfg(feature = "std")] +use rlp::{Rlp, RlpStream, DecoderError}; + +macro_rules! impl_rest { + ($name: ident, $len: expr) => { + #[cfg(feature = "std")] + impl Serialize for $name { + fn serialize(&self, serializer: S) -> Result where S: Serializer { + bytes::serialize(&self.0, serializer) + } + } + + #[cfg(feature = "std")] + impl<'de> Deserialize<'de> for $name { + fn deserialize(deserializer: D) -> Result where D: Deserializer<'de> { + bytes::deserialize_check_len(deserializer, bytes::ExpectedLen::Exact($len)) + .map(|x| (&*x).into()) + } + } + + impl ::codec::Encode for $name { + fn using_encoded R>(&self, f: F) -> R { + self.0.using_encoded(f) + } + } + impl ::codec::Decode for $name { + fn decode(input: &mut I) -> Option { + <[u8; $len] as ::codec::Decode>::decode(input).map($name) + } + } + + #[cfg(feature = "std")] + impl ::rlp::Encodable for $name { + fn rlp_append(&self, s: &mut RlpStream) { + s.encoder().encode_value(self); + } + } + + #[cfg(feature = "std")] + impl ::rlp::Decodable for $name { + fn decode(rlp: &Rlp) -> Result { + rlp.decoder().decode_value(|bytes| match bytes.len().cmp(&$len) { + cmp::Ordering::Less => Err(DecoderError::RlpIsTooShort), + cmp::Ordering::Greater => Err(DecoderError::RlpIsTooBig), + cmp::Ordering::Equal => { + let mut t = [0u8; $len]; + t.copy_from_slice(bytes); + Ok($name(t)) + } + }) + } + } + + } +} + +construct_hash!(H160, 20); +construct_hash!(H256, 32); +construct_hash!(H512, 64); +impl_rest!(H160, 20); +impl_rest!(H256, 32); +impl_rest!(H512, 64); + +#[cfg(test)] +mod tests { + use super::*; + use substrate_serializer as ser; + use rlp::{Encodable, RlpStream}; + + #[test] + fn test_hash_is_encodable() { + let h = H160::from(21); + let mut s = RlpStream::new(); + h.rlp_append(&mut s); + assert_eq!(s.drain().into_vec(), &[148, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21]); + } + + #[test] + fn test_hash_is_decodable() { + let data = vec![148, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 123]; + let res = ::rlp::decode::(&data); + assert!(res.is_ok()); + assert_eq!(res.unwrap(), H160::from(123)); + + let res = ::rlp::decode::(&data); + assert!(res.is_err()); + } + + #[test] + fn test_h160() { + let tests = vec![ + (Default::default(), "0x0000000000000000000000000000000000000000"), + (H160::from(2), "0x0000000000000000000000000000000000000002"), + (H160::from(15), "0x000000000000000000000000000000000000000f"), + (H160::from(16), "0x0000000000000000000000000000000000000010"), + (H160::from(1_000), "0x00000000000000000000000000000000000003e8"), + (H160::from(100_000), "0x00000000000000000000000000000000000186a0"), + (H160::from(u64::max_value()), "0x000000000000000000000000ffffffffffffffff"), + ]; + + for (number, expected) in tests { + assert_eq!(format!("{:?}", expected), ser::to_string_pretty(&number)); + assert_eq!(number, ser::from_str(&format!("{:?}", expected)).unwrap()); + } + } + + #[test] + fn test_h256() { + let tests = vec![ + (Default::default(), "0x0000000000000000000000000000000000000000000000000000000000000000"), + (H256::from(2), "0x0000000000000000000000000000000000000000000000000000000000000002"), + (H256::from(15), "0x000000000000000000000000000000000000000000000000000000000000000f"), + (H256::from(16), "0x0000000000000000000000000000000000000000000000000000000000000010"), + (H256::from(1_000), "0x00000000000000000000000000000000000000000000000000000000000003e8"), + (H256::from(100_000), "0x00000000000000000000000000000000000000000000000000000000000186a0"), + (H256::from(u64::max_value()), "0x000000000000000000000000000000000000000000000000ffffffffffffffff"), + ]; + + for (number, expected) in tests { + assert_eq!(format!("{:?}", expected), ser::to_string_pretty(&number)); + assert_eq!(number, ser::from_str(&format!("{:?}", expected)).unwrap()); + } + } + + #[test] + fn test_invalid() { + assert!(ser::from_str::("\"0x000000000000000000000000000000000000000000000000000000000000000\"").unwrap_err().is_data()); + assert!(ser::from_str::("\"0x000000000000000000000000000000000000000000000000000000000000000g\"").unwrap_err().is_data()); + assert!(ser::from_str::("\"0x00000000000000000000000000000000000000000000000000000000000000000\"").unwrap_err().is_data()); + assert!(ser::from_str::("\"\"").unwrap_err().is_data()); + assert!(ser::from_str::("\"0\"").unwrap_err().is_data()); + assert!(ser::from_str::("\"10\"").unwrap_err().is_data()); + } + + #[test] + fn test_heapsizeof() { + use heapsize::HeapSizeOf; + let h = H256::new(); + assert_eq!(h.heap_size_of_children(), 0); + } +} diff --git a/core/primitives/src/hasher.rs b/core/primitives/src/hasher.rs new file mode 100644 index 0000000000000..d4dd39b855498 --- /dev/null +++ b/core/primitives/src/hasher.rs @@ -0,0 +1,53 @@ +// Copyright 2018 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 . + +//! Polkadot Blake2b Hasher implementation + +use hashdb::Hasher; +use plain_hasher::PlainHasher; +use hash::H256; + +pub mod blake2 { + use super::{Hasher, PlainHasher, H256}; + #[cfg(feature = "std")] + use hashing::blake2_256; + + #[cfg(not(feature = "std"))] + extern "C" { + fn ext_blake2_256(data: *const u8, len: u32, out: *mut u8); + } + #[cfg(not(feature = "std"))] + fn blake2_256(data: &[u8]) -> [u8; 32] { + let mut result: [u8; 32] = Default::default(); + unsafe { + ext_blake2_256(data.as_ptr(), data.len() as u32, result.as_mut_ptr()); + } + result + } + + /// Concrete implementation of Hasher using Blake2b 256-bit hashes + #[derive(Debug)] + pub struct Blake2Hasher; + + impl Hasher for Blake2Hasher { + type Out = H256; + type StdHasher = PlainHasher; + const LENGTH:usize = 32; + fn hash(x: &[u8]) -> Self::Out { + blake2_256(x).into() + } + } +} diff --git a/core/primitives/src/hashing.rs b/core/primitives/src/hashing.rs new file mode 100644 index 0000000000000..379ea4a56f4e7 --- /dev/null +++ b/core/primitives/src/hashing.rs @@ -0,0 +1,106 @@ +// Copyright 2017 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 . + +//! Hashing functions. + +use blake2_rfc; +use twox_hash; + +/// Do a Blake2 512-bit hash and place result in `dest`. +pub fn blake2_512_into(data: &[u8], dest: &mut [u8; 64]) { + dest.copy_from_slice(blake2_rfc::blake2b::blake2b(64, &[], data).as_bytes()); +} + +/// Do a Blake2 512-bit hash and return result. +pub fn blake2_512(data: &[u8]) -> [u8; 64] { + let mut r = [0; 64]; + blake2_512_into(data, &mut r); + r +} + +/// Do a Blake2 256-bit hash and place result in `dest`. +pub fn blake2_256_into(data: &[u8], dest: &mut [u8; 32]) { + dest.copy_from_slice(blake2_rfc::blake2b::blake2b(32, &[], data).as_bytes()); +} + +/// Do a Blake2 256-bit hash and return result. +pub fn blake2_256(data: &[u8]) -> [u8; 32] { + let mut r = [0; 32]; + blake2_256_into(data, &mut r); + r +} + +/// Do a Blake2 128-bit hash and place result in `dest`. +pub fn blake2_128_into(data: &[u8], dest: &mut [u8; 16]) { + dest.copy_from_slice(blake2_rfc::blake2b::blake2b(16, &[], data).as_bytes()); +} + +/// Do a Blake2 128-bit hash and return result. +pub fn blake2_128(data: &[u8]) -> [u8; 16] { + let mut r = [0; 16]; + blake2_128_into(data, &mut r); + r +} + +/// Do a XX 128-bit hash and place result in `dest`. +pub fn twox_128_into(data: &[u8], dest: &mut [u8; 16]) { + use ::core::hash::Hasher; + let mut h0 = twox_hash::XxHash::with_seed(0); + let mut h1 = twox_hash::XxHash::with_seed(1); + h0.write(data); + h1.write(data); + let r0 = h0.finish(); + let r1 = h1.finish(); + use byteorder::{ByteOrder, LittleEndian}; + LittleEndian::write_u64(&mut dest[0..8], r0); + LittleEndian::write_u64(&mut dest[8..16], r1); +} + +/// Do a XX 128-bit hash and return result. +pub fn twox_128(data: &[u8]) -> [u8; 16] { + let mut r: [u8; 16] = [0; 16]; + twox_128_into(data, &mut r); + r +} + +/// Do a XX 256-bit hash and place result in `dest`. +pub fn twox_256_into(data: &[u8], dest: &mut [u8; 32]) { + use ::core::hash::Hasher; + use byteorder::{ByteOrder, LittleEndian}; + let mut h0 = twox_hash::XxHash::with_seed(0); + let mut h1 = twox_hash::XxHash::with_seed(1); + let mut h2 = twox_hash::XxHash::with_seed(2); + let mut h3 = twox_hash::XxHash::with_seed(3); + h0.write(data); + h1.write(data); + h2.write(data); + h3.write(data); + let r0 = h0.finish(); + let r1 = h1.finish(); + let r2 = h2.finish(); + let r3 = h3.finish(); + LittleEndian::write_u64(&mut dest[0..8], r0); + LittleEndian::write_u64(&mut dest[8..16], r1); + LittleEndian::write_u64(&mut dest[16..24], r2); + LittleEndian::write_u64(&mut dest[24..32], r3); +} + +/// Do a XX 256-bit hash and return result. +pub fn twox_256(data: &[u8]) -> [u8; 32] { + let mut r: [u8; 32] = [0; 32]; + twox_256_into(data, &mut r); + r +} diff --git a/core/primitives/src/hexdisplay.rs b/core/primitives/src/hexdisplay.rs new file mode 100644 index 0000000000000..938d3945c5aa3 --- /dev/null +++ b/core/primitives/src/hexdisplay.rs @@ -0,0 +1,94 @@ +// Copyright 2017 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 . + +//! Wrapper type for byte collections that outputs hex. + +/// Simple wrapper to display hex representation of bytes. +pub struct HexDisplay<'a>(&'a [u8]); + +impl<'a> HexDisplay<'a> { + /// Create new instance that will display `d` as a hex string when displayed. + pub fn from(d: &'a AsBytesRef) -> Self { HexDisplay(d.as_bytes_ref()) } +} + +impl<'a> ::core::fmt::Display for HexDisplay<'a> { + fn fmt(&self, fmtr: &mut ::core::fmt::Formatter) -> Result<(), ::core::fmt::Error> { + if self.0.len() < 1027 { + for byte in self.0 { + fmtr.write_fmt(format_args!("{:02x}", byte))?; + } + } else { + for byte in &self.0[0..512] { + fmtr.write_fmt(format_args!("{:02x}", byte))?; + } + fmtr.write_str("...")?; + for byte in &self.0[self.0.len() - 512..] { + fmtr.write_fmt(format_args!("{:02x}", byte))?; + } + } + Ok(()) + } +} + +/// Simple trait to transform various types to `&[u8]` +pub trait AsBytesRef { + /// Transform `self` into `&[u8]`. + fn as_bytes_ref(&self) -> &[u8]; +} + +impl<'a> AsBytesRef for &'a [u8] { + fn as_bytes_ref(&self) -> &[u8] { self } +} + +impl AsBytesRef for [u8] { + fn as_bytes_ref(&self) -> &[u8] { &self } +} + +impl AsBytesRef for ::bytes::Vec { + fn as_bytes_ref(&self) -> &[u8] { &self } +} + +macro_rules! impl_non_endians { + ( $( $t:ty ),* ) => { $( + impl AsBytesRef for $t { + fn as_bytes_ref(&self) -> &[u8] { &self[..] } + } + )* } +} + +impl_non_endians!([u8; 1], [u8; 2], [u8; 3], [u8; 4], [u8; 5], [u8; 6], [u8; 7], [u8; 8], + [u8; 10], [u8; 12], [u8; 14], [u8; 16], [u8; 20], [u8; 24], [u8; 28], [u8; 32], [u8; 40], + [u8; 48], [u8; 56], [u8; 64], [u8; 80], [u8; 96], [u8; 112], [u8; 128]); + +/// Format into ASCII + # + hex, suitable for storage key preimages. +pub fn ascii_format(asciish: &[u8]) -> String { + let mut r = String::new(); + let mut latch = false; + for c in asciish { + match (latch, *c) { + (false, 32...127) => r.push(*c as char), + _ => { + if !latch { + r.push('#'); + latch = true; + } + r.push_str(&format!("{:02x}", *c)); + } + } + } + r +} + diff --git a/core/primitives/src/lib.rs b/core/primitives/src/lib.rs new file mode 100644 index 0000000000000..e61a96270416c --- /dev/null +++ b/core/primitives/src/lib.rs @@ -0,0 +1,142 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +// tag::description[] +//! Shareable Polkadot types. +// end::description[] + +#![warn(missing_docs)] + +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(not(feature = "std"), feature(alloc))] + +#[macro_use] +extern crate crunchy; +#[macro_use] +extern crate fixed_hash; +#[macro_use] +extern crate uint as uint_crate; +#[macro_use] +extern crate substrate_codec_derive; + +extern crate rustc_hex; +extern crate byteorder; +extern crate substrate_codec as codec; +#[cfg(feature = "std")] +extern crate rlp; + +#[cfg(feature = "std")] +extern crate serde; +#[cfg(feature = "std")] +extern crate twox_hash; + +#[cfg(feature = "std")] +extern crate ring; +#[cfg(feature = "std")] +extern crate base58; +#[cfg(feature = "std")] +extern crate untrusted; +#[cfg(feature = "std")] +extern crate blake2_rfc; + +#[cfg(feature = "std")] +#[macro_use] +extern crate serde_derive; +#[cfg(feature = "std")] +extern crate core; +#[cfg(feature = "std")] +extern crate wasmi; +extern crate hashdb; +extern crate plain_hasher; +#[cfg(feature = "std")] +extern crate patricia_trie; +#[cfg(feature = "std")] +extern crate elastic_array; + +extern crate substrate_runtime_std as rstd; + +#[cfg(test)] +extern crate substrate_serializer; + +#[cfg(test)] +extern crate heapsize; + +#[cfg(test)] +#[macro_use] +extern crate pretty_assertions; + +#[macro_export] +macro_rules! map { + ($( $name:expr => $value:expr ),*) => ( + vec![ $( ( $name, $value ) ),* ].into_iter().collect() + ) +} + +use rstd::prelude::*; +use rstd::ops::Deref; + +#[cfg(feature = "std")] +pub mod bytes; +#[cfg(feature = "std")] +pub mod hashing; +#[cfg(feature = "std")] +pub use hashing::{blake2_256, twox_128, twox_256}; +#[cfg(feature = "std")] +pub mod hexdisplay; + +#[cfg(feature = "std")] +pub mod ed25519; +pub mod u32_trait; + +pub mod hash; +mod hasher; +pub mod sandbox; +pub mod storage; +pub mod uint; +mod authority_id; +#[cfg(feature = "std")] +mod rlp_codec; + +#[cfg(test)] +mod tests; + +pub use self::hash::{H160, H256, H512}; +pub use self::uint::U256; +pub use authority_id::AuthorityId; + +// Switch back to Blake after PoC-3 is out +// pub use self::hasher::blake::BlakeHasher; +pub use self::hasher::blake2::Blake2Hasher; + +#[cfg(feature = "std")] +pub use self::rlp_codec::RlpCodec; + +/// A 512-bit value interpreted as a signature. +pub type Signature = hash::H512; + +/// Hex-serialised shim for `Vec`. +#[derive(PartialEq, Eq, Clone)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug, Hash, PartialOrd, Ord))] +pub struct Bytes(#[cfg_attr(feature = "std", serde(with="bytes"))] pub Vec); + +impl From> for Bytes { + fn from(s: Vec) -> Self { Bytes(s) } +} + +impl Deref for Bytes { + type Target = [u8]; + fn deref(&self) -> &[u8] { &self.0[..] } +} diff --git a/core/primitives/src/rlp_codec.rs b/core/primitives/src/rlp_codec.rs new file mode 100644 index 0000000000000..bf1ae978b3d12 --- /dev/null +++ b/core/primitives/src/rlp_codec.rs @@ -0,0 +1,123 @@ +// Copyright 2018 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 . + +//! Polkadot Blake2b (trie) NodeCodec implementation + +use elastic_array::{ElasticArray1024, ElasticArray128}; +use hashdb::Hasher; +use rlp::{DecoderError, RlpStream, Rlp, Prototype}; +use core::marker::PhantomData; +use patricia_trie::{NibbleSlice, NodeCodec, node::Node, ChildReference}; + +use hash::H256; +use Blake2Hasher; + +/// Concrete implementation of a `NodeCodec` with Rlp encoding, generic over the `Hasher` +pub struct RlpNodeCodec {mark: PhantomData} + +/// Convenience type for a Blake2_256/Rlp flavoured NodeCodec +pub type RlpCodec = RlpNodeCodec; + +impl NodeCodec for RlpCodec { + type Error = DecoderError; + const HASHED_NULL_NODE : H256 = H256( [0x45, 0xb0, 0xcf, 0xc2, 0x20, 0xce, 0xec, 0x5b, 0x7c, 0x1c, 0x62, 0xc4, 0xd4, 0x19, 0x3d, 0x38, 0xe4, 0xeb, 0xa4, 0x8e, 0x88, 0x15, 0x72, 0x9c, 0xe7, 0x5f, 0x9c, 0xa, 0xb0, 0xe4, 0xc1, 0xc0] ); + fn decode(data: &[u8]) -> ::std::result::Result { + let r = Rlp::new(data); + match r.prototype()? { + // either leaf or extension - decode first item with NibbleSlice::??? + // and use is_leaf return to figure out which. + // if leaf, second item is a value (is_data()) + // if extension, second item is a node (either SHA3 to be looked up and + // fed back into this function or inline RLP which can be fed back into this function). + Prototype::List(2) => match NibbleSlice::from_encoded(r.at(0)?.data()?) { + (slice, true) => Ok(Node::Leaf(slice, r.at(1)?.data()?)), + (slice, false) => Ok(Node::Extension(slice, r.at(1)?.as_raw())), + }, + // branch - first 16 are nodes, 17th is a value (or empty). + Prototype::List(17) => { + let mut nodes = [&[] as &[u8]; 16]; + for i in 0..16 { + nodes[i] = r.at(i)?.as_raw(); + } + Ok(Node::Branch(nodes, if r.at(16)?.is_empty() { None } else { Some(r.at(16)?.data()?) })) + }, + // an empty branch index. + Prototype::Data(0) => Ok(Node::Empty), + // something went wrong. + _ => Err(DecoderError::Custom("Rlp is not valid.")) + } + } + fn try_decode_hash(data: &[u8]) -> Option<::Out> { + let r = Rlp::new(data); + if r.is_data() && r.size() == Blake2Hasher::LENGTH { + Some(r.as_val().expect("Hash is the correct size; qed")) + } else { + None + } + } + fn is_empty_node(data: &[u8]) -> bool { + Rlp::new(data).is_empty() + } + fn empty_node() -> ElasticArray1024 { + let mut stream = RlpStream::new(); + stream.append_empty_data(); + stream.drain() + } + + fn leaf_node(partial: &[u8], value: &[u8]) -> ElasticArray1024 { + let mut stream = RlpStream::new_list(2); + stream.append(&partial); + stream.append(&value); + stream.drain() + } + + fn ext_node(partial: &[u8], child_ref: ChildReference<::Out>) -> ElasticArray1024 { + let mut stream = RlpStream::new_list(2); + stream.append(&partial); + match child_ref { + ChildReference::Hash(h) => stream.append(&h), + ChildReference::Inline(inline_data, len) => { + let bytes = &AsRef::<[u8]>::as_ref(&inline_data)[..len]; + stream.append_raw(bytes, 1) + }, + }; + stream.drain() + } + + fn branch_node(children: I, value: Option>) -> ElasticArray1024 + where I: IntoIterator::Out>>> + { + let mut stream = RlpStream::new_list(17); + for child_ref in children { + match child_ref { + Some(c) => match c { + ChildReference::Hash(h) => stream.append(&h), + ChildReference::Inline(inline_data, len) => { + let bytes = &AsRef::<[u8]>::as_ref(&inline_data)[..len]; + stream.append_raw(bytes, 1) + }, + }, + None => stream.append_empty_data() + }; + } + if let Some(value) = value { + stream.append(&&*value); + } else { + stream.append_empty_data(); + } + stream.drain() + } +} diff --git a/core/primitives/src/sandbox.rs b/core/primitives/src/sandbox.rs new file mode 100644 index 0000000000000..2e3144b24ff25 --- /dev/null +++ b/core/primitives/src/sandbox.rs @@ -0,0 +1,221 @@ +// Copyright 2018 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 . + +//! Definition of a sandbox environment. + +#[cfg(test)] +use codec::Encode; +use rstd::vec::Vec; + +/// Error error that can be returned from host function. +#[derive(Encode, Decode)] +#[cfg_attr(feature = "std", derive(Debug))] +pub struct HostError; + +/// Representation of a typed wasm value. +#[derive(Clone, Copy, PartialEq, Encode, Decode)] +#[cfg_attr(feature = "std", derive(Debug))] +pub enum TypedValue { + /// Value of 32-bit signed or unsigned integer. + #[codec(index = "1")] + I32(i32), + + /// Value of 64-bit signed or unsigned integer. + #[codec(index = "2")] + I64(i64), + + /// Value of 32-bit IEEE 754-2008 floating point number represented as a bit pattern. + #[codec(index = "3")] + F32(i32), + + /// Value of 64-bit IEEE 754-2008 floating point number represented as a bit pattern. + #[codec(index = "4")] + F64(i64), +} + +impl TypedValue { + /// Returns `Some` if this value of type `I32`. + pub fn as_i32(&self) -> Option { + match *self { + TypedValue::I32(v) => Some(v), + _ => None, + } + } +} + +#[cfg(feature = "std")] +impl From<::wasmi::RuntimeValue> for TypedValue { + fn from(val: ::wasmi::RuntimeValue) -> TypedValue { + use ::wasmi::RuntimeValue; + match val { + RuntimeValue::I32(v) => TypedValue::I32(v), + RuntimeValue::I64(v) => TypedValue::I64(v), + RuntimeValue::F32(v) => TypedValue::F32(v.to_bits() as i32), + RuntimeValue::F64(v) => TypedValue::F64(v.to_bits() as i64), + } + } +} + +#[cfg(feature = "std")] +impl From for ::wasmi::RuntimeValue { + fn from(val: TypedValue) -> ::wasmi::RuntimeValue { + use ::wasmi::RuntimeValue; + use ::wasmi::nan_preserving_float::{F32, F64}; + match val { + TypedValue::I32(v) => RuntimeValue::I32(v), + TypedValue::I64(v) => RuntimeValue::I64(v), + TypedValue::F32(v_bits) => RuntimeValue::F32(F32::from_bits(v_bits as u32)), + TypedValue::F64(v_bits) => RuntimeValue::F64(F64::from_bits(v_bits as u64)), + } + } +} + +/// Typed value that can be returned from a function. +/// +/// Basically a `TypedValue` plus `Unit`, for functions which return nothing. +#[derive(Clone, Copy, PartialEq, Encode, Decode)] +#[cfg_attr(feature = "std", derive(Debug))] +pub enum ReturnValue { + /// For returning nothing. + Unit, + /// For returning some concrete value. + Value(TypedValue), +} + +impl From for ReturnValue { + fn from(v: TypedValue) -> ReturnValue { + ReturnValue::Value(v) + } +} + +impl ReturnValue { + /// Maximum number of bytes `ReturnValue` might occupy when serialized with + /// `Codec`. + /// + /// Breakdown: + /// 1 byte for encoding unit/value variant + /// 1 byte for encoding value type + /// 8 bytes for encoding the biggest value types available in wasm: f64, i64. + pub const ENCODED_MAX_SIZE: usize = 10; +} + +#[test] +fn return_value_encoded_max_size() { + let encoded = ReturnValue::Value(TypedValue::I64(-1)).encode(); + assert_eq!(encoded.len(), ReturnValue::ENCODED_MAX_SIZE); +} + +/// Describes an entity to define or import into the environment. +#[derive(Clone, PartialEq, Eq, Encode, Decode)] +#[cfg_attr(feature = "std", derive(Debug))] +pub enum ExternEntity { + /// Function that is specified by an index in a default table of + /// a module that creates the sandbox. + #[codec(index = "1")] + Function(u32), + + /// Linear memory that is specified by some identifier returned by sandbox + /// module upon creation new sandboxed memory. + #[codec(index = "2")] + Memory(u32), +} + +/// An entry in a environment definition table. +/// +/// Each entry has a two-level name and description of an entity +/// being defined. +#[derive(Clone, PartialEq, Eq, Encode, Decode)] +#[cfg_attr(feature = "std", derive(Debug))] +pub struct Entry { + /// Module name of which corresponding entity being defined. + pub module_name: Vec, + /// Field name in which corresponding entity being defined. + pub field_name: Vec, + /// External entity being defined. + pub entity: ExternEntity, +} + +/// Definition of runtime that could be used by sandboxed code. +#[derive(Clone, PartialEq, Eq, Encode, Decode)] +#[cfg_attr(feature = "std", derive(Debug))] +pub struct EnvironmentDefinition { + /// Vector of all entries in the environment definition. + pub entries: Vec, +} + +/// Constant for specifying no limit when creating a sandboxed +/// memory instance. For FFI purposes. +pub const MEM_UNLIMITED: u32 = -1i32 as u32; + +/// No error happened. +/// +/// For FFI purposes. +pub const ERR_OK: u32 = 0; + +/// Validation or instantiation error occured when creating new +/// sandboxed module instance. +/// +/// For FFI purposes. +pub const ERR_MODULE: u32 = -1i32 as u32; + +/// Out-of-bounds access attempted with memory or table. +/// +/// For FFI purposes. +pub const ERR_OUT_OF_BOUNDS: u32 = -2i32 as u32; + +/// Execution error occurred (typically trap). +/// +/// For FFI purposes. +pub const ERR_EXECUTION: u32 = -3i32 as u32; + +#[cfg(test)] +mod tests { + use super::*; + use std::fmt; + use codec::Codec; + + fn roundtrip(s: S) { + let encoded = s.encode(); + assert_eq!(S::decode(&mut &encoded[..]).unwrap(), s); + } + + #[test] + fn env_def_roundtrip() { + roundtrip(EnvironmentDefinition { + entries: vec![], + }); + + roundtrip(EnvironmentDefinition { + entries: vec![ + Entry { + module_name: b"kernel"[..].into(), + field_name: b"memory"[..].into(), + entity: ExternEntity::Memory(1337), + }, + ], + }); + + roundtrip(EnvironmentDefinition { + entries: vec![ + Entry { + module_name: b"env"[..].into(), + field_name: b"abort"[..].into(), + entity: ExternEntity::Function(228), + }, + ], + }); + } +} diff --git a/core/primitives/src/storage.rs b/core/primitives/src/storage.rs new file mode 100644 index 0000000000000..55d62f6a81356 --- /dev/null +++ b/core/primitives/src/storage.rs @@ -0,0 +1,44 @@ +// Copyright 2017 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 . + +//! Contract execution data. + +#[cfg(feature = "std")] +use bytes; +use rstd::vec::Vec; + +/// Contract storage key. +#[derive(PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug, Hash, PartialOrd, Ord, Clone))] +pub struct StorageKey(#[cfg_attr(feature = "std", serde(with="bytes"))] pub Vec); + +/// Contract storage entry data. +#[derive(PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug, Hash, PartialOrd, Ord, Clone))] +pub struct StorageData(#[cfg_attr(feature = "std", serde(with="bytes"))] pub Vec); + +/// Storage change set +#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug, PartialEq, Eq))] +pub struct StorageChangeSet { + /// Block hash + pub block: Hash, + /// A list of changes + pub changes: Vec<( + StorageKey, + Option, + )>, +} + diff --git a/core/primitives/src/tests.rs b/core/primitives/src/tests.rs new file mode 100644 index 0000000000000..c650104394505 --- /dev/null +++ b/core/primitives/src/tests.rs @@ -0,0 +1,17 @@ +// Copyright 2017 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. diff --git a/core/primitives/src/u32_trait.rs b/core/primitives/src/u32_trait.rs new file mode 100644 index 0000000000000..5fba6f8e04de6 --- /dev/null +++ b/core/primitives/src/u32_trait.rs @@ -0,0 +1,89 @@ +// Copyright 2017 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 u32 trait with "values" as impl'd types. + +/// A u32 value, wrapped in a trait because we don't yet have const generics. +pub trait Value { + /// The actual value represented by the impl'ing type. + const VALUE: u32; +} +/// Type representing the value 0 for the `Value` trait. +pub struct _0; impl Value for _0 { const VALUE: u32 = 0; } +/// Type representing the value 1 for the `Value` trait. +pub struct _1; impl Value for _1 { const VALUE: u32 = 1; } +/// Type representing the value 2 for the `Value` trait. +pub struct _2; impl Value for _2 { const VALUE: u32 = 2; } +/// Type representing the value 3 for the `Value` trait. +pub struct _3; impl Value for _3 { const VALUE: u32 = 3; } +/// Type representing the value 4 for the `Value` trait. +pub struct _4; impl Value for _4 { const VALUE: u32 = 4; } +/// Type representing the value 5 for the `Value` trait. +pub struct _5; impl Value for _5 { const VALUE: u32 = 5; } +/// Type representing the value 6 for the `Value` trait. +pub struct _6; impl Value for _6 { const VALUE: u32 = 6; } +/// Type representing the value 7 for the `Value` trait. +pub struct _7; impl Value for _7 { const VALUE: u32 = 7; } +/// Type representing the value 8 for the `Value` trait. +pub struct _8; impl Value for _8 { const VALUE: u32 = 8; } +/// Type representing the value 9 for the `Value` trait. +pub struct _9; impl Value for _9 { const VALUE: u32 = 9; } +/// Type representing the value 10 for the `Value` trait. +pub struct _10; impl Value for _10 { const VALUE: u32 = 10; } +/// Type representing the value 11 for the `Value` trait. +pub struct _11; impl Value for _11 { const VALUE: u32 = 11; } +/// Type representing the value 12 for the `Value` trait. +pub struct _12; impl Value for _12 { const VALUE: u32 = 12; } +/// Type representing the value 13 for the `Value` trait. +pub struct _13; impl Value for _13 { const VALUE: u32 = 13; } +/// Type representing the value 14 for the `Value` trait. +pub struct _14; impl Value for _14 { const VALUE: u32 = 14; } +/// Type representing the value 15 for the `Value` trait. +pub struct _15; impl Value for _15 { const VALUE: u32 = 15; } +/// Type representing the value 16 for the `Value` trait. +pub struct _16; impl Value for _16 { const VALUE: u32 = 16; } +/// Type representing the value 24 for the `Value` trait. +pub struct _24; impl Value for _24 { const VALUE: u32 = 24; } +/// Type representing the value 32 for the `Value` trait. +pub struct _32; impl Value for _32 { const VALUE: u32 = 32; } +/// Type representing the value 40 for the `Value` trait. +pub struct _40; impl Value for _40 { const VALUE: u32 = 40; } +/// Type representing the value 48 for the `Value` trait. +pub struct _48; impl Value for _48 { const VALUE: u32 = 48; } +/// Type representing the value 56 for the `Value` trait. +pub struct _56; impl Value for _56 { const VALUE: u32 = 56; } +/// Type representing the value 64 for the `Value` trait. +pub struct _64; impl Value for _64 { const VALUE: u32 = 64; } +/// Type representing the value 80 for the `Value` trait. +pub struct _80; impl Value for _80 { const VALUE: u32 = 80; } +/// Type representing the value 96 for the `Value` trait. +pub struct _96; impl Value for _96 { const VALUE: u32 = 96; } +/// Type representing the value 112 for the `Value` trait. +pub struct _112; impl Value for _112 { const VALUE: u32 = 112; } +/// Type representing the value 128 for the `Value` trait. +pub struct _128; impl Value for _128 { const VALUE: u32 = 128; } +/// Type representing the value 160 for the `Value` trait. +pub struct _160; impl Value for _160 { const VALUE: u32 = 160; } +/// Type representing the value 192 for the `Value` trait. +pub struct _192; impl Value for _192 { const VALUE: u32 = 192; } +/// Type representing the value 224 for the `Value` trait. +pub struct _224; impl Value for _224 { const VALUE: u32 = 224; } +/// Type representing the value 256 for the `Value` trait. +pub struct _256; impl Value for _256 { const VALUE: u32 = 256; } +/// Type representing the value 384 for the `Value` trait. +pub struct _384; impl Value for _384 { const VALUE: u32 = 384; } +/// Type representing the value 512 for the `Value` trait. +pub struct _512; impl Value for _512 { const VALUE: u32 = 512; } diff --git a/core/primitives/src/uint.rs b/core/primitives/src/uint.rs new file mode 100644 index 0000000000000..af3278ef01809 --- /dev/null +++ b/core/primitives/src/uint.rs @@ -0,0 +1,99 @@ +// Copyright 2017 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 unsigned fixed-size integer. + +#[cfg(feature = "std")] +use serde::{Serialize, Serializer, Deserialize, Deserializer}; + +#[cfg(feature = "std")] +use bytes; + +macro_rules! impl_serde { + ($name: ident, $len: expr) => { + #[cfg(feature = "std")] + impl Serialize for $name { + fn serialize(&self, serializer: S) -> Result where S: Serializer { + let mut bytes = [0u8; $len * 8]; + self.to_big_endian(&mut bytes); + bytes::serialize_uint(&bytes, serializer) + } + } + + #[cfg(feature = "std")] + impl<'de> Deserialize<'de> for $name { + fn deserialize(deserializer: D) -> Result where D: Deserializer<'de> { + bytes::deserialize_check_len(deserializer, bytes::ExpectedLen::Between(0, $len * 8)) + .map(|x| (&*x).into()) + } + } + } +} + +construct_uint!(U256, 4); +impl_serde!(U256, 4); + +#[cfg(test)] +mod tests { + use super::*; + use substrate_serializer as ser; + + macro_rules! test { + ($name: ident, $test_name: ident) => { + #[test] + fn $test_name() { + let tests = vec![ + ($name::from(0), "0x0"), + ($name::from(1), "0x1"), + ($name::from(2), "0x2"), + ($name::from(10), "0xa"), + ($name::from(15), "0xf"), + ($name::from(15), "0xf"), + ($name::from(16), "0x10"), + ($name::from(1_000), "0x3e8"), + ($name::from(100_000), "0x186a0"), + ($name::from(u64::max_value()), "0xffffffffffffffff"), + ($name::from(u64::max_value()) + $name::from(1), "0x10000000000000000"), + ]; + + for (number, expected) in tests { + assert_eq!(format!("{:?}", expected), ser::to_string_pretty(&number)); + assert_eq!(number, ser::from_str(&format!("{:?}", expected)).unwrap()); + } + + // Invalid examples + assert!(ser::from_str::<$name>("\"0x\"").unwrap_err().is_data()); + assert!(ser::from_str::<$name>("\"0xg\"").unwrap_err().is_data()); + assert!(ser::from_str::<$name>("\"\"").unwrap_err().is_data()); + assert!(ser::from_str::<$name>("\"10\"").unwrap_err().is_data()); + assert!(ser::from_str::<$name>("\"0\"").unwrap_err().is_data()); + } + } + } + + test!(U256, test_u256); + + #[test] + fn test_large_values() { + assert_eq!( + ser::to_string_pretty(&!U256::zero()), + "\"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\"" + ); + assert!( + ser::from_str::("\"0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\"").unwrap_err().is_data() + ); + } +} diff --git a/core/pwasm-alloc/Cargo.lock b/core/pwasm-alloc/Cargo.lock new file mode 100644 index 0000000000000..56aacd92e4237 --- /dev/null +++ b/core/pwasm-alloc/Cargo.lock @@ -0,0 +1,37 @@ +[[package]] +name = "pwasm-alloc" +version = "0.1.0" +dependencies = [ + "pwasm-libc 0.1.0", + "rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pwasm-libc" +version = "0.1.0" + +[[package]] +name = "rustc_version" +version = "0.2.2" +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" + +[metadata] +"checksum rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a54aa04a10c68c1c4eacb4337fd883b435997ede17a9385784b990777686b09a" +"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" diff --git a/core/pwasm-alloc/Cargo.toml b/core/pwasm-alloc/Cargo.toml new file mode 100644 index 0000000000000..d5dbe87e6adfe --- /dev/null +++ b/core/pwasm-alloc/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "pwasm-alloc" +version = "0.1.0" +authors = ["Nikolay Volf "] +license = "MIT/Apache-2.0" +readme = "README.md" +repository = "https://github.com/paritytech/pwasm-std" +homepage = "https://github.com/paritytech/pwasm-std" +documentation = "https://paritytech.github.io/pwasm-std/pwasm_std/" +description = "Parity WebAssembly standard library internal allocator" +keywords = ["wasm", "parity", "webassembly", "blockchain"] +categories = ["no-std", "embedded"] +build = "build.rs" + +[dependencies] +pwasm-libc = { path = "../pwasm-libc", version = "0.1" } + +[build-dependencies] +rustc_version = "0.2" + +[features] +strict = [] +nightly = [] diff --git a/core/pwasm-alloc/README.adoc b/core/pwasm-alloc/README.adoc new file mode 100644 index 0000000000000..5ddc97ea1b901 --- /dev/null +++ b/core/pwasm-alloc/README.adoc @@ -0,0 +1,25 @@ + += Pwasm-alloc + +Parity WASM contracts standard library libc bindings. + +See https://paritytech.github.io/pwasm-std/pwasm_alloc/ for the documentation. + +== License + +`pwasm_alloc` is primarily distributed under the terms of both the MIT +license and the Apache License (Version 2.0), at your choice. + +See LICENSE-APACHE, and LICENSE-MIT for details. + +.Summary +[source, toml] +---- +include::Cargo.toml[lines=2..5] +---- + +.Description +---- +include::src/lib.rs[tag=description] +---- + diff --git a/core/pwasm-alloc/build.rs b/core/pwasm-alloc/build.rs new file mode 100644 index 0000000000000..35eb154f3a69a --- /dev/null +++ b/core/pwasm-alloc/build.rs @@ -0,0 +1,14 @@ +//! Set a nightly feature + +extern crate rustc_version; +use rustc_version::{version, version_meta, Channel}; + +fn main() { + // Assert we haven't travelled back in time + assert!(version().unwrap().major >= 1); + + // Set cfg flags depending on release channel + if let Channel::Nightly = version_meta().unwrap().channel { + println!("cargo:rustc-cfg=feature=\"nightly\""); + } +} diff --git a/core/pwasm-alloc/src/lib.rs b/core/pwasm-alloc/src/lib.rs new file mode 100644 index 0000000000000..b08eef6854808 --- /dev/null +++ b/core/pwasm-alloc/src/lib.rs @@ -0,0 +1,34 @@ +#![warn(missing_docs)] +#![cfg_attr(feature = "strict", deny(warnings))] +#![no_std] +#![crate_type = "rlib"] + +// tag::description[] +//! Custom allocator crate for wasm +// end::description[] + +/// Wasm allocator +pub struct WasmAllocator; + +#[cfg(feature = "nightly")] +#[global_allocator] +static ALLOCATOR: WasmAllocator = WasmAllocator; + +#[cfg(feature = "nightly")] +mod __impl { + extern crate pwasm_libc; + + use core::alloc::{GlobalAlloc, Layout}; + + use super::WasmAllocator; + + unsafe impl GlobalAlloc for WasmAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + pwasm_libc::malloc(layout.size()) as *mut u8 + } + + unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { + pwasm_libc::free(ptr as *mut u8) + } + } +} diff --git a/core/pwasm-libc/Cargo.lock b/core/pwasm-libc/Cargo.lock new file mode 100644 index 0000000000000..6cc9d84c09255 --- /dev/null +++ b/core/pwasm-libc/Cargo.lock @@ -0,0 +1,4 @@ +[[package]] +name = "pwasm-libc" +version = "0.1.0" + diff --git a/core/pwasm-libc/Cargo.toml b/core/pwasm-libc/Cargo.toml new file mode 100644 index 0000000000000..d3ff1f1f320e6 --- /dev/null +++ b/core/pwasm-libc/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "pwasm-libc" +version = "0.1.0" +authors = ["Sergey Pepyakin "] +license = "MIT/Apache-2.0" +readme = "README.md" +repository = "https://github.com/paritytech/pwasm-std" +homepage = "https://github.com/paritytech/pwasm-std" +documentation = "https://paritytech.github.io/pwasm-std/pwasm_std/" +description = "Parity WebAssembly standard library libc bindings" +keywords = ["wasm", "parity", "webassembly", "blockchain"] +categories = ["no-std", "embedded"] + +[features] +strict = [] diff --git a/core/pwasm-libc/README.adoc b/core/pwasm-libc/README.adoc new file mode 100644 index 0000000000000..e1cba6632f1df --- /dev/null +++ b/core/pwasm-libc/README.adoc @@ -0,0 +1,24 @@ + += Pwasm-libc + +Parity WASM contracts standard library libc bindings + +https://paritytech.github.io/pwasm-std/pwasm_libc/[Documentation] + +== License + +`pwasm-libc` is primarily distributed under the terms of both the MIT +license and the Apache License (Version 2.0), at your choice. + +See LICENSE-APACHE, and LICENSE-MIT for details. + +.Summary +[source, toml] +---- +include::Cargo.toml[lines=2..5] +---- + +.Description +---- +include::src/lib.rs[tag=description] +---- diff --git a/core/pwasm-libc/src/lib.rs b/core/pwasm-libc/src/lib.rs new file mode 100644 index 0000000000000..6b97ff378890b --- /dev/null +++ b/core/pwasm-libc/src/lib.rs @@ -0,0 +1,55 @@ +#![warn(missing_docs)] +#![cfg_attr(feature = "strict", deny(warnings))] +#![no_std] + +// tag::description[] +//! libc externs crate +// end::description[] + +extern "C" { + fn ext_memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8; + fn ext_memmove(dest: *mut u8, src: *const u8, n: usize) -> *mut u8; + fn ext_memset(dest: *mut u8, c: i32, n: usize) -> *mut u8; + fn ext_memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32; + fn ext_malloc(size: usize) -> *mut u8; + fn ext_free(ptr: *mut u8); +} + +// Declaring these function here prevents Emscripten from including it's own verisons +// into final binary. + +/// memcpy extern +#[no_mangle] +pub unsafe extern "C" fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 { + ext_memcpy(dest, src, n) +} + +/// memcmp extern +#[no_mangle] +pub unsafe extern "C" fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32 { + ext_memcmp(s1, s2, n) +} + +/// memmove extern +#[no_mangle] +pub unsafe extern "C" fn memmove(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 { + ext_memmove(dest, src, n) +} + +/// memset extern +#[no_mangle] +pub unsafe extern "C" fn memset(dest: *mut u8, c: i32, n: usize) -> *mut u8 { + ext_memset(dest, c, n) +} + +/// malloc extern +#[no_mangle] +pub unsafe extern "C" fn malloc(size: usize) -> *mut u8 { + ext_malloc(size) +} + +/// free extern +#[no_mangle] +pub unsafe extern "C" fn free(ptr: *mut u8) { + ext_free(ptr); +} diff --git a/core/rpc-servers/Cargo.toml b/core/rpc-servers/Cargo.toml new file mode 100644 index 0000000000000..ffbd900357b9b --- /dev/null +++ b/core/rpc-servers/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "substrate-rpc-servers" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git" } +jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git" } +jsonrpc-pubsub = { git = "https://github.com/paritytech/jsonrpc.git" } +jsonrpc-ws-server = { git = "https://github.com/paritytech/jsonrpc.git" } +log = "0.3" +serde = "1.0" +substrate-rpc = { path = "../rpc", version = "0.1" } +substrate-runtime-primitives = { path = "../../runtime/primitives" } diff --git a/core/rpc-servers/README.adoc b/core/rpc-servers/README.adoc new file mode 100644 index 0000000000000..18840be421fdd --- /dev/null +++ b/core/rpc-servers/README.adoc @@ -0,0 +1,14 @@ + += RPC Server + +.Summary +[source, toml] +---- +include::Cargo.toml[lines=2..5] +---- + +.Description +---- +include::src/lib.rs[tag=description] +---- + diff --git a/core/rpc-servers/src/lib.rs b/core/rpc-servers/src/lib.rs new file mode 100644 index 0000000000000..6139d32aec963 --- /dev/null +++ b/core/rpc-servers/src/lib.rs @@ -0,0 +1,93 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +// tag::description[] +//! Substrate RPC servers. +// end::description[] + +#[warn(missing_docs)] + +pub extern crate substrate_rpc as apis; + +extern crate jsonrpc_core as rpc; +extern crate jsonrpc_http_server as http; +extern crate jsonrpc_pubsub as pubsub; +extern crate jsonrpc_ws_server as ws; +extern crate serde; +extern crate substrate_runtime_primitives; + +#[macro_use] +extern crate log; + +use std::io; +use substrate_runtime_primitives::traits::{Block as BlockT, NumberFor}; + +type Metadata = apis::metadata::Metadata; +type RpcHandler = pubsub::PubSubHandler; +pub type HttpServer = http::Server; +pub type WsServer = ws::Server; + +/// Construct rpc `IoHandler` +pub fn rpc_handler( + state: S, + chain: C, + author: A, + system: Y, +) -> RpcHandler where + Block: BlockT + 'static, + ExHash: Send + Sync + 'static + substrate_runtime_primitives::Serialize + substrate_runtime_primitives::DeserializeOwned, + PendingExtrinsics: serde::Serialize + serde::de::DeserializeOwned + Send + Sync + 'static, + S: apis::state::StateApi, + C: apis::chain::ChainApi, Block::Extrinsic, Metadata=Metadata>, + A: apis::author::AuthorApi, + Y: apis::system::SystemApi, +{ + let mut io = pubsub::PubSubHandler::default(); + io.extend_with(state.to_delegate()); + io.extend_with(chain.to_delegate()); + io.extend_with(author.to_delegate()); + io.extend_with(system.to_delegate()); + io +} + +/// Start HTTP server listening on given address. +pub fn start_http( + addr: &std::net::SocketAddr, + io: RpcHandler, +) -> io::Result { + http::ServerBuilder::new(io) + .threads(4) + .rest_api(http::RestApi::Unsecure) + .cors(http::DomainsValidation::Disabled) + .start_http(addr) +} + +/// Start WS server listening on given address. +pub fn start_ws( + addr: &std::net::SocketAddr, + io: RpcHandler, +) -> io::Result { + ws::ServerBuilder::with_meta_extractor(io, |context: &ws::RequestContext| Metadata::new(context.sender())) + .start(addr) + .map_err(|err| match err { + ws::Error(ws::ErrorKind::Io(io), _) => io, + ws::Error(ws::ErrorKind::ConnectionClosed, _) => io::ErrorKind::BrokenPipe.into(), + ws::Error(e, _) => { + error!("{}", e); + io::ErrorKind::Other.into() + } + }) +} diff --git a/core/rpc/Cargo.toml b/core/rpc/Cargo.toml new file mode 100644 index 0000000000000..5460137dc75f0 --- /dev/null +++ b/core/rpc/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "substrate-rpc" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +error-chain = "0.12" +jsonrpc-core = { git="https://github.com/paritytech/jsonrpc.git" } +jsonrpc-macros = { git="https://github.com/paritytech/jsonrpc.git" } +jsonrpc-pubsub = { git="https://github.com/paritytech/jsonrpc.git" } +log = "0.3" +parking_lot = "0.4" +substrate-codec = { path = "../codec" } +substrate-client = { path = "../client" } +substrate-executor = { path = "../executor" } +substrate-extrinsic-pool = { path = "../extrinsic-pool" } +substrate-primitives = { path = "../primitives" } +substrate-runtime-primitives = { path = "../../runtime/primitives" } +substrate-runtime-version = { path = "../../runtime/version" } +substrate-state-machine = { path = "../state-machine" } +tokio = "0.1.7" +serde_json = "1.0" + +[dev-dependencies] +assert_matches = "1.1" +substrate-test-client = { path = "../test-client" } +rustc-hex = "2.0" diff --git a/core/rpc/README.adoc b/core/rpc/README.adoc new file mode 100644 index 0000000000000..5e4db4909976a --- /dev/null +++ b/core/rpc/README.adoc @@ -0,0 +1,13 @@ + += RPC + +.Summary +[source, toml] +---- +include::Cargo.toml[lines=2..5] +---- + +.Description +---- +include::src/lib.rs[tag=description] +---- diff --git a/core/rpc/src/author/error.rs b/core/rpc/src/author/error.rs new file mode 100644 index 0000000000000..e831770056271 --- /dev/null +++ b/core/rpc/src/author/error.rs @@ -0,0 +1,68 @@ +// Copyright 2017 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 . + +//! Authoring RPC module errors. + +use client; +use extrinsic_pool; +use rpc; + +use errors; + +error_chain! { + links { + Pool(extrinsic_pool::Error, extrinsic_pool::ErrorKind) #[doc = "Pool error"]; + Client(client::error::Error, client::error::ErrorKind) #[doc = "Client error"]; + } + errors { + /// Not implemented yet + Unimplemented { + description("not yet implemented"), + display("Method Not Implemented"), + } + /// Incorrect extrinsic format. + BadFormat { + description("bad format"), + display("Invalid extrinsic format"), + } + /// Verification error + Verification(e: Box<::std::error::Error + Send>) { + description("extrinsic verification error"), + display("Extrinsic verification error: {}", e.description()), + } + } +} + +const ERROR: i64 = 1000; + +impl From for rpc::Error { + fn from(e: Error) -> Self { + match e { + Error(ErrorKind::Unimplemented, _) => errors::unimplemented(), + Error(ErrorKind::BadFormat, _) => rpc::Error { + code: rpc::ErrorCode::ServerError(ERROR + 1), + message: "Extrinsic has invalid format.".into(), + data: None, + }, + Error(ErrorKind::Verification(e), _) => rpc::Error { + code: rpc::ErrorCode::ServerError(ERROR + 2), + message: e.description().into(), + data: Some(format!("{:?}", e).into()), + }, + e => errors::internal(e), + } + } +} diff --git a/core/rpc/src/author/mod.rs b/core/rpc/src/author/mod.rs new file mode 100644 index 0000000000000..5ba5140c5ee52 --- /dev/null +++ b/core/rpc/src/author/mod.rs @@ -0,0 +1,162 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Substrate block-author/full-node API. + +use std::sync::Arc; + +use client::{self, Client}; +use codec::Decode; +use extrinsic_pool::{ + Pool, + IntoPoolError, + ChainApi as PoolChainApi, + watcher::Status, + VerifiedTransaction, + AllExtrinsics, + ExHash, + ExtrinsicFor, +}; +use jsonrpc_macros::pubsub; +use jsonrpc_pubsub::SubscriptionId; +use primitives::{Bytes, Blake2Hasher, RlpCodec}; +use rpc::futures::{Sink, Stream, Future}; +use runtime_primitives::{generic, traits}; +use subscriptions::Subscriptions; +use tokio::runtime::TaskExecutor; + +pub mod error; + +#[cfg(test)] +mod tests; + +use self::error::Result; + +build_rpc_trait! { + /// Substrate authoring RPC API + pub trait AuthorApi { + type Metadata; + + /// Submit extrinsic for inclusion in block. + #[rpc(name = "author_submitRichExtrinsic")] + fn submit_rich_extrinsic(&self, Extrinsic) -> Result; + /// Submit hex-encoded extrinsic for inclusion in block. + #[rpc(name = "author_submitExtrinsic")] + fn submit_extrinsic(&self, Bytes) -> Result; + + /// Returns all pending extrinsics, potentially grouped by sender. + #[rpc(name = "author_pendingExtrinsics")] + fn pending_extrinsics(&self) -> Result; + + #[pubsub(name = "author_extrinsicUpdate")] { + /// Submit an extrinsic to watch. + #[rpc(name = "author_submitAndWatchExtrinsic")] + fn watch_extrinsic(&self, Self::Metadata, pubsub::Subscriber>, Bytes); + + /// Unsubscribe from extrinsic watching. + #[rpc(name = "author_unwatchExtrinsic")] + fn unwatch_extrinsic(&self, SubscriptionId) -> Result; + } + + } +} + +/// Authoring API +pub struct Author where + P: PoolChainApi + Sync + Send + 'static, +{ + /// Substrate client + client: Arc::Block>>, + /// Extrinsic pool + pool: Arc>, + /// Subscriptions manager + subscriptions: Subscriptions, +} + +impl Author where + P: PoolChainApi + Sync + Send + 'static, +{ + /// Create new instance of Authoring API. + pub fn new(client: Arc::Block>>, pool: Arc>, executor: TaskExecutor) -> Self { + Author { + client, + pool, + subscriptions: Subscriptions::new(executor), + } + } +} + +impl AuthorApi, ExtrinsicFor

, AllExtrinsics

> for Author where + B: client::backend::Backend<

::Block, Blake2Hasher, RlpCodec> + Send + Sync + 'static, + E: client::CallExecutor<

::Block, Blake2Hasher, RlpCodec> + Send + Sync + 'static, + P: PoolChainApi + Sync + Send + 'static, + P::Error: 'static, +{ + type Metadata = ::metadata::Metadata; + + fn submit_extrinsic(&self, xt: Bytes) -> Result> { + let dxt = Decode::decode(&mut &xt[..]).ok_or(error::Error::from(error::ErrorKind::BadFormat))?; + self.submit_rich_extrinsic(dxt) + } + + fn submit_rich_extrinsic(&self, xt: <

::Block as traits::Block>::Extrinsic) -> Result> { + let best_block_hash = self.client.info()?.chain.best_hash; + self.pool + .submit_one(&generic::BlockId::hash(best_block_hash), xt) + .map_err(|e| e.into_pool_error() + .map(Into::into) + .unwrap_or_else(|e| error::ErrorKind::Verification(Box::new(e)).into()) + ) + .map(|ex| ex.hash().clone()) + } + + fn pending_extrinsics(&self) -> Result> { + Ok(self.pool.all()) + } + + fn watch_extrinsic(&self, _metadata: Self::Metadata, subscriber: pubsub::Subscriber>>, xt: Bytes) { + let submit = || -> Result<_> { + let best_block_hash = self.client.info()?.chain.best_hash; + let dxt = <

::Block as traits::Block>::Extrinsic::decode(&mut &xt[..]).ok_or(error::Error::from(error::ErrorKind::BadFormat))?; + self.pool + .submit_and_watch(&generic::BlockId::hash(best_block_hash), dxt) + .map_err(|e| e.into_pool_error() + .map(Into::into) + .unwrap_or_else(|e| error::ErrorKind::Verification(Box::new(e)).into()) + ) + }; + + let watcher = match submit() { + Ok(watcher) => watcher, + Err(err) => { + // reject the subscriber (ignore errors - we don't care if subscriber is no longer there). + let _ = subscriber.reject(err.into()); + return; + }, + }; + + self.subscriptions.add(subscriber, move |sink| { + sink + .sink_map_err(|e| warn!("Error sending notifications: {:?}", e)) + .send_all(watcher.into_stream().map(Ok)) + .map(|_| ()) + }) + } + + fn unwatch_extrinsic(&self, id: SubscriptionId) -> Result { + Ok(self.subscriptions.cancel(id)) + } +} diff --git a/core/rpc/src/author/tests.rs b/core/rpc/src/author/tests.rs new file mode 100644 index 0000000000000..e5cdf759da4fb --- /dev/null +++ b/core/rpc/src/author/tests.rs @@ -0,0 +1,178 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +use super::*; + +use std::{sync::Arc, result::Result}; +use codec::Encode; +use extrinsic_pool::{VerifiedTransaction, scoring, Transaction, ChainApi, Error as PoolError, + Readiness, ExtrinsicFor, VerifiedFor}; +use test_client::runtime::{Block, Extrinsic, Transfer}; +use test_client; +use tokio::runtime; +use runtime_primitives::generic::BlockId; + +#[derive(Clone, Debug)] +pub struct Verified +{ + sender: u64, + hash: u64, +} + +impl VerifiedTransaction for Verified { + type Hash = u64; + type Sender = u64; + + fn hash(&self) -> &Self::Hash { &self.hash } + fn sender(&self) -> &Self::Sender { &self.sender } + fn mem_usage(&self) -> usize { 256 } +} + +struct TestApi; + +impl ChainApi for TestApi { + type Block = Block; + type Hash = u64; + type Sender = u64; + type Error = PoolError; + type VEx = Verified; + type Score = u64; + type Event = (); + type Ready = (); + + fn verify_transaction(&self, _at: &BlockId, uxt: &ExtrinsicFor) -> Result { + Ok(Verified { + sender: uxt.transfer.from[31] as u64, + hash: uxt.transfer.nonce, + }) + } + + fn is_ready(&self, _at: &BlockId, _c: &mut Self::Ready, _xt: &VerifiedFor) -> Readiness { + Readiness::Ready + } + + fn ready(&self) -> Self::Ready { } + + fn compare(old: &VerifiedFor, other: &VerifiedFor) -> ::std::cmp::Ordering { + old.verified.hash().cmp(&other.verified.hash()) + } + + fn choose(_old: &VerifiedFor, _new: &VerifiedFor) -> scoring::Choice { + scoring::Choice::ReplaceOld + } + + fn update_scores(xts: &[Transaction>], scores: &mut [Self::Score], _change: scoring::Change<()>) { + for i in 0..xts.len() { + scores[i] = xts[i].verified.sender + } + } + + fn should_replace(_old: &VerifiedFor, _new: &VerifiedFor) -> scoring::Choice { + scoring::Choice::ReplaceOld + } +} + +type DummyTxPool = Pool; + +fn uxt(sender: u64, hash: u64) -> Extrinsic { + Extrinsic { + signature: Default::default(), + transfer: Transfer { + amount: Default::default(), + nonce: hash, + from: From::from(sender), + to: Default::default(), + } + } +} + +#[test] +fn submit_transaction_should_not_cause_error() { + let runtime = runtime::Runtime::new().unwrap(); + let p = Author { + client: Arc::new(test_client::new()), + pool: Arc::new(DummyTxPool::new(Default::default(), TestApi)), + subscriptions: Subscriptions::new(runtime.executor()), + }; + + assert_matches!( + AuthorApi::submit_extrinsic(&p, uxt(5, 1).encode().into()), + Ok(1) + ); + assert!( + AuthorApi::submit_extrinsic(&p, uxt(5, 1).encode().into()).is_err() + ); +} + +#[test] +fn submit_rich_transaction_should_not_cause_error() { + let runtime = runtime::Runtime::new().unwrap(); + let p = Author { + client: Arc::new(test_client::new()), + pool: Arc::new(DummyTxPool::new(Default::default(), TestApi)), + subscriptions: Subscriptions::new(runtime.executor()), + }; + + assert_matches!( + AuthorApi::submit_rich_extrinsic(&p, uxt(5, 0)), + Ok(0) + ); + assert!( + AuthorApi::submit_rich_extrinsic(&p, uxt(5, 0)).is_err() + ); +} + +#[test] +fn should_watch_extrinsic() { + //given + let mut runtime = runtime::Runtime::new().unwrap(); + let pool = Arc::new(DummyTxPool::new(Default::default(), TestApi)); + let p = Author { + client: Arc::new(test_client::new()), + pool: pool.clone(), + subscriptions: Subscriptions::new(runtime.executor()), + }; + let (subscriber, id_rx, data) = ::jsonrpc_macros::pubsub::Subscriber::new_test("test"); + + // when + p.watch_extrinsic(Default::default(), subscriber, uxt(5, 5).encode().into()); + + // then + assert_eq!(runtime.block_on(id_rx), Ok(Ok(0.into()))); + // check notifications + AuthorApi::submit_rich_extrinsic(&p, uxt(5, 1)).unwrap(); + assert_eq!( + runtime.block_on(data.into_future()).unwrap().0, + Some(r#"{"jsonrpc":"2.0","method":"test","params":{"result":{"usurped":1},"subscription":0}}"#.into()) + ); +} + +#[test] +fn should_return_pending_extrinsics() { + let runtime = runtime::Runtime::new().unwrap(); + let pool = Arc::new(DummyTxPool::new(Default::default(), TestApi)); + let p = Author { + client: Arc::new(test_client::new()), + pool: pool.clone(), + subscriptions: Subscriptions::new(runtime.executor()), + }; + let ex = uxt(5, 1); + AuthorApi::submit_rich_extrinsic(&p, ex.clone()).unwrap(); + assert_matches!( + p.pending_extrinsics(), + Ok(ref expected) if expected.get(&5) == Some(&vec![ex]) + ); +} diff --git a/core/rpc/src/chain/error.rs b/core/rpc/src/chain/error.rs new file mode 100644 index 0000000000000..2c42e8c98956f --- /dev/null +++ b/core/rpc/src/chain/error.rs @@ -0,0 +1,42 @@ +// Copyright 2017 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 client; +use rpc; + +use errors; + +error_chain! { + links { + Client(client::error::Error, client::error::ErrorKind) #[doc = "Client error"]; + } + errors { + /// Not implemented yet + Unimplemented { + description("not yet implemented"), + display("Method Not Implemented"), + } + } +} + +impl From for rpc::Error { + fn from(e: Error) -> Self { + match e { + Error(ErrorKind::Unimplemented, _) => errors::unimplemented(), + e => errors::internal(e), + } + } +} diff --git a/core/rpc/src/chain/mod.rs b/core/rpc/src/chain/mod.rs new file mode 100644 index 0000000000000..86551e04847d0 --- /dev/null +++ b/core/rpc/src/chain/mod.rs @@ -0,0 +1,165 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Substrate blockchain API. + +use std::sync::Arc; + +use client::{self, Client, BlockchainEvents}; +use jsonrpc_macros::{pubsub, Trailing}; +use jsonrpc_pubsub::SubscriptionId; +use rpc::Result as RpcResult; +use rpc::futures::{stream, Future, Sink, Stream}; +use runtime_primitives::generic::{BlockId, SignedBlock}; +use runtime_primitives::traits::{Block as BlockT, Header, NumberFor}; +use runtime_version::RuntimeVersion; +use tokio::runtime::TaskExecutor; +use primitives::{Blake2Hasher, RlpCodec}; + +use subscriptions::Subscriptions; + +mod error; +#[cfg(test)] +mod tests; + +use self::error::Result; + +build_rpc_trait! { + /// Polkadot blockchain API + pub trait ChainApi { + type Metadata; + + /// Get header of a relay chain block. + #[rpc(name = "chain_getHeader")] + fn header(&self, Trailing) -> Result>; + + /// Get header and body of a relay chain block. + #[rpc(name = "chain_getBlock")] + fn block(&self, Trailing) -> Result>>; + + /// Get hash of the n-th block in the canon chain. + /// + /// By default returns latest block hash. + #[rpc(name = "chain_getBlockHash", alias = ["chain_getHead", ])] + fn block_hash(&self, Trailing) -> Result>; + + /// Get the runtime version. + #[rpc(name = "chain_getRuntimeVersion")] + fn runtime_version(&self, Trailing) -> Result; + + #[pubsub(name = "chain_newHead")] { + /// New head subscription + #[rpc(name = "chain_subscribeNewHead", alias = ["subscribe_newHead", ])] + fn subscribe_new_head(&self, Self::Metadata, pubsub::Subscriber

); + + /// Unsubscribe from new head subscription. + #[rpc(name = "chain_unsubscribeNewHead", alias = ["unsubscribe_newHead", ])] + fn unsubscribe_new_head(&self, SubscriptionId) -> RpcResult; + } + } +} + +/// Chain API with subscriptions support. +pub struct Chain { + /// Substrate client. + client: Arc>, + /// Current subscriptions. + subscriptions: Subscriptions, +} + +impl Chain { + /// Create new Chain API RPC handler. + pub fn new(client: Arc>, executor: TaskExecutor) -> Self { + Self { + client, + subscriptions: Subscriptions::new(executor), + } + } +} + +impl Chain where + Block: BlockT + 'static, + B: client::backend::Backend + Send + Sync + 'static, + E: client::CallExecutor + Send + Sync + 'static, +{ + fn unwrap_or_best(&self, hash: Trailing) -> Result { + Ok(match hash.into() { + None => self.client.info()?.chain.best_hash, + Some(hash) => hash, + }) + } +} + +impl ChainApi, Block::Extrinsic> for Chain where + Block: BlockT + 'static, + B: client::backend::Backend + Send + Sync + 'static, + E: client::CallExecutor + Send + Sync + 'static, +{ + type Metadata = ::metadata::Metadata; + + fn header(&self, hash: Trailing) -> Result> { + let hash = self.unwrap_or_best(hash)?; + Ok(self.client.header(&BlockId::Hash(hash))?) + } + + fn block(&self, hash: Trailing) -> Result>> { + let hash = self.unwrap_or_best(hash)?; + Ok(self.client.block(&BlockId::Hash(hash))?) + } + + fn block_hash(&self, number: Trailing>) -> Result> { + Ok(match number.into() { + None => Some(self.client.info()?.chain.best_hash), + Some(number) => self.client.header(&BlockId::number(number))?.map(|h| h.hash()), + }) + } + + fn runtime_version(&self, at: Trailing) -> Result { + let at = self.unwrap_or_best(at)?; + Ok(self.client.runtime_version_at(&BlockId::Hash(at))?) + } + + fn subscribe_new_head(&self, _metadata: Self::Metadata, subscriber: pubsub::Subscriber) { + self.subscriptions.add(subscriber, |sink| { + // send current head right at the start. + let header = self.block_hash(None.into()) + .and_then(|hash| self.header(hash.into())) + .and_then(|header| { + header.ok_or_else(|| self::error::ErrorKind::Unimplemented.into()) + }) + .map_err(Into::into); + + // send further subscriptions + let stream = self.client.import_notification_stream() + .filter(|notification| notification.is_new_best) + .map(|notification| Ok(notification.header)) + .map_err(|e| warn!("Block notification stream error: {:?}", e)); + + sink + .sink_map_err(|e| warn!("Error sending notifications: {:?}", e)) + .send_all( + stream::iter_result(vec![Ok(header)]) + .chain(stream) + ) + // we ignore the resulting Stream (if the first stream is over we are unsubscribed) + .map(|_| ()) + }); + } + + fn unsubscribe_new_head(&self, id: SubscriptionId) -> RpcResult { + Ok(self.subscriptions.cancel(id)) + } +} diff --git a/core/rpc/src/chain/tests.rs b/core/rpc/src/chain/tests.rs new file mode 100644 index 0000000000000..def410532cd25 --- /dev/null +++ b/core/rpc/src/chain/tests.rs @@ -0,0 +1,207 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +use super::*; +use jsonrpc_macros::pubsub; +use client::BlockOrigin; +use test_client::{self, TestClient}; +use test_client::runtime::{Block, Header}; + +#[test] +fn should_return_header() { + let core = ::tokio::runtime::Runtime::new().unwrap(); + let remote = core.executor(); + + let client = Chain { + client: Arc::new(test_client::new()), + subscriptions: Subscriptions::new(remote), + }; + + assert_matches!( + client.header(Some(client.client.genesis_hash()).into()), + Ok(Some(ref x)) if x == &Header { + parent_hash: 0.into(), + number: 0, + state_root: x.state_root.clone(), + extrinsics_root: "45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0".into(), + digest: Default::default(), + } + ); + + assert_matches!( + client.header(None.into()), + Ok(Some(ref x)) if x == &Header { + parent_hash: 0.into(), + number: 0, + state_root: x.state_root.clone(), + extrinsics_root: "45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0".into(), + digest: Default::default(), + } + ); + + assert_matches!( + client.header(Some(5.into()).into()), + Ok(None) + ); +} + +#[test] +fn should_return_a_block() { + let core = ::tokio::runtime::Runtime::new().unwrap(); + let remote = core.executor(); + + let api = Chain { + client: Arc::new(test_client::new()), + subscriptions: Subscriptions::new(remote), + }; + + let block = api.client.new_block().unwrap().bake().unwrap(); + let block_hash = block.hash(); + api.client.justify_and_import(BlockOrigin::Own, block).unwrap(); + + + // Genesis block is not justified, so we can't query it? + assert_matches!( + api.block(Some(api.client.genesis_hash()).into()), + Ok(None) + ); + + assert_matches!( + api.block(Some(block_hash).into()), + Ok(Some(ref x)) if x.block == Block { + header: Header { + parent_hash: api.client.genesis_hash(), + number: 1, + state_root: x.block.header.state_root.clone(), + extrinsics_root: "45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0".into(), + digest: Default::default(), + }, + extrinsics: vec![], + } + ); + + assert_matches!( + api.block(None.into()), + Ok(Some(ref x)) if x.block == Block { + header: Header { + parent_hash: api.client.genesis_hash(), + number: 1, + state_root: x.block.header.state_root.clone(), + extrinsics_root: "45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0".into(), + digest: Default::default(), + }, + extrinsics: vec![], + } + ); + + assert_matches!( + api.block(Some(5.into()).into()), + Ok(None) + ); +} + +#[test] +fn should_return_block_hash() { + let core = ::tokio::runtime::Runtime::new().unwrap(); + let remote = core.executor(); + + let client = Chain { + client: Arc::new(test_client::new()), + subscriptions: Subscriptions::new(remote), + }; + + assert_matches!( + client.block_hash(None.into()), + Ok(Some(ref x)) if x == &client.client.genesis_hash() + ); + + + assert_matches!( + client.block_hash(Some(0u64).into()), + Ok(Some(ref x)) if x == &client.client.genesis_hash() + ); + + assert_matches!( + client.block_hash(Some(1u64).into()), + Ok(None) + ); + + let block = client.client.new_block().unwrap().bake().unwrap(); + client.client.justify_and_import(BlockOrigin::Own, block.clone()).unwrap(); + + assert_matches!( + client.block_hash(Some(0u64).into()), + Ok(Some(ref x)) if x == &client.client.genesis_hash() + ); + assert_matches!( + client.block_hash(Some(1u64).into()), + Ok(Some(ref x)) if x == &block.hash() + ); + +} + +#[test] +fn should_notify_about_latest_block() { + let mut core = ::tokio::runtime::Runtime::new().unwrap(); + let remote = core.executor(); + let (subscriber, id, transport) = pubsub::Subscriber::new_test("test"); + + { + let api = Chain { + client: Arc::new(test_client::new()), + subscriptions: Subscriptions::new(remote), + }; + + api.subscribe_new_head(Default::default(), subscriber); + + // assert id assigned + assert_eq!(core.block_on(id), Ok(Ok(SubscriptionId::Number(0)))); + + let builder = api.client.new_block().unwrap(); + api.client.justify_and_import(BlockOrigin::Own, builder.bake().unwrap()).unwrap(); + } + + // assert initial head sent. + let (notification, next) = core.block_on(transport.into_future()).unwrap(); + assert!(notification.is_some()); + // assert notification sent to transport + let (notification, next) = core.block_on(next.into_future()).unwrap(); + assert!(notification.is_some()); + // no more notifications on this channel + assert_eq!(core.block_on(next.into_future()).unwrap().0, None); +} + +#[test] +fn should_return_runtime_version() { + let core = ::tokio::runtime::Runtime::new().unwrap(); + let remote = core.executor(); + + let client = Chain { + client: Arc::new(test_client::new()), + subscriptions: Subscriptions::new(remote), + }; + + assert_matches!( + client.runtime_version(None.into()), + Ok(ref ver) if ver == &RuntimeVersion { + spec_name: "test".into(), + impl_name: "parity-test".into(), + authoring_version: 1, + spec_version: 1, + impl_version: 1, + } + ); +} diff --git a/core/rpc/src/errors.rs b/core/rpc/src/errors.rs new file mode 100644 index 0000000000000..a9b9e27a9cbc6 --- /dev/null +++ b/core/rpc/src/errors.rs @@ -0,0 +1,34 @@ +// Copyright 2018 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 rpc; + +pub fn unimplemented() -> rpc::Error { + rpc::Error { + code: rpc::ErrorCode::ServerError(1), + message: "Not implemented yet".into(), + data: None, + } +} + +pub fn internal(e: E) -> rpc::Error { + warn!("Unknown error: {:?}", e); + rpc::Error { + code: rpc::ErrorCode::InternalError, + message: "Unknown error occured".into(), + data: Some(format!("{:?}", e).into()), + } +} diff --git a/core/rpc/src/helpers.rs b/core/rpc/src/helpers.rs new file mode 100644 index 0000000000000..dca1a45db563d --- /dev/null +++ b/core/rpc/src/helpers.rs @@ -0,0 +1,25 @@ +// Copyright 2018 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 . + +/// Unwraps the trailing parameter or falls back with the closure result. +pub fn unwrap_or_else(or_else: F, optional: ::jsonrpc_macros::Trailing) -> Result where + F: FnOnce() -> Result, +{ + match optional.into() { + None => or_else(), + Some(x) => Ok(x), + } +} diff --git a/core/rpc/src/lib.rs b/core/rpc/src/lib.rs new file mode 100644 index 0000000000000..b4e07fc823e80 --- /dev/null +++ b/core/rpc/src/lib.rs @@ -0,0 +1,59 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +// tag::description[] +//! Substrate RPC interfaces. +// end::description[] + +#![warn(missing_docs)] + +extern crate jsonrpc_core as rpc; +extern crate jsonrpc_pubsub; +extern crate parking_lot; +extern crate substrate_codec as codec; +extern crate substrate_client as client; +extern crate substrate_extrinsic_pool as extrinsic_pool; +extern crate substrate_primitives as primitives; +extern crate substrate_runtime_primitives as runtime_primitives; +extern crate substrate_state_machine as state_machine; +extern crate substrate_runtime_version as runtime_version; +extern crate tokio; +extern crate serde_json; + +#[macro_use] +extern crate error_chain; +#[macro_use] +extern crate jsonrpc_macros; +#[macro_use] +extern crate log; + +#[cfg(test)] +#[macro_use] +extern crate assert_matches; +#[cfg(test)] +extern crate substrate_test_client as test_client; +#[cfg(test)] +extern crate rustc_hex; + +mod errors; +mod helpers; +mod subscriptions; + +pub mod author; +pub mod chain; +pub mod metadata; +pub mod state; +pub mod system; diff --git a/core/rpc/src/metadata.rs b/core/rpc/src/metadata.rs new file mode 100644 index 0000000000000..06d19d11d8cec --- /dev/null +++ b/core/rpc/src/metadata.rs @@ -0,0 +1,54 @@ +// Copyright 2017-2018 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! RPC Metadata +use std::sync::Arc; + +use jsonrpc_pubsub::{Session, PubSubMetadata}; +use rpc::futures::sync::mpsc; + +/// RPC Metadata. +/// +/// Manages persistent session for transports that support it +/// and may contain some additional info extracted from specific transports +/// (like remote client IP address, request headers, etc) +#[derive(Default, Clone)] +pub struct Metadata { + session: Option>, +} + +impl ::rpc::Metadata for Metadata {} +impl PubSubMetadata for Metadata { + fn session(&self) -> Option> { + self.session.clone() + } +} + +impl Metadata { + /// Create new `Metadata` with session (Pub/Sub) support. + pub fn new(transport: mpsc::Sender) -> Self { + Metadata { + session: Some(Arc::new(Session::new(transport))), + } + } + + /// Create new `Metadata` for tests. + #[cfg(test)] + pub fn new_test() -> (mpsc::Receiver, Self) { + let (tx, rx) = mpsc::channel(1); + (rx, Self::new(tx)) + } +} diff --git a/core/rpc/src/state/error.rs b/core/rpc/src/state/error.rs new file mode 100644 index 0000000000000..de65e466c08b9 --- /dev/null +++ b/core/rpc/src/state/error.rs @@ -0,0 +1,54 @@ +// Copyright 2017 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 client; +use rpc; + +use errors; + +use serde_json; + +error_chain! { + links { + Client(client::error::Error, client::error::ErrorKind) #[doc = "Client error"]; + } + + foreign_links { + Json(serde_json::Error); + } + + errors { + /// Provided block range couldn't be resolved to a list of blocks. + InvalidBlockRange(from: String, to: String, details: String) { + description("Invalid block range"), + display("Cannot resolve a block range ['{:?}' ... '{:?}]. {}", from, to, details), + } + /// Not implemented yet + Unimplemented { + description("not implemented yet"), + display("Method Not Implemented"), + } + } +} + +impl From for rpc::Error { + fn from(e: Error) -> Self { + match e { + Error(ErrorKind::Unimplemented, _) => errors::unimplemented(), + e => errors::internal(e), + } + } +} diff --git a/core/rpc/src/state/mod.rs b/core/rpc/src/state/mod.rs new file mode 100644 index 0000000000000..e335310af74aa --- /dev/null +++ b/core/rpc/src/state/mod.rs @@ -0,0 +1,277 @@ +// Copyright 2017 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 . + +//! Polkadot state API. + +use std::{ + collections::HashMap, + sync::Arc, +}; + +use client::{self, Client, CallExecutor, BlockchainEvents}; +use jsonrpc_macros::Trailing; +use jsonrpc_macros::pubsub; +use jsonrpc_pubsub::SubscriptionId; +use primitives::hexdisplay::HexDisplay; +use primitives::storage::{StorageKey, StorageData, StorageChangeSet}; +use primitives::{Blake2Hasher, RlpCodec}; +use rpc::Result as RpcResult; +use rpc::futures::{stream, Future, Sink, Stream}; +use runtime_primitives::generic::BlockId; +use runtime_primitives::traits::{Block as BlockT, Header}; +use tokio::runtime::TaskExecutor; +use serde_json; + +use subscriptions::Subscriptions; + +mod error; +#[cfg(test)] +mod tests; + +use self::error::Result; + +build_rpc_trait! { + /// Polkadot state API + pub trait StateApi { + type Metadata; + + /// Call a contract at a block's state. + #[rpc(name = "state_call", alias = ["state_callAt", ])] + fn call(&self, String, Vec, Trailing) -> Result>; + + /// Returns a storage entry at a specific block's state. + #[rpc(name = "state_getStorage", alias = ["state_getStorageAt", ])] + fn storage(&self, StorageKey, Trailing) -> Result>; + + /// Returns the hash of a storage entry at a block's state. + #[rpc(name = "state_getStorageHash", alias = ["state_getStorageHashAt", ])] + fn storage_hash(&self, StorageKey, Trailing) -> Result>; + + /// Returns the size of a storage entry at a block's state. + #[rpc(name = "state_getStorageSize", alias = ["state_getStorageSizeAt", ])] + fn storage_size(&self, StorageKey, Trailing) -> Result>; + + /// Returns the runtime metadata as JSON. + #[rpc(name = "state_getMetadata")] + fn metadata(&self, Trailing) -> Result; + + /// Query historical storage entries (by key) starting from a block given as the second parameter. + /// + /// NOTE This first returned result contains the initial state of storage for all keys. + /// Subsequent values in the vector represent changes to the previous state (diffs). + #[rpc(name = "state_queryStorage")] + fn query_storage(&self, Vec, Hash, Trailing) -> Result>>; + + #[pubsub(name = "state_storage")] { + /// New storage subscription + #[rpc(name = "state_subscribeStorage")] + fn subscribe_storage(&self, Self::Metadata, pubsub::Subscriber>, Trailing>); + + /// Unsubscribe from storage subscription + #[rpc(name = "state_unsubscribeStorage")] + fn unsubscribe_storage(&self, SubscriptionId) -> RpcResult; + } + } +} + +/// State API with subscriptions support. +pub struct State { + /// Substrate client. + client: Arc>, + /// Current subscriptions. + subscriptions: Subscriptions, +} + +impl State { + /// Create new State API RPC handler. + pub fn new(client: Arc>, executor: TaskExecutor) -> Self { + Self { + client, + subscriptions: Subscriptions::new(executor), + } + } +} + +impl State where + Block: BlockT, + B: client::backend::Backend, + E: CallExecutor, +{ + fn unwrap_or_best(&self, hash: Trailing) -> Result { + ::helpers::unwrap_or_else(|| Ok(self.client.info()?.chain.best_hash), hash) + } +} + +impl StateApi for State where + Block: BlockT + 'static, + B: client::backend::Backend + Send + Sync + 'static, + E: CallExecutor + Send + Sync + 'static, +{ + type Metadata = ::metadata::Metadata; + + fn call(&self, method: String, data: Vec, block: Trailing) -> Result> { + let block = self.unwrap_or_best(block)?; + trace!(target: "rpc", "Calling runtime at {:?} for method {} ({})", block, method, HexDisplay::from(&data)); + Ok(self.client.executor().call(&BlockId::Hash(block), &method, &data)?.return_data) + } + + fn storage(&self, key: StorageKey, block: Trailing) -> Result> { + let block = self.unwrap_or_best(block)?; + trace!(target: "rpc", "Querying storage at {:?} for key {}", block, HexDisplay::from(&key.0)); + Ok(self.client.storage(&BlockId::Hash(block), &key)?) + } + + fn storage_hash(&self, key: StorageKey, block: Trailing) -> Result> { + use runtime_primitives::traits::{Hash, Header as HeaderT}; + Ok(self.storage(key, block)?.map(|x| ::Hashing::hash(&x.0))) + } + + fn storage_size(&self, key: StorageKey, block: Trailing) -> Result> { + Ok(self.storage(key, block)?.map(|x| x.0.len() as u64)) + } + + fn metadata(&self, block: Trailing) -> Result { + let block = self.unwrap_or_best(block)?; + let metadata = self.client.json_metadata(&BlockId::Hash(block))?; + serde_json::from_str(&metadata).map_err(Into::into) + } + + fn query_storage(&self, keys: Vec, from: Block::Hash, to: Trailing) -> Result>> { + let to = self.unwrap_or_best(to)?; + + let from_hdr = self.client.header(&BlockId::hash(from))?; + let to_hdr = self.client.header(&BlockId::hash(to))?; + + match (from_hdr, to_hdr) { + (Some(ref from), Some(ref to)) if from.number() <= to.number() => { + let from = from.clone(); + let to = to.clone(); + // check if we can get from `to` to `from` by going through parent_hashes. + let blocks = { + let mut blocks = vec![to.hash()]; + let mut last = to.clone(); + while last.number() > from.number() { + if let Some(hdr) = self.client.header(&BlockId::hash(*last.parent_hash()))? { + blocks.push(hdr.hash()); + last = hdr; + } else { + bail!(invalid_block_range( + Some(from), + Some(to), + format!("Parent of {} ({}) not found", last.number(), last.hash()), + )) + } + } + if last.hash() != from.hash() { + bail!(invalid_block_range( + Some(from), + Some(to), + format!("Expected to reach `from`, got {} ({})", last.number(), last.hash()), + )) + } + blocks.reverse(); + blocks + }; + let mut result = Vec::new(); + let mut last_state: HashMap<_, Option<_>> = Default::default(); + for block in blocks { + let mut changes = vec![]; + let id = BlockId::hash(block.clone()); + + for key in &keys { + let (has_changed, data) = { + let curr_data = self.client.storage(&id, key)?; + let prev_data = last_state.get(key).and_then(|x| x.as_ref()); + + (curr_data.as_ref() != prev_data, curr_data) + }; + + if has_changed { + changes.push((key.clone(), data.clone())); + } + + last_state.insert(key.clone(), data); + } + + result.push(StorageChangeSet { + block, + changes, + }); + } + Ok(result) + }, + (from, to) => bail!(invalid_block_range(from, to, "Invalid range or unknown block".into())), + } + } + + fn subscribe_storage( + &self, + _meta: Self::Metadata, + subscriber: pubsub::Subscriber>, + keys: Trailing> + ) { + let keys = Into::>>::into(keys); + let stream = match self.client.storage_changes_notification_stream(keys.as_ref().map(|x| &**x)) { + Ok(stream) => stream, + Err(err) => { + let _ = subscriber.reject(error::Error::from(err).into()); + return; + }, + }; + + // initial values + let initial = stream::iter_result(keys + .map(|keys| { + let block = self.client.info().map(|info| info.chain.best_hash).unwrap_or_default(); + let changes = keys + .into_iter() + .map(|key| self.storage(key.clone(), Some(block.clone()).into()) + .map(|val| (key.clone(), val)) + .unwrap_or_else(|_| (key, None)) + ) + .collect(); + vec![Ok(Ok(StorageChangeSet { block, changes }))] + }).unwrap_or_default()); + + self.subscriptions.add(subscriber, |sink| { + let stream = stream + .map_err(|e| warn!("Error creating storage notification stream: {:?}", e)) + .map(|(block, changes)| Ok(StorageChangeSet { + block, + changes: changes.iter().cloned().collect(), + })); + + sink + .sink_map_err(|e| warn!("Error sending notifications: {:?}", e)) + .send_all(initial.chain(stream)) + // we ignore the resulting Stream (if the first stream is over we are unsubscribed) + .map(|_| ()) + }) + } + + fn unsubscribe_storage(&self, id: SubscriptionId) -> RpcResult { + Ok(self.subscriptions.cancel(id)) + } +} + +fn invalid_block_range(from: Option, to: Option, reason: String) -> error::ErrorKind { + let to_string = |x: Option| match x { + None => "unknown hash".into(), + Some(h) => format!("{} ({})", h.number(), h.hash()), + }; + + error::ErrorKind::InvalidBlockRange(to_string(from), to_string(to), reason) +} diff --git a/core/rpc/src/state/tests.rs b/core/rpc/src/state/tests.rs new file mode 100644 index 0000000000000..bad934b8405a3 --- /dev/null +++ b/core/rpc/src/state/tests.rs @@ -0,0 +1,186 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +use super::*; +use self::error::{Error, ErrorKind}; + +use client::BlockOrigin; +use jsonrpc_macros::pubsub; +use rustc_hex::FromHex; +use test_client::{self, runtime, keyring::Keyring, TestClient, BlockBuilderExt}; + +#[test] +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, core.executor()); + + assert_matches!( + client.storage(StorageKey(vec![10]), Some(genesis_hash).into()), + Ok(None) + ) +} + +#[test] +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, core.executor()); + + assert_matches!( + client.call("balanceOf".into(), vec![1,2,3], Some(genesis_hash).into()), + Err(Error(ErrorKind::Client(client::error::ErrorKind::Execution(_)), _)) + ) +} + +#[test] +fn should_notify_about_storage_changes() { + let mut core = ::tokio::runtime::Runtime::new().unwrap(); + let remote = core.executor(); + let (subscriber, id, transport) = pubsub::Subscriber::new_test("test"); + + { + let api = State { + client: Arc::new(test_client::new()), + subscriptions: Subscriptions::new(remote), + }; + + api.subscribe_storage(Default::default(), subscriber, None.into()); + + // assert id assigned + assert_eq!(core.block_on(id), Ok(Ok(SubscriptionId::Number(0)))); + + let mut builder = api.client.new_block().unwrap(); + builder.push_transfer(runtime::Transfer { + from: Keyring::Alice.to_raw_public().into(), + to: Keyring::Ferdie.to_raw_public().into(), + amount: 42, + nonce: 0, + }).unwrap(); + api.client.justify_and_import(BlockOrigin::Own, builder.bake().unwrap()).unwrap(); + } + + // assert notification sent to transport + let (notification, next) = core.block_on(transport.into_future()).unwrap(); + assert!(notification.is_some()); + // no more notifications on this channel + assert_eq!(core.block_on(next.into_future()).unwrap().0, None); +} + +#[test] +fn should_send_initial_storage_changes_and_notifications() { + let mut core = ::tokio::runtime::Runtime::new().unwrap(); + let remote = core.executor(); + let (subscriber, id, transport) = pubsub::Subscriber::new_test("test"); + + { + let api = State { + client: Arc::new(test_client::new()), + subscriptions: Subscriptions::new(remote), + }; + + api.subscribe_storage(Default::default(), subscriber, Some(vec![ + StorageKey("a52da2b7c269da1366b3ed1cdb7299ce".from_hex().unwrap()), + ]).into()); + + // assert id assigned + assert_eq!(core.block_on(id), Ok(Ok(SubscriptionId::Number(0)))); + + let mut builder = api.client.new_block().unwrap(); + builder.push_transfer(runtime::Transfer { + from: Keyring::Alice.to_raw_public().into(), + to: Keyring::Ferdie.to_raw_public().into(), + amount: 42, + nonce: 0, + }).unwrap(); + api.client.justify_and_import(BlockOrigin::Own, builder.bake().unwrap()).unwrap(); + } + + // assert initial values sent to transport + let (notification, next) = core.block_on(transport.into_future()).unwrap(); + assert!(notification.is_some()); + // assert notification sent to transport + let (notification, next) = core.block_on(next.into_future()).unwrap(); + assert!(notification.is_some()); + // no more notifications on this channel + assert_eq!(core.block_on(next.into_future()).unwrap().0, None); +} + +#[test] +fn should_query_storage() { + let core = ::tokio::runtime::Runtime::new().unwrap(); + let client = Arc::new(test_client::new()); + let api = State::new(client.clone(), core.executor()); + + let add_block = |nonce| { + let mut builder = client.new_block().unwrap(); + builder.push_transfer(runtime::Transfer { + from: Keyring::Alice.to_raw_public().into(), + to: Keyring::Ferdie.to_raw_public().into(), + amount: 42, + nonce, + }).unwrap(); + let block = builder.bake().unwrap(); + let hash = block.header.hash(); + client.justify_and_import(BlockOrigin::Own, block).unwrap(); + hash + }; + let block1_hash = add_block(0); + let block2_hash = add_block(1); + let genesis_hash = client.genesis_hash(); + + + let mut expected = vec![ + StorageChangeSet { + block: genesis_hash, + changes: vec![ + (StorageKey("a52da2b7c269da1366b3ed1cdb7299ce".from_hex().unwrap()), Some(StorageData(vec![232, 3, 0, 0, 0, 0, 0, 0]))), + ], + }, + StorageChangeSet { + block: block1_hash, + changes: vec![ + (StorageKey("a52da2b7c269da1366b3ed1cdb7299ce".from_hex().unwrap()), Some(StorageData(vec![190, 3, 0, 0, 0, 0, 0, 0]))), + ], + }, + ]; + + // Query changes only up to block1 + let result = api.query_storage( + vec![StorageKey("a52da2b7c269da1366b3ed1cdb7299ce".from_hex().unwrap())], + genesis_hash, + Some(block1_hash).into(), + ); + + assert_eq!(result.unwrap(), expected); + + // Query all changes + let result = api.query_storage( + vec![StorageKey("a52da2b7c269da1366b3ed1cdb7299ce".from_hex().unwrap())], + genesis_hash, + None.into(), + ); + + expected.push(StorageChangeSet { + block: block2_hash, + changes: vec![ + (StorageKey("a52da2b7c269da1366b3ed1cdb7299ce".from_hex().unwrap()), Some(StorageData(vec![148, 3, 0, 0, 0, 0, 0, 0]))), + ], + }); + assert_eq!(result.unwrap(), expected); +} diff --git a/core/rpc/src/subscriptions.rs b/core/rpc/src/subscriptions.rs new file mode 100644 index 0000000000000..9013edf742e1c --- /dev/null +++ b/core/rpc/src/subscriptions.rs @@ -0,0 +1,85 @@ +// Copyright 2017-2018 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::collections::HashMap; +use std::sync::atomic::{self, AtomicUsize}; + +use jsonrpc_macros::pubsub; +use jsonrpc_pubsub::SubscriptionId; +use parking_lot::Mutex; +use rpc::futures::sync::oneshot; +use rpc::futures::{Future, future}; +use tokio::runtime::TaskExecutor; + +type Id = u64; + +/// Subscriptions manager. +/// +/// Takes care of assigning unique subscription ids and +/// driving the sinks into completion. +#[derive(Debug)] +pub struct Subscriptions { + next_id: AtomicUsize, + active_subscriptions: Mutex>>, + executor: TaskExecutor, +} + +impl Subscriptions { + /// Creates new `Subscriptions` object. + pub fn new(executor: TaskExecutor) -> Self { + Subscriptions { + next_id: Default::default(), + active_subscriptions: Default::default(), + executor, + } + } + + /// Creates new subscription for given subscriber. + /// + /// Second parameter is a function that converts Subscriber sink into a future. + /// This future will be driven to completion bu underlying event loop + /// or will be cancelled in case #cancel is invoked. + pub fn add(&self, subscriber: pubsub::Subscriber, into_future: G) where + G: FnOnce(pubsub::Sink) -> R, + R: future::IntoFuture, + F: future::Future + Send + 'static, + { + let id = self.next_id.fetch_add(1, atomic::Ordering::AcqRel) as u64; + if let Ok(sink) = subscriber.assign_id(id.into()) { + let (tx, rx) = oneshot::channel(); + let future = into_future(sink) + .into_future() + .select(rx.map_err(|e| warn!("Error timeing out: {:?}", e))) + .then(|_| Ok(())); + + self.active_subscriptions.lock().insert(id, tx); + self.executor.spawn(future); + } + } + + /// Cancel subscription. + /// + /// Returns true if subscription existed or false otherwise. + pub fn cancel(&self, id: SubscriptionId) -> bool { + if let SubscriptionId::Number(id) = id { + if let Some(tx) = self.active_subscriptions.lock().remove(&id) { + let _ = tx.send(()); + return true; + } + } + false + } +} diff --git a/core/rpc/src/system/error.rs b/core/rpc/src/system/error.rs new file mode 100644 index 0000000000000..42d215caefd33 --- /dev/null +++ b/core/rpc/src/system/error.rs @@ -0,0 +1,40 @@ +// Copyright 2017 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 . + +//! System RPC module errors. + +use rpc; + +use errors; + +error_chain! { + errors { + /// Not implemented yet + Unimplemented { + description("not yet implemented"), + display("Method Not Implemented"), + } + } +} + +impl From for rpc::Error { + fn from(e: Error) -> Self { + match e { + Error(ErrorKind::Unimplemented, _) => errors::unimplemented(), + e => errors::internal(e), + } + } +} diff --git a/core/rpc/src/system/mod.rs b/core/rpc/src/system/mod.rs new file mode 100644 index 0000000000000..56d06be9e5cda --- /dev/null +++ b/core/rpc/src/system/mod.rs @@ -0,0 +1,41 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Substrate system API. + +pub mod error; + +#[cfg(test)] +mod tests; + +use self::error::Result; + +build_rpc_trait! { + /// Substrate system RPC API + pub trait SystemApi { + /// Get the node's implementation name. Plain old string. + #[rpc(name = "system_name")] + fn system_name(&self) -> Result; + + /// Get the node implementation's version. Should be a semver string. + #[rpc(name = "system_version")] + fn system_version(&self) -> Result; + + /// Get the chain's type. Given as a string identifier. + #[rpc(name = "system_chain")] + fn system_chain(&self) -> Result; + } +} diff --git a/core/rpc/src/system/tests.rs b/core/rpc/src/system/tests.rs new file mode 100644 index 0000000000000..f22cd5a157779 --- /dev/null +++ b/core/rpc/src/system/tests.rs @@ -0,0 +1,54 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +use super::*; +use super::error::*; + +impl SystemApi for () { + fn system_name(&self) -> Result { + Ok("testclient".into()) + } + fn system_version(&self) -> Result { + Ok("0.2.0".into()) + } + fn system_chain(&self) -> Result { + Ok("testchain".into()) + } +} + +#[test] +fn system_name_works() { + assert_eq!( + SystemApi::system_name(&()).unwrap(), + "testclient".to_owned() + ); +} + +#[test] +fn system_version_works() { + assert_eq!( + SystemApi::system_version(&()).unwrap(), + "0.2.0".to_owned() + ); +} + +#[test] +fn system_chain_works() { + assert_eq!( + SystemApi::system_chain(&()).unwrap(), + "testchain".to_owned() + ); +} diff --git a/core/runtime-io/Cargo.toml b/core/runtime-io/Cargo.toml new file mode 100644 index 0000000000000..8bf3c3406f6f7 --- /dev/null +++ b/core/runtime-io/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "substrate-runtime-io" +version = "0.1.0" +authors = ["Parity Technologies "] +build = "build.rs" + +[build-dependencies] +rustc_version = "0.2" + +[dependencies] +substrate-runtime-std = { path = "../runtime-std", default_features = false } +environmental = { path = "../environmental", optional = true } +substrate-state-machine = { path = "../state-machine", optional = true } +substrate-primitives = { path = "../primitives", default_features = false } +substrate-codec = { path = "../codec", default_features = false } +triehash = { version = "0.2", optional = true } +hashdb = { version = "0.2", default_features = false } +rlp = { version = "0.2", optional = true, default_features = false } + +[features] +default = ["std"] +std = [ + "environmental", + "substrate-state-machine", + "triehash", + "substrate-primitives/std", + "substrate-codec/std", + "substrate-runtime-std/std", + "rlp" +] +nightly = [] +strict = [] diff --git a/core/runtime-io/README.adoc b/core/runtime-io/README.adoc new file mode 100644 index 0000000000000..c035be1461188 --- /dev/null +++ b/core/runtime-io/README.adoc @@ -0,0 +1,13 @@ + += Runtime io + +.Summary +[source, toml] +---- +include::Cargo.toml[lines=2..5] +---- + +.Description +---- +include::src/lib.rs[tag=description] +---- diff --git a/core/runtime-io/build.rs b/core/runtime-io/build.rs new file mode 100644 index 0000000000000..35eb154f3a69a --- /dev/null +++ b/core/runtime-io/build.rs @@ -0,0 +1,14 @@ +//! Set a nightly feature + +extern crate rustc_version; +use rustc_version::{version, version_meta, Channel}; + +fn main() { + // Assert we haven't travelled back in time + assert!(version().unwrap().major >= 1); + + // Set cfg flags depending on release channel + if let Channel::Nightly = version_meta().unwrap().channel { + println!("cargo:rustc-cfg=feature=\"nightly\""); + } +} diff --git a/core/runtime-io/src/lib.rs b/core/runtime-io/src/lib.rs new file mode 100644 index 0000000000000..9214a7aa37b98 --- /dev/null +++ b/core/runtime-io/src/lib.rs @@ -0,0 +1,35 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +// tag::description[] +//! This is part of the Substrate runtime. +// end::description[] + +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(not(feature = "std"), feature(lang_items))] +#![cfg_attr(not(feature = "std"), feature(panic_handler))] +#![cfg_attr(not(feature = "std"), feature(alloc_error_handler))] +#![cfg_attr(not(feature = "std"), feature(core_intrinsics))] +#![cfg_attr(not(feature = "std"), feature(alloc))] + +#![cfg_attr(feature = "std", doc = "Substrate runtime standard library as compiled when linked with Rust's standard library.")] +#![cfg_attr(not(feature = "std"), doc = "Substrate's runtime standard library as compiled without Rust's standard library.")] + +#[cfg(feature = "std")] +include!("../with_std.rs"); + +#[cfg(not(feature = "std"))] +include!("../without_std.rs"); diff --git a/core/runtime-io/with_std.rs b/core/runtime-io/with_std.rs new file mode 100644 index 0000000000000..3790f925bd4a2 --- /dev/null +++ b/core/runtime-io/with_std.rs @@ -0,0 +1,266 @@ +// Copyright 2017 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 . + +#[macro_use] +extern crate environmental; + +#[cfg_attr(test, macro_use)] +extern crate substrate_primitives as primitives; + +extern crate substrate_state_machine; +extern crate triehash; +extern crate hashdb; +extern crate rlp; + +#[doc(hidden)] +pub extern crate substrate_codec as codec; +// re-export hashing functions. +pub use primitives::{blake2_256, twox_128, twox_256, ed25519}; + +pub use primitives::Blake2Hasher; +// Switch to this after PoC-3 +// pub use primitives::BlakeHasher; +pub use substrate_state_machine::{Externalities, TestExternalities}; +use primitives::hexdisplay::HexDisplay; +use primitives::H256; +use hashdb::Hasher; +use rlp::Encodable; + +// TODO: use the real error, not NoError. + +environmental!(ext: trait Externalities); + +/// Get `key` from storage and return a `Vec`, empty if there's a problem. +pub fn storage(key: &[u8]) -> Option> { + ext::with(|ext| ext.storage(key).map(|s| s.to_vec())) + .expect("storage cannot be called outside of an Externalities-provided environment.") +} + +/// Get `key` from storage, placing the value into `value_out` (as much of it as possible) and return +/// the number of bytes that the entry in storage had beyond the offset or None if the storage entry +/// doesn't exist at all. Note that if the buffer is smaller than the storage entry length, the returned +/// number of bytes is not equal to the number of bytes written to the `value_out`. +pub fn read_storage(key: &[u8], value_out: &mut [u8], value_offset: usize) -> Option { + ext::with(|ext| ext.storage(key).map(|value| { + let value = &value[value_offset..]; + let written = ::std::cmp::min(value.len(), value_out.len()); + value_out[0..written].copy_from_slice(&value[0..written]); + value.len() + })).expect("read_storage cannot be called outside of an Externalities-provided environment.") +} + +/// Set the storage of a key to some value. +pub fn set_storage(key: &[u8], value: &[u8]) { + ext::with(|ext| + ext.set_storage(key.to_vec(), value.to_vec()) + ); +} + +/// Clear the storage of a key. +pub fn clear_storage(key: &[u8]) { + ext::with(|ext| + ext.clear_storage(key) + ); +} + +/// Check whether a given `key` exists in storage. +pub fn exists_storage(key: &[u8]) -> bool { + ext::with(|ext| + ext.exists_storage(key) + ).unwrap_or(false) +} + +/// Clear the storage entries with a key that starts with the given prefix. +pub fn clear_prefix(prefix: &[u8]) { + ext::with(|ext| + ext.clear_prefix(prefix) + ); +} + +/// The current relay chain identifier. +pub fn chain_id() -> u64 { + ext::with(|ext| + ext.chain_id() + ).unwrap_or(0) +} + +/// "Commit" all existing operations and compute the resultant storage root. +pub fn storage_root() -> H256 { + ext::with(|ext| + ext.storage_root() + ).unwrap_or(H256::new()) +} + +/// A trie root formed from the enumerated items. +pub fn enumerated_trie_root(serialised_values: &[&[u8]]) -> H::Out +where + H: Hasher, + H::Out: Encodable + Ord, +{ + triehash::ordered_trie_root::(serialised_values.iter().map(|s| s.to_vec())) +} + +/// A trie root formed from the iterated items. +pub fn trie_root(input: I) -> H::Out +where + I: IntoIterator, + A: AsRef<[u8]> + Ord, + B: AsRef<[u8]>, + H: Hasher, + ::Out: Encodable + Ord, +{ + triehash::trie_root::(input) +} + +/// A trie root formed from the enumerated items. +pub fn ordered_trie_root(input: I) -> H::Out +where + I: IntoIterator, + A: AsRef<[u8]>, + H: Hasher, + ::Out: Encodable + Ord, +{ + triehash::ordered_trie_root::(input) +} + +/// Verify a ed25519 signature. +pub fn ed25519_verify>(sig: &[u8; 64], msg: &[u8], pubkey: P) -> bool { + ed25519::verify(sig, msg, pubkey) +} + +/// Execute the given closure with global function available whose functionality routes into the +/// externalities `ext`. Forwards the value that the closure returns. +// NOTE: need a concrete hasher here due to limitations of the `environmental!` macro, otherwise a type param would have been fine I think. +pub fn with_externalities R>(ext: &mut Externalities, f: F) -> R { + ext::using(ext, f) +} + +/// Trait for things which can be printed. +pub trait Printable { + fn print(self); +} + +impl<'a> Printable for &'a [u8] { + fn print(self) { + println!("Runtime: {}", HexDisplay::from(&self)); + } +} + +impl<'a> Printable for &'a str { + fn print(self) { + println!("Runtime: {}", self); + } +} + +impl Printable for u64 { + fn print(self) { + println!("Runtime: {}", self); + } +} + +/// Print a printable value. +pub fn print(value: T) { + value.print(); +} + +#[macro_export] +macro_rules! impl_stubs { + ( $( $new_name:ident $($nodecode:ident)* => $invoke: expr ),*) => { + /// Dispatch logic for the native runtime. + pub fn dispatch(method: &str, data: &[u8]) -> Option> { + match method { + $( + stringify!($new_name) => { impl_stubs!(@METHOD data $new_name $($nodecode)* => $invoke) } + )* + _ => None, + } + } + }; + (@METHOD $data: ident $new_name: ident NO_DECODE => $invoke:expr) => { + Some($invoke($data)) + }; + (@METHOD $data: ident $new_name: ident => $invoke:expr) => {{ + let mut data = $data; + let input = match $crate::codec::Decode::decode(&mut data) { + Some(input) => input, + None => panic!("Bad input data provided to {}", stringify!($new_name)), + }; + + let output = $invoke(input); + Some($crate::codec::Encode::encode(&output)) + }} +} + +#[cfg(test)] +mod std_tests { + use super::*; + + #[test] + fn storage_works() { + let mut t = TestExternalities::::new(); + assert!(with_externalities(&mut t, || { + assert_eq!(storage(b"hello"), None); + set_storage(b"hello", b"world"); + assert_eq!(storage(b"hello"), Some(b"world".to_vec())); + assert_eq!(storage(b"foo"), None); + set_storage(b"foo", &[1, 2, 3][..]); + true + })); + + t = map![b"foo".to_vec() => b"bar".to_vec()]; + + assert!(!with_externalities(&mut t, || { + assert_eq!(storage(b"hello"), None); + assert_eq!(storage(b"foo"), Some(b"bar".to_vec())); + false + })); + } + + #[test] + fn read_storage_works() { + let mut t: TestExternalities = map![ + b":test".to_vec() => b"\x0b\0\0\0Hello world".to_vec() + ]; + + with_externalities(&mut t, || { + let mut v = [0u8; 4]; + assert!(read_storage(b":test", &mut v[..], 0).unwrap() >= 4); + assert_eq!(v, [11u8, 0, 0, 0]); + let mut w = [0u8; 11]; + assert!(read_storage(b":test", &mut w[..], 4).unwrap() >= 11); + assert_eq!(&w, b"Hello world"); + }); + } + + #[test] + fn clear_prefix_works() { + let mut t: TestExternalities = map![ + b":a".to_vec() => b"\x0b\0\0\0Hello world".to_vec(), + b":abcd".to_vec() => b"\x0b\0\0\0Hello world".to_vec(), + b":abc".to_vec() => b"\x0b\0\0\0Hello world".to_vec(), + b":abdd".to_vec() => b"\x0b\0\0\0Hello world".to_vec() + ]; + + with_externalities(&mut t, || { + clear_prefix(b":abc"); + + assert!(storage(b":a").is_some()); + assert!(storage(b":abdd").is_some()); + assert!(storage(b":abcd").is_none()); + assert!(storage(b":abc").is_none()); + }); + } +} diff --git a/core/runtime-io/without_std.rs b/core/runtime-io/without_std.rs new file mode 100644 index 0000000000000..34c4191e97e22 --- /dev/null +++ b/core/runtime-io/without_std.rs @@ -0,0 +1,329 @@ +// Copyright 2017 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 . + + +extern crate substrate_primitives as primitives; +extern crate hashdb; + +#[doc(hidden)] +pub extern crate substrate_runtime_std as rstd; + +#[doc(hidden)] +pub extern crate substrate_codec as codec; + +use core::intrinsics; +use rstd::vec::Vec; +use hashdb::Hasher; +use primitives::Blake2Hasher; +pub use rstd::{mem, slice}; + +#[panic_handler] +#[no_mangle] +pub fn panic(info: &::core::panic::PanicInfo) -> ! { + unsafe { + if let Some(loc) = info.location() { + ext_print_utf8(loc.file().as_ptr() as *const u8, loc.file().len() as u32); + ext_print_num(loc.line() as u64); + ext_print_num(loc.column() as u64); + } + intrinsics::abort() + } +} + +#[alloc_error_handler] +pub extern fn oom(_: ::core::alloc::Layout) -> ! { + static OOM_MSG: &str = "Runtime memory exhausted. Aborting"; + + unsafe { + ext_print_utf8(OOM_MSG.as_ptr(), OOM_MSG.len() as u32); + intrinsics::abort(); + } +} + +extern "C" { + fn ext_print_utf8(utf8_data: *const u8, utf8_len: u32); + fn ext_print_hex(data: *const u8, len: u32); + fn ext_print_num(value: u64); + fn ext_set_storage(key_data: *const u8, key_len: u32, value_data: *const u8, value_len: u32); + fn ext_clear_storage(key_data: *const u8, key_len: u32); + fn ext_exists_storage(key_data: *const u8, key_len: u32) -> u32; + fn ext_clear_prefix(prefix_data: *const u8, prefix_len: u32); + fn ext_get_allocated_storage(key_data: *const u8, key_len: u32, written_out: *mut u32) -> *mut u8; + fn ext_get_storage_into(key_data: *const u8, key_len: u32, value_data: *mut u8, value_len: u32, value_offset: u32) -> u32; + fn ext_storage_root(result: *mut u8); + fn ext_blake2_256_enumerated_trie_root(values_data: *const u8, lens_data: *const u32, lens_len: u32, result: *mut u8); + fn ext_chain_id() -> u64; + fn ext_blake2_256(data: *const u8, len: u32, out: *mut u8); + fn ext_twox_128(data: *const u8, len: u32, out: *mut u8); + fn ext_twox_256(data: *const u8, len: u32, out: *mut u8); + fn ext_ed25519_verify(msg_data: *const u8, msg_len: u32, sig_data: *const u8, pubkey_data: *const u8) -> u32; +} + +/// Ensures we use the right crypto when calling into native +pub trait ExternTrieCrypto { + fn enumerated_trie_root(values: &[&[u8]]) -> [u8; 32]; +} + +// Ensures we use a Blake2_256-flavoured Hasher when calling into native +impl ExternTrieCrypto for Blake2Hasher { + fn enumerated_trie_root(values: &[&[u8]]) -> [u8; 32] { + let lengths = values.iter().map(|v| (v.len() as u32).to_le()).collect::>(); + let values = values.iter().fold(Vec::new(), |mut acc, sl| { acc.extend_from_slice(sl); acc }); + let mut result: [u8; 32] = Default::default(); + unsafe { + ext_blake2_256_enumerated_trie_root( + values.as_ptr(), + lengths.as_ptr(), + lengths.len() as u32, + result.as_mut_ptr() + ); + } + result + } +} + +/// Get `key` from storage and return a `Vec`, empty if there's a problem. +pub fn storage(key: &[u8]) -> Option> { + let mut length: u32 = 0; + unsafe { + let ptr = ext_get_allocated_storage(key.as_ptr(), key.len() as u32, &mut length); + if length == u32::max_value() { + None + } else { + Some(Vec::from_raw_parts(ptr, length as usize, length as usize)) + } + } +} + +/// Set the storage of some particular key to Some value. +pub fn set_storage(key: &[u8], value: &[u8]) { + unsafe { + ext_set_storage( + key.as_ptr(), key.len() as u32, + value.as_ptr(), value.len() as u32 + ); + } +} + +/// Clear the storage of some particular key. +pub fn clear_storage(key: &[u8]) { + unsafe { + ext_clear_storage( + key.as_ptr(), key.len() as u32 + ); + } +} + +/// Determine whether a particular key exists in storage. +pub fn exists_storage(key: &[u8]) -> bool { + unsafe { + ext_exists_storage( + key.as_ptr(), key.len() as u32 + ) != 0 + } +} + +/// Clear the storage entries key of which starts with the given prefix. +pub fn clear_prefix(prefix: &[u8]) { + unsafe { + ext_clear_prefix( + prefix.as_ptr(), + prefix.len() as u32 + ); + } +} + +/// Get `key` from storage, placing the value into `value_out` (as much as possible) and return +/// the number of bytes that the key in storage was beyond the offset. +pub fn read_storage(key: &[u8], value_out: &mut [u8], value_offset: usize) -> Option { + unsafe { + match ext_get_storage_into( + key.as_ptr(), key.len() as u32, + value_out.as_mut_ptr(), value_out.len() as u32, + value_offset as u32 + ) { + none if none == u32::max_value() => None, + length => Some(length as usize), + } + } +} + +/// The current storage's root. +pub fn storage_root() -> [u8; 32] { + let mut result: [u8; 32] = Default::default(); + unsafe { + ext_storage_root(result.as_mut_ptr()); + } + result +} + +/// A trie root calculated from enumerated values. +pub fn enumerated_trie_root(values: &[&[u8]]) -> [u8; 32] { + H::enumerated_trie_root(values) +} + +/// A trie root formed from the iterated items. +pub fn trie_root< + H: Hasher + ExternTrieCrypto, + I: IntoIterator, + A: AsRef<[u8]> + Ord, + B: AsRef<[u8]>, +>(_input: I) -> [u8; 32] { + unimplemented!() + // TODO Maybe implement (though probably easier/cleaner to have blake2 be the only thing + // implemneted natively and compile the trie logic as wasm). +} + +/// A trie root formed from the enumerated items. +pub fn ordered_trie_root< + H: Hasher + ExternTrieCrypto, + I: IntoIterator, + A: AsRef<[u8]> +>(_input: I) -> [u8; 32] { + unimplemented!() + // TODO Maybe implement (though probably easier/cleaner to have blake2 be the only thing + // implemneted natively and compile the trie logic as wasm). +} + +/// The current relay chain identifier. +pub fn chain_id() -> u64 { + unsafe { + ext_chain_id() + } +} + +/// Conduct a 256-bit Blake2 hash. +pub fn blake2_256(data: &[u8]) -> [u8; 32] { + let mut result: [u8; 32] = Default::default(); + unsafe { + ext_blake2_256(data.as_ptr(), data.len() as u32, result.as_mut_ptr()); + } + result +} + +/// Conduct four XX hashes to give a 256-bit result. +pub fn twox_256(data: &[u8]) -> [u8; 32] { + let mut result: [u8; 32] = Default::default(); + unsafe { + ext_twox_256(data.as_ptr(), data.len() as u32, result.as_mut_ptr()); + } + result +} + +/// Conduct two XX hashes to give a 128-bit result. +pub fn twox_128(data: &[u8]) -> [u8; 16] { + let mut result: [u8; 16] = Default::default(); + unsafe { + ext_twox_128(data.as_ptr(), data.len() as u32, result.as_mut_ptr()); + } + result +} + +/// Verify a ed25519 signature. +pub fn ed25519_verify>(sig: &[u8; 64], msg: &[u8], pubkey: P) -> bool { + unsafe { + ext_ed25519_verify(msg.as_ptr(), msg.len() as u32, sig.as_ptr(), pubkey.as_ref().as_ptr()) == 0 + } +} + +/// Trait for things which can be printed. +pub trait Printable { + fn print(self); +} + +impl<'a> Printable for &'a [u8] { + fn print(self) { + unsafe { + ext_print_hex(self.as_ptr(), self.len() as u32); + } + } +} + +impl<'a> Printable for &'a str { + fn print(self) { + unsafe { + ext_print_utf8(self.as_ptr() as *const u8, self.len() as u32); + } + } +} + +impl Printable for u64 { + fn print(self) { + unsafe { ext_print_num(self); } + } +} + +/// Print a printable value. +pub fn print(value: T) { + value.print(); +} + +#[macro_export] +macro_rules! impl_stubs { + ( $( $new_name:ident $($nodecode:ident)* => $invoke:expr ),* ) => { + $( + impl_stubs!(@METHOD $new_name $($nodecode)* => $invoke); + )* + }; + ( @METHOD $new_name:ident NO_DECODE => $invoke:expr ) => { + #[no_mangle] + pub fn $new_name(input_data: *mut u8, input_len: usize) -> u64 { + let input: &[u8] = if input_len == 0 { + &[0u8; 0] + } else { + unsafe { + $crate::slice::from_raw_parts(input_data, input_len) + } + }; + + let output: $crate::rstd::vec::Vec = $invoke(input); + let res = output.as_ptr() as u64 + ((output.len() as u64) << 32); + + // Leak the output vector to avoid it being freed. + // This is fine in a WASM context since the heap + // will be discarded after the call. + ::core::mem::forget(output); + res + } + }; + ( @METHOD $new_name:ident => $invoke:expr ) => { + #[no_mangle] + pub fn $new_name(input_data: *mut u8, input_len: usize) -> u64 { + let mut input = if input_len == 0 { + &[0u8; 0] + } else { + unsafe { + $crate::slice::from_raw_parts(input_data, input_len) + } + }; + + let input = match $crate::codec::Decode::decode(&mut input) { + Some(input) => input, + None => panic!("Bad input data provided to {}", stringify!($name)), + }; + + let output = ($invoke)(input); + let output = $crate::codec::Encode::encode(&output); + let res = output.as_ptr() as u64 + ((output.len() as u64) << 32); + + // Leak the output vector to avoid it being freed. + // This is fine in a WASM context since the heap + // will be discarded after the call. + ::core::mem::forget(output); + res + } + } +} diff --git a/core/runtime-sandbox/Cargo.toml b/core/runtime-sandbox/Cargo.toml new file mode 100755 index 0000000000000..b80cb4443f8ae --- /dev/null +++ b/core/runtime-sandbox/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "substrate-runtime-sandbox" +version = "0.1.0" +authors = ["Parity Technologies "] +build = "build.rs" + +[build-dependencies] +rustc_version = "0.2" + +[dependencies] +wasmi = { version = "0.4", optional = true } +substrate-primitives = { path = "../primitives", default_features = false } +substrate-runtime-std = { path = "../runtime-std", default_features = false } +substrate-runtime-io = { path = "../runtime-io", default_features = false } +substrate-codec = { path = "../codec", default_features = false } + +[dev-dependencies] +wabt = "0.4" +assert_matches = "1.1" + +[features] +default = ["std"] +std = [ + "wasmi", + "substrate-primitives/std", + "substrate-runtime-std/std", + "substrate-codec/std", + "substrate-runtime-io/std", +] +nightly = [] +strict = [] diff --git a/core/runtime-sandbox/README.adoc b/core/runtime-sandbox/README.adoc new file mode 100644 index 0000000000000..61a203708c811 --- /dev/null +++ b/core/runtime-sandbox/README.adoc @@ -0,0 +1,13 @@ + += Runtime Sandbox + +.Summary +[source, toml] +---- +include::Cargo.toml[lines=2..5] +---- + +.Description +---- +include::src/lib.rs[tag=description] +---- diff --git a/core/runtime-sandbox/build.rs b/core/runtime-sandbox/build.rs new file mode 100755 index 0000000000000..35eb154f3a69a --- /dev/null +++ b/core/runtime-sandbox/build.rs @@ -0,0 +1,14 @@ +//! Set a nightly feature + +extern crate rustc_version; +use rustc_version::{version, version_meta, Channel}; + +fn main() { + // Assert we haven't travelled back in time + assert!(version().unwrap().major >= 1); + + // Set cfg flags depending on release channel + if let Channel::Nightly = version_meta().unwrap().channel { + println!("cargo:rustc-cfg=feature=\"nightly\""); + } +} diff --git a/core/runtime-sandbox/src/lib.rs b/core/runtime-sandbox/src/lib.rs new file mode 100755 index 0000000000000..16066af160f15 --- /dev/null +++ b/core/runtime-sandbox/src/lib.rs @@ -0,0 +1,221 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +// tag::description[] +//! This crate provides means of instantiation and execution of wasm modules. +//! +//! It works even when the user of this library is itself executes +//! inside the wasm VM. In this case same VM is used for execution +//! of both the sandbox owner and the sandboxed module, without compromising security +//! and without performance penalty of full wasm emulation inside wasm. +//! +//! This is achieved by using bindings to wasm VM which are published by the host API. +//! This API is thin and consists of only handful functions. It contains functions for instantiating +//! modules and executing them and for example doesn't contain functions for inspecting the module +//! structure. The user of this library is supposed to read wasm module by it's own means. +//! +//! When this crate is used in `std` environment all these functions are implemented by directly +//! calling wasm VM. +//! +//! Example of possible use-cases for this library are following: +//! +//! - implementing smart-contract runtimes which uses wasm for contract code +//! - executing wasm substrate runtime inside of a wasm parachain +//! - etc +// end::description[] + +#![warn(missing_docs)] +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(not(feature = "std"), feature(panic_handler))] +#![cfg_attr(not(feature = "std"), feature(core_intrinsics))] +#![cfg_attr(not(feature = "std"), feature(alloc))] + +extern crate substrate_codec as codec; +extern crate substrate_runtime_io as runtime_io; +#[cfg_attr(not(feature = "std"), macro_use)] +extern crate substrate_runtime_std as rstd; +extern crate substrate_primitives as primitives; + +#[cfg(test)] +extern crate wabt; + +#[cfg(test)] +#[macro_use] +extern crate assert_matches; + +use rstd::prelude::*; + +pub use primitives::sandbox::{TypedValue, ReturnValue, HostError}; + +mod imp { + #[cfg(feature = "std")] + include!("../with_std.rs"); + + #[cfg(not(feature = "std"))] + include!("../without_std.rs"); +} + +/// Error that can occur while using this crate. +#[cfg_attr(feature = "std", derive(Debug))] +pub enum Error { + /// Module is not valid, couldn't be instantiated or it's `start` function trapped + /// when executed. + Module, + + /// Access to a memory or table was made with an address or an index which is out of bounds. + /// + /// Note that if wasm module makes an out-of-bounds access then trap will occur. + OutOfBounds, + + /// Failed to invoke an exported function for some reason. + Execution, +} + +impl From for HostError { + fn from(_e: Error) -> HostError { + HostError + } +} + +/// Function pointer for specifying functions by the +/// supervisor in [`EnvironmentDefinitionBuilder`]. +/// +/// [`EnvironmentDefinitionBuilder`]: struct.EnvironmentDefinitionBuilder.html +pub type HostFuncType = fn(&mut T, &[TypedValue]) -> Result; + +/// Reference to a sandboxed linear memory, that +/// will be used by the guest module. +/// +/// The memory can't be directly accessed by supervisor, but only +/// through designated functions [`get`] and [`set`]. +/// +/// [`get`]: #method.get +/// [`set`]: #method.set +#[derive(Clone)] +pub struct Memory { + inner: imp::Memory, +} + +impl Memory { + /// Construct a new linear memory instance. + /// + /// 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). + /// + /// 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. + /// + /// Allocated memory is always zeroed. + pub fn new(initial: u32, maximum: Option) -> Result { + Ok(Memory { + inner: imp::Memory::new(initial, maximum)?, + }) + } + + /// Read a memory area at the address `ptr` with the size of the provided slice `buf`. + /// + /// Returns `Err` if the range is out-of-bounds. + pub fn get(&self, ptr: u32, buf: &mut [u8]) -> Result<(), Error> { + self.inner.get(ptr, buf) + } + + /// Write a memory area at the address `ptr` with contents of the provided slice `buf`. + /// + /// Returns `Err` if the range is out-of-bounds. + pub fn set(&self, ptr: u32, value: &[u8]) -> Result<(), Error> { + self.inner.set(ptr, value) + } +} + +/// Struct that can be used for defining an environment for a sandboxed module. +/// +/// The sandboxed module can access only the entities which were defined and passed +/// to the module at the instantiation time. +pub struct EnvironmentDefinitionBuilder { + inner: imp::EnvironmentDefinitionBuilder, +} + +impl EnvironmentDefinitionBuilder { + /// Construct a new `EnvironmentDefinitionBuilder`. + pub fn new() -> EnvironmentDefinitionBuilder { + EnvironmentDefinitionBuilder { + inner: imp::EnvironmentDefinitionBuilder::new(), + } + } + + /// Register a host function in this environment defintion. + /// + /// NOTE that there is no constraints on type of this function. An instance + /// can import function passed here with any signature it wants. It can even import + /// the same function (i.e. with same `module` and `field`) several times. It's up to + /// the user code to check or constrain the types of signatures. + pub fn add_host_func(&mut self, module: N1, field: N2, f: HostFuncType) + where + N1: Into>, + N2: Into>, + { + self.inner.add_host_func(module, field, f); + } + + /// Register a memory in this environment definition. + pub fn add_memory(&mut self, module: N1, field: N2, mem: Memory) + where + N1: Into>, + N2: Into>, + { + self.inner.add_memory(module, field, mem.inner); + } +} + +/// Sandboxed instance of a wasm module. +/// +/// This instance can be used for invoking exported functions. +pub struct Instance { + inner: imp::Instance, + +} + +impl Instance { + /// Instantiate a module with the given [`EnvironmentDefinitionBuilder`]. + /// + /// [`EnvironmentDefinitionBuilder`]: struct.EnvironmentDefinitionBuilder.html + pub fn new(code: &[u8], env_def_builder: &EnvironmentDefinitionBuilder, state: &mut T) -> Result, Error> { + Ok(Instance { + inner: imp::Instance::new(code, &env_def_builder.inner, state)?, + }) + } + + /// Invoke an exported function with the given name. + /// + /// # Errors + /// + /// Returns `Err(Error::Execution)` if: + /// + /// - An export function name isn't a proper utf8 byte sequence, + /// - This module doesn't have an exported function with the given name, + /// - If types of the arguments passed to the function doesn't match function signature + /// then trap occurs (as if the exported function was called via call_indirect), + /// - Trap occured at the execution time. + pub fn invoke( + &mut self, + name: &[u8], + args: &[TypedValue], + state: &mut T, + ) -> Result { + self.inner.invoke(name, args, state) + } +} diff --git a/core/runtime-sandbox/with_std.rs b/core/runtime-sandbox/with_std.rs new file mode 100755 index 0000000000000..e4e4aa131ea34 --- /dev/null +++ b/core/runtime-sandbox/with_std.rs @@ -0,0 +1,482 @@ +// Copyright 2018 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 . + +extern crate wasmi; + +use rstd::collections::btree_map::BTreeMap; +use rstd::fmt; + +use self::wasmi::{ + Externals, FuncInstance, FuncRef, GlobalDescriptor, GlobalRef, ImportResolver, + MemoryDescriptor, MemoryInstance, MemoryRef, Module, ModuleInstance, ModuleRef, + RuntimeArgs, RuntimeValue, Signature, TableDescriptor, TableRef, Trap, TrapKind +}; +use self::wasmi::memory_units::Pages; +use super::{Error, TypedValue, ReturnValue, HostFuncType, HostError}; + +#[derive(Clone)] +pub struct Memory { + memref: MemoryRef, +} + +impl Memory { + pub fn new(initial: u32, maximum: Option) -> Result { + Ok(Memory { + memref: MemoryInstance::alloc( + Pages(initial as usize), + maximum.map(|m| Pages(m as usize)), + ).map_err(|_| Error::Module)?, + }) + } + + pub fn get(&self, ptr: u32, buf: &mut [u8]) -> Result<(), Error> { + self.memref.get_into(ptr, buf).map_err(|_| Error::OutOfBounds)?; + Ok(()) + } + + pub fn set(&self, ptr: u32, value: &[u8]) -> Result<(), Error> { + self.memref.set(ptr, value).map_err(|_| Error::OutOfBounds)?; + Ok(()) + } +} + +struct HostFuncIndex(usize); + +struct DefinedHostFunctions { + funcs: Vec>, +} + +impl Clone for DefinedHostFunctions { + fn clone(&self) -> DefinedHostFunctions { + DefinedHostFunctions { + funcs: self.funcs.clone(), + } + } +} + +impl DefinedHostFunctions { + fn new() -> DefinedHostFunctions { + DefinedHostFunctions { + funcs: Vec::new(), + } + } + + fn define(&mut self, f: HostFuncType) -> HostFuncIndex { + let idx = self.funcs.len(); + self.funcs.push(f); + HostFuncIndex(idx) + } +} + +#[derive(Debug)] +struct DummyHostError; + +impl fmt::Display for DummyHostError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "DummyHostError") + } +} + +impl self::wasmi::HostError for DummyHostError { +} + +fn from_runtime_value(v: RuntimeValue) -> TypedValue { + match v { + RuntimeValue::I32(v) => TypedValue::I32(v), + RuntimeValue::I64(v) => TypedValue::I64(v), + RuntimeValue::F32(v) => TypedValue::F32(v.to_bits() as i32), + RuntimeValue::F64(v) => TypedValue::F64(v.to_bits() as i64), + } +} + +fn to_runtime_value(v: TypedValue) -> RuntimeValue { + use self::wasmi::nan_preserving_float::{F32, F64}; + match v { + TypedValue::I32(v) => RuntimeValue::I32(v as i32), + TypedValue::I64(v) => RuntimeValue::I64(v as i64), + TypedValue::F32(v_bits) => RuntimeValue::F32(F32::from_bits(v_bits as u32)), + TypedValue::F64(v_bits) => RuntimeValue::F64(F64::from_bits(v_bits as u64)), + } +} + +struct GuestExternals<'a, T: 'a> { + state: &'a mut T, + defined_host_functions: &'a DefinedHostFunctions, +} + +impl<'a, T> Externals for GuestExternals<'a, T> { + fn invoke_index( + &mut self, + index: usize, + args: RuntimeArgs, + ) -> Result, Trap> { + let args = args.as_ref() + .iter() + .cloned() + .map(from_runtime_value) + .collect::>(); + + let result = (self.defined_host_functions.funcs[index])(self.state, &args); + match result { + Ok(value) => Ok(match value { + ReturnValue::Value(v) => Some(to_runtime_value(v)), + ReturnValue::Unit => None, + }), + Err(HostError) => Err(TrapKind::Host(Box::new(DummyHostError)).into()), + } + } +} + +enum ExternVal { + HostFunc(HostFuncIndex), + Memory(Memory), +} + +pub struct EnvironmentDefinitionBuilder { + map: BTreeMap<(Vec, Vec), ExternVal>, + defined_host_functions: DefinedHostFunctions, +} + +impl EnvironmentDefinitionBuilder { + pub fn new() -> EnvironmentDefinitionBuilder { + EnvironmentDefinitionBuilder { + map: BTreeMap::new(), + defined_host_functions: DefinedHostFunctions::new(), + } + } + + pub fn add_host_func(&mut self, module: N1, field: N2, f: HostFuncType) + where + N1: Into>, + N2: Into>, + { + let idx = self.defined_host_functions.define(f); + self.map + .insert((module.into(), field.into()), ExternVal::HostFunc(idx)); + } + + pub fn add_memory(&mut self, module: N1, field: N2, mem: Memory) + where + N1: Into>, + N2: Into>, + { + self.map + .insert((module.into(), field.into()), ExternVal::Memory(mem)); + } +} + +impl ImportResolver for EnvironmentDefinitionBuilder { + fn resolve_func( + &self, + module_name: &str, + field_name: &str, + signature: &Signature, + ) -> Result { + let key = ( + module_name.as_bytes().to_owned(), + field_name.as_bytes().to_owned(), + ); + let externval = self.map.get(&key).ok_or_else(|| { + wasmi::Error::Instantiation(format!("Export {}:{} not found", module_name, field_name)) + })?; + let host_func_idx = match *externval { + ExternVal::HostFunc(ref idx) => idx, + _ => { + return Err(wasmi::Error::Instantiation(format!( + "Export {}:{} is not a host func", + module_name, field_name + ))) + } + }; + Ok(FuncInstance::alloc_host(signature.clone(), host_func_idx.0)) + } + + fn resolve_global( + &self, + _module_name: &str, + _field_name: &str, + _global_type: &GlobalDescriptor, + ) -> Result { + Err(wasmi::Error::Instantiation(format!( + "Importing globals is not supported yet" + ))) + } + + fn resolve_memory( + &self, + module_name: &str, + field_name: &str, + _memory_type: &MemoryDescriptor, + ) -> Result { + let key = ( + module_name.as_bytes().to_owned(), + field_name.as_bytes().to_owned(), + ); + let externval = self.map.get(&key).ok_or_else(|| { + wasmi::Error::Instantiation(format!("Export {}:{} not found", module_name, field_name)) + })?; + let memory = match *externval { + ExternVal::Memory(ref m) => m, + _ => { + return Err(wasmi::Error::Instantiation(format!( + "Export {}:{} is not a memory", + module_name, field_name + ))) + } + }; + Ok(memory.memref.clone()) + } + + fn resolve_table( + &self, + _module_name: &str, + _field_name: &str, + _table_type: &TableDescriptor, + ) -> Result { + Err(wasmi::Error::Instantiation(format!( + "Importing tables is not supported yet" + ))) + } +} + +pub struct Instance { + instance: ModuleRef, + defined_host_functions: DefinedHostFunctions, + _marker: ::std::marker::PhantomData, +} + +impl Instance { + pub fn new(code: &[u8], env_def_builder: &EnvironmentDefinitionBuilder, state: &mut T) -> Result, Error> { + let module = Module::from_buffer(code).map_err(|_| Error::Module)?; + let not_started_instance = ModuleInstance::new(&module, env_def_builder) + .map_err(|_| Error::Module)?; + + + let defined_host_functions = env_def_builder.defined_host_functions.clone(); + let instance = { + let mut externals = GuestExternals { + state, + defined_host_functions: &defined_host_functions, + }; + let instance = not_started_instance.run_start(&mut externals).map_err(|_| Error::Module)?; + instance + }; + + Ok(Instance { + instance, + defined_host_functions, + _marker: ::std::marker::PhantomData::, + }) + } + + pub fn invoke( + &mut self, + name: &[u8], + args: &[TypedValue], + state: &mut T, + ) -> Result { + let args = args.iter().cloned().map(Into::into).collect::>(); + + let name = ::std::str::from_utf8(name).map_err(|_| Error::Execution)?; + let mut externals = GuestExternals { + state, + defined_host_functions: &self.defined_host_functions, + }; + let result = self.instance + .invoke_export(&name, &args, &mut externals); + + match result { + Ok(None) => Ok(ReturnValue::Unit), + Ok(Some(val)) => Ok(ReturnValue::Value(val.into())), + Err(_err) => Err(Error::Execution), + } + } +} + +#[cfg(test)] +mod tests { + use wabt; + use ::{Error, TypedValue, ReturnValue, HostError, EnvironmentDefinitionBuilder, Instance}; + + fn execute_sandboxed(code: &[u8], args: &[TypedValue]) -> Result { + struct State { + counter: u32, + } + + fn env_assert(_e: &mut State, args: &[TypedValue]) -> Result { + if args.len() != 1 { + return Err(HostError); + } + let condition = args[0].as_i32().ok_or_else(|| HostError)?; + if condition != 0 { + Ok(ReturnValue::Unit) + } else { + Err(HostError) + } + } + fn env_inc_counter(e: &mut State, args: &[TypedValue]) -> Result { + if args.len() != 1 { + return Err(HostError); + } + let inc_by = args[0].as_i32().ok_or_else(|| HostError)?; + e.counter += inc_by as u32; + Ok(ReturnValue::Value(TypedValue::I32(e.counter as i32))) + } + /// Function that takes one argument of any type and returns that value. + fn env_polymorphic_id(_e: &mut State, args: &[TypedValue]) -> Result { + if args.len() != 1 { + return Err(HostError); + } + Ok(ReturnValue::Value(args[0])) + } + + let mut state = State { counter: 0 }; + + let mut env_builder = EnvironmentDefinitionBuilder::new(); + env_builder.add_host_func("env", "assert", env_assert); + env_builder.add_host_func("env", "inc_counter", env_inc_counter); + env_builder.add_host_func("env", "polymorphic_id", env_polymorphic_id); + + let mut instance = Instance::new(code, &env_builder, &mut state)?; + let result = instance.invoke(b"call", args, &mut state); + + result.map_err(|_| HostError) + } + + #[test] + fn invoke_args() { + let code = wabt::wat2wasm(r#" + (module + (import "env" "assert" (func $assert (param i32))) + + (func (export "call") (param $x i32) (param $y i64) + ;; assert that $x = 0x12345678 + (call $assert + (i32.eq + (get_local $x) + (i32.const 0x12345678) + ) + ) + + (call $assert + (i64.eq + (get_local $y) + (i64.const 0x1234567887654321) + ) + ) + ) + ) + "#).unwrap(); + + let result = execute_sandboxed( + &code, + &[ + TypedValue::I32(0x12345678), + TypedValue::I64(0x1234567887654321), + ] + ); + assert!(result.is_ok()); + } + + #[test] + fn return_value() { + let code = wabt::wat2wasm(r#" + (module + (func (export "call") (param $x i32) (result i32) + (i32.add + (get_local $x) + (i32.const 1) + ) + ) + ) + "#).unwrap(); + + let return_val = execute_sandboxed( + &code, + &[ + TypedValue::I32(0x1336), + ] + ).unwrap(); + assert_eq!(return_val, ReturnValue::Value(TypedValue::I32(0x1337))); + } + + #[test] + fn signatures_dont_matter() { + let code = wabt::wat2wasm(r#" + (module + (import "env" "polymorphic_id" (func $id_i32 (param i32) (result i32))) + (import "env" "polymorphic_id" (func $id_i64 (param i64) (result i64))) + (import "env" "assert" (func $assert (param i32))) + + (func (export "call") + ;; assert that we can actually call the "same" function with different + ;; signatures. + (call $assert + (i32.eq + (call $id_i32 + (i32.const 0x012345678) + ) + (i32.const 0x012345678) + ) + ) + (call $assert + (i64.eq + (call $id_i64 + (i64.const 0x0123456789abcdef) + ) + (i64.const 0x0123456789abcdef) + ) + ) + ) + ) + "#).unwrap(); + + let return_val = execute_sandboxed(&code, &[]).unwrap(); + assert_eq!(return_val, ReturnValue::Unit); + } + + #[test] + fn cant_return_unmatching_type() { + fn env_returns_i32(_e: &mut (), _args: &[TypedValue]) -> Result { + Ok(ReturnValue::Value(TypedValue::I32(42))) + } + + let mut env_builder = EnvironmentDefinitionBuilder::new(); + env_builder.add_host_func("env", "returns_i32", env_returns_i32); + + let code = wabt::wat2wasm(r#" + (module + ;; It's actually returns i32, but imported as if it returned i64 + (import "env" "returns_i32" (func $returns_i32 (result i64))) + + (func (export "call") + (drop + (call $returns_i32) + ) + ) + ) + "#).unwrap(); + + // It succeeds since we are able to import functions with types we want. + let mut instance = Instance::new(&code, &env_builder, &mut ()).unwrap(); + + // But this fails since we imported a function that returns i32 as if it returned i64. + assert_matches!( + instance.invoke(b"call", &[], &mut ()), + Err(Error::Execution) + ); + } +} diff --git a/core/runtime-sandbox/without_std.rs b/core/runtime-sandbox/without_std.rs new file mode 100755 index 0000000000000..f7feb466a090b --- /dev/null +++ b/core/runtime-sandbox/without_std.rs @@ -0,0 +1,303 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +use rstd::prelude::*; +use rstd::{slice, marker, mem}; +use codec::{Decode, Encode}; +use primitives::sandbox as sandbox_primitives; +use super::{Error, TypedValue, ReturnValue, HostFuncType}; + +mod ffi { + use rstd::mem; + use super::HostFuncType; + + /// Index into the default table that points to a `HostFuncType`. + pub type HostFuncIndex = usize; + + /// Coerce `HostFuncIndex` to a callable host function pointer. + /// + /// # Safety + /// + /// This function should be only called with a `HostFuncIndex` that was previously registered + /// in the environment defintion. 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 + // indeed equal. + // We can't use `static_assertions` create because it makes compiler panic, fallback to runtime assert. + // const_assert!(mem::size_of::() == mem::size_of::>(),); + assert!(mem::size_of::() == mem::size_of::>()); + mem::transmute::>(idx) + } + + extern "C" { + pub fn ext_sandbox_instantiate( + dispatch_thunk: extern "C" fn( + serialized_args_ptr: *const u8, + serialized_args_len: usize, + state: usize, + f: HostFuncIndex, + ) -> u64, + wasm_ptr: *const u8, + wasm_len: usize, + imports_ptr: *const u8, + imports_len: usize, + state: usize, + ) -> u32; + pub fn ext_sandbox_invoke( + instance_idx: u32, + export_ptr: *const u8, + export_len: usize, + args_ptr: *const u8, + args_len: usize, + return_val_ptr: *mut u8, + return_val_len: usize, + state: usize, + ) -> u32; + pub fn ext_sandbox_memory_new(initial: u32, maximum: u32) -> u32; + pub fn ext_sandbox_memory_get( + memory_idx: u32, + offset: u32, + buf_ptr: *mut u8, + buf_len: usize, + ) -> u32; + pub fn ext_sandbox_memory_set( + memory_idx: u32, + offset: u32, + val_ptr: *const u8, + val_len: usize, + ) -> u32; + pub fn ext_sandbox_memory_teardown( + memory_idx: u32, + ); + pub fn ext_sandbox_instance_teardown( + instance_idx: u32, + ); + } +} + +#[derive(Clone)] +pub struct Memory { + memory_idx: u32, +} + +impl Memory { + pub fn new(initial: u32, maximum: Option) -> Result { + let result = unsafe { + let maximum = if let Some(maximum) = maximum { + maximum + } else { + sandbox_primitives::MEM_UNLIMITED + }; + ffi::ext_sandbox_memory_new(initial, maximum) + }; + match result { + sandbox_primitives::ERR_MODULE => Err(Error::Module), + memory_idx => Ok(Memory { memory_idx }), + } + } + + pub fn get(&self, offset: u32, buf: &mut [u8]) -> Result<(), Error> { + let result = unsafe { ffi::ext_sandbox_memory_get(self.memory_idx, offset, buf.as_mut_ptr(), buf.len()) }; + match result { + sandbox_primitives::ERR_OK => Ok(()), + sandbox_primitives::ERR_OUT_OF_BOUNDS => Err(Error::OutOfBounds), + _ => unreachable!(), + } + } + + pub fn set(&self, offset: u32, val: &[u8]) -> Result<(), Error> { + let result = unsafe { ffi::ext_sandbox_memory_set(self.memory_idx, offset, val.as_ptr(), val.len()) }; + match result { + sandbox_primitives::ERR_OK => Ok(()), + sandbox_primitives::ERR_OUT_OF_BOUNDS => Err(Error::OutOfBounds), + _ => unreachable!(), + } + } +} + +impl Drop for Memory { + fn drop(&mut self) { + unsafe { + ffi::ext_sandbox_memory_teardown(self.memory_idx); + } + } +} + +pub struct EnvironmentDefinitionBuilder { + env_def: sandbox_primitives::EnvironmentDefinition, + _marker: marker::PhantomData, +} + +impl EnvironmentDefinitionBuilder { + pub fn new() -> EnvironmentDefinitionBuilder { + EnvironmentDefinitionBuilder { + env_def: sandbox_primitives::EnvironmentDefinition { + entries: Vec::new(), + }, + _marker: marker::PhantomData::, + } + } + + fn add_entry( + &mut self, + module: N1, + field: N2, + extern_entity: sandbox_primitives::ExternEntity, + ) where + N1: Into>, + N2: Into>, + { + let entry = sandbox_primitives::Entry { + module_name: module.into(), + field_name: field.into(), + entity: extern_entity, + }; + self.env_def.entries.push(entry); + } + + pub fn add_host_func(&mut self, module: N1, field: N2, f: HostFuncType) + where + N1: Into>, + N2: Into>, + { + let f = sandbox_primitives::ExternEntity::Function(f as u32); + self.add_entry(module, field, f); + } + + pub fn add_memory(&mut self, module: N1, field: N2, mem: Memory) + where + N1: Into>, + N2: Into>, + { + let mem = sandbox_primitives::ExternEntity::Memory(mem.memory_idx as u32); + self.add_entry(module, field, mem); + } +} + +pub struct Instance { + instance_idx: u32, + _marker: marker::PhantomData, +} + +/// The primary responsibility of this thunk is to deserialize arguments and +/// call the original function, specified by the index. +extern "C" fn dispatch_thunk( + serialized_args_ptr: *const u8, + serialized_args_len: usize, + state: usize, + f: ffi::HostFuncIndex, +) -> u64 { + let serialized_args = unsafe { + if serialized_args_len == 0 { + &[] + } else { + slice::from_raw_parts(serialized_args_ptr, serialized_args_len) + } + }; + let args = Vec::::decode(&mut &serialized_args[..]).expect( + "serialized args should be provided by the runtime; + correctly serialized data should be deserializable; + qed", + ); + + unsafe { + // This should be safe since `coerce_host_index_to_func` is called with an argument + // received in an `dispatch_thunk` implementation, so `f` should point + // on a valid host function. + let f = ffi::coerce_host_index_to_func(f); + + // This should be safe since mutable reference to T is passed upon the invocation. + let state = &mut *(state as *mut T); + + // Pass control flow to the designated function. + let result = f(state, &args).encode(); + + // Leak the result vector and return the pointer to return data. + let result_ptr = result.as_ptr() as u64; + let result_len = result.len() as u64; + mem::forget(result); + + (result_ptr << 32) | result_len + } +} + +impl Instance { + pub fn new(code: &[u8], env_def_builder: &EnvironmentDefinitionBuilder, state: &mut T) -> Result, Error> { + let serialized_env_def: Vec = env_def_builder.env_def.encode(); + let result = unsafe { + // It's very important to instantiate thunk with the right type. + let dispatch_thunk = dispatch_thunk::; + + ffi::ext_sandbox_instantiate( + dispatch_thunk, + code.as_ptr(), + code.len(), + serialized_env_def.as_ptr(), + serialized_env_def.len(), + state as *const T as usize, + ) + }; + let instance_idx = match result { + sandbox_primitives::ERR_MODULE => return Err(Error::Module), + instance_idx => instance_idx, + }; + Ok(Instance { + instance_idx, + _marker: marker::PhantomData::, + }) + } + + pub fn invoke( + &mut self, + name: &[u8], + args: &[TypedValue], + state: &mut T, + ) -> Result { + let serialized_args = args.to_vec().encode(); + let mut return_val = vec![0u8; sandbox_primitives::ReturnValue::ENCODED_MAX_SIZE]; + + let result = unsafe { + ffi::ext_sandbox_invoke( + self.instance_idx, + name.as_ptr(), + name.len(), + serialized_args.as_ptr(), + serialized_args.len(), + return_val.as_mut_ptr(), + return_val.len(), + state as *const T as usize, + ) + }; + match result { + sandbox_primitives::ERR_OK => { + let return_val = sandbox_primitives::ReturnValue::decode(&mut &return_val[..]) + .ok_or(Error::Execution)?; + Ok(return_val) + } + sandbox_primitives::ERR_EXECUTION => Err(Error::Execution), + _ => unreachable!(), + } + } +} + +impl Drop for Instance { + fn drop(&mut self) { + unsafe { + ffi::ext_sandbox_instance_teardown(self.instance_idx); + } + } +} diff --git a/core/runtime-std/Cargo.toml b/core/runtime-std/Cargo.toml new file mode 100644 index 0000000000000..42745a0152a10 --- /dev/null +++ b/core/runtime-std/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "substrate-runtime-std" +version = "0.1.0" +authors = ["Parity Technologies "] +build = "build.rs" + +[build-dependencies] +rustc_version = "0.2" + +[dependencies] +pwasm-alloc = { path = "../pwasm-alloc" } +pwasm-libc = { path = "../pwasm-libc" } + +[features] +default = ["std"] +std = [] +nightly = [] +strict = [] diff --git a/core/runtime-std/README.adoc b/core/runtime-std/README.adoc new file mode 100644 index 0000000000000..36ea8d99c2262 --- /dev/null +++ b/core/runtime-std/README.adoc @@ -0,0 +1,14 @@ + += Runtime std + +.Summary +[source, toml] +---- +include::Cargo.toml[lines=2..5] +---- + +.Description +---- +include::src/lib.rs[tag=description] +---- + diff --git a/core/runtime-std/build.rs b/core/runtime-std/build.rs new file mode 100644 index 0000000000000..55688bad9cc51 --- /dev/null +++ b/core/runtime-std/build.rs @@ -0,0 +1,14 @@ +//! Set a nightly feature + +extern crate rustc_version; +use rustc_version::{version, version_meta, Channel}; + +fn main() { + // Assert we haven't travelled back in time + assert!(version().unwrap().major >= 1); + + // Set cfg flags depending on release channel + if let Channel::Nightly = version_meta().unwrap().channel { + println!("cargo:rustc-cfg=feature=\"nightly\""); + } +} diff --git a/core/runtime-std/src/lib.rs b/core/runtime-std/src/lib.rs new file mode 100644 index 0000000000000..d0fddddd298af --- /dev/null +++ b/core/runtime-std/src/lib.rs @@ -0,0 +1,51 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +// tag::description[] +//! Lowest-abstraction level for the Substrate runtime: just exports useful primitives from std +//! or core/alloc to be used with any code that depends on the runtime. +// end::description[] + +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(not(feature = "std"), feature(panic_handler))] +#![cfg_attr(not(feature = "std"), feature(core_intrinsics))] +#![cfg_attr(not(feature = "std"), feature(alloc))] + +#![cfg_attr(feature = "std", doc = "Polkadot runtime standard library as compiled when linked with Rust's standard library.")] +#![cfg_attr(not(feature = "std"), doc = "Polkadot's runtime standard library as compiled without Rust's standard library.")] + +#[macro_export] +macro_rules! map { + ($( $name:expr => $value:expr ),*) => ( + vec![ $( ( $name, $value ) ),* ].into_iter().collect() + ) +} + +#[cfg(feature = "std")] +include!("../with_std.rs"); + +#[cfg(not(feature = "std"))] +include!("../without_std.rs"); + +/// Prelude of common useful imports. +/// +/// This should include only things which are in the normal std prelude. +pub mod prelude { + pub use ::vec::Vec; + pub use ::boxed::Box; + pub use ::cmp::{Eq, PartialEq}; + pub use ::clone::Clone; +} diff --git a/core/runtime-std/with_std.rs b/core/runtime-std/with_std.rs new file mode 100644 index 0000000000000..443fea1a61717 --- /dev/null +++ b/core/runtime-std/with_std.rs @@ -0,0 +1,38 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +pub use std::borrow; +pub use std::boxed; +pub use std::cell; +pub use std::clone; +pub use std::cmp; +pub use std::fmt; +pub use std::hash; +pub use std::iter; +pub use std::marker; +pub use std::mem; +pub use std::num; +pub use std::ops; +pub use std::ptr; +pub use std::rc; +pub use std::slice; +pub use std::string; +pub use std::vec; +pub use std::result; + +pub mod collections { + pub use std::collections::btree_map; +} diff --git a/core/runtime-std/without_std.rs b/core/runtime-std/without_std.rs new file mode 100644 index 0000000000000..5219cba062703 --- /dev/null +++ b/core/runtime-std/without_std.rs @@ -0,0 +1,46 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +#[cfg(feature = "nightly")] +extern crate alloc; +#[cfg(feature = "nightly")] +extern crate pwasm_libc; +#[cfg(feature = "nightly")] +extern crate pwasm_alloc; + +pub use alloc::boxed; +pub use alloc::rc; +pub use alloc::vec; +pub use alloc::string; +pub use core::borrow; +pub use core::cell; +pub use core::clone; +pub use core::cmp; +pub use core::fmt; +pub use core::hash; +pub use core::intrinsics; +pub use core::iter; +pub use core::marker; +pub use core::mem; +pub use core::num; +pub use core::ops; +pub use core::ptr; +pub use core::slice; +pub use core::result; + +pub mod collections { + pub use alloc::collections::btree_map; +} diff --git a/core/serializer/Cargo.toml b/core/serializer/Cargo.toml new file mode 100644 index 0000000000000..412586599e6ce --- /dev/null +++ b/core/serializer/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "substrate-serializer" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +serde = { version = "1.0", default_features = false } +serde_json = "1.0" diff --git a/core/serializer/README.adoc b/core/serializer/README.adoc new file mode 100644 index 0000000000000..7b780bae43677 --- /dev/null +++ b/core/serializer/README.adoc @@ -0,0 +1,14 @@ + += Serializer + +.Summary +[source, toml] +---- +include::Cargo.toml[lines=2..5] +---- + +.Description +---- +include::src/lib.rs[tag=description] +---- + diff --git a/core/serializer/src/lib.rs b/core/serializer/src/lib.rs new file mode 100644 index 0000000000000..48afc57497354 --- /dev/null +++ b/core/serializer/src/lib.rs @@ -0,0 +1,46 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +// tag::description[] +//! Substrate customizable serde serializer. +//! +//! The idea is that we can later change the implementation +//! to something more compact, but for now we're using JSON. +// end::description[] + +#![warn(missing_docs)] + +extern crate serde; +extern crate serde_json; + +pub use serde_json::{from_str, from_slice, from_reader, Result, Error}; + +const PROOF: &str = "Serializers are infallible; qed"; + +/// Serialize the given data structure as a pretty-printed String of JSON. +pub fn to_string_pretty(value: &T) -> String { + serde_json::to_string_pretty(value).expect(PROOF) +} + +/// Serialize the given data structure as a JSON byte vector. +pub fn encode(value: &T) -> Vec { + serde_json::to_vec(value).expect(PROOF) +} + +/// Serialize the given data structure as JSON into the IO stream. +pub fn to_writer(writer: W, value: &T) -> Result<()> { + serde_json::to_writer(writer, value) +} diff --git a/core/service/Cargo.toml b/core/service/Cargo.toml new file mode 100644 index 0000000000000..e2ddcb0704bc0 --- /dev/null +++ b/core/service/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "substrate-service" +version = "0.3.0" +authors = ["Parity Technologies "] + +[dependencies] +futures = "0.1.17" +parking_lot = "0.4" +error-chain = "0.12" +lazy_static = "1.0" +log = "0.3" +slog = "^2" +tokio = "0.1.7" +exit-future = "0.1" +serde = "1.0" +serde_json = "1.0" +serde_derive = "1.0" +target_info = "0.1" +substrate-keystore = { path = "../keystore" } +substrate-runtime-io = { path = "../runtime-io" } +substrate-runtime-primitives = { path = "../../runtime/primitives" } +substrate-primitives = { path = "../primitives" } +substrate-network = { path = "../network" } +substrate-client = { path = "../client" } +substrate-client-db = { path = "../client/db" } +substrate-codec = { path = "../codec" } +substrate-executor = { path = "../executor" } +substrate-extrinsic-pool = { path = "../extrinsic-pool" } +substrate-rpc = { path = "../rpc" } +substrate-rpc-servers = { path = "../rpc-servers" } +substrate-telemetry = { path = "../telemetry" } diff --git a/core/service/README.adoc b/core/service/README.adoc new file mode 100644 index 0000000000000..4d74c098b2e17 --- /dev/null +++ b/core/service/README.adoc @@ -0,0 +1,14 @@ + += Service + +.Summary +[source, toml] +---- +include::Cargo.toml[lines=2..5] +---- + +.Description +---- +include::src/lib.rs[tag=description] +---- + diff --git a/core/service/src/chain_ops.rs b/core/service/src/chain_ops.rs new file mode 100644 index 0000000000000..aeefda6a676a0 --- /dev/null +++ b/core/service/src/chain_ops.rs @@ -0,0 +1,139 @@ +// Copyright 2017 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 . + +//! Chain utilities. + +use std::{self, io::{Read, Write}}; +use futures::Future; +use serde_json; + +use client::BlockOrigin; +use runtime_primitives::generic::{SignedBlock, BlockId}; +use runtime_primitives::traits::{As}; +use components::{ServiceFactory, FactoryFullConfiguration, FactoryBlockNumber, RuntimeGenesis}; +use new_client; +use codec::{Decode, Encode}; +use error; +use chain_spec::ChainSpec; + +/// Export a range of blocks to a binary stream. +pub fn export_blocks(config: FactoryFullConfiguration, exit: E, mut output: W, from: FactoryBlockNumber, to: Option>, json: bool) -> error::Result<()> + where F: ServiceFactory, E: Future + Send + 'static, W: Write, +{ + let client = new_client::(config)?; + let mut block = from; + + let last = match to { + Some(v) if v == As::sa(0) => As::sa(1), + Some(v) => v, + None => client.info()?.chain.best_number, + }; + + if last < block { + return Err("Invalid block range specified".into()); + } + + let (exit_send, exit_recv) = std::sync::mpsc::channel(); + ::std::thread::spawn(move || { + let _ = exit.wait(); + let _ = exit_send.send(()); + }); + info!("Exporting blocks from #{} to #{}", block, last); + if !json { + output.write(&(last - block + As::sa(1)).encode())?; + } + + loop { + if exit_recv.try_recv().is_ok() { + break; + } + match client.block(&BlockId::number(block))? { + Some(block) => { + if json { + serde_json::to_writer(&mut output, &block).map_err(|e| format!("Eror writing JSON: {}", e))?; + } else { + output.write(&block.encode())?; + } + }, + None => break, + } + if block.as_() % 10000 == 0 { + info!("#{}", block); + } + if block == last { + break; + } + block += As::sa(1); + } + Ok(()) +} + +/// Import blocks from a binary stream. +pub fn import_blocks(config: FactoryFullConfiguration, exit: E, mut input: R) -> error::Result<()> + where F: ServiceFactory, E: Future + Send + 'static, R: Read, +{ + let client = new_client::(config)?; + + let (exit_send, exit_recv) = std::sync::mpsc::channel(); + ::std::thread::spawn(move || { + let _ = exit.wait(); + let _ = exit_send.send(()); + }); + + let count: u32 = Decode::decode(&mut input).ok_or("Error reading file")?; + info!("Importing {} blocks", count); + let mut block = 0; + for _ in 0 .. count { + if exit_recv.try_recv().is_ok() { + break; + } + match SignedBlock::decode(&mut input) { + Some(block) => { + let header = client.check_justification(block.block.header, block.justification.into())?; + client.import_block(BlockOrigin::File, header, Some(block.block.extrinsics))?; + }, + None => { + warn!("Error reading block data."); + break; + } + } + block += 1; + if block % 1000 == 0 { + info!("#{}", block); + } + } + info!("Imported {} blocks. Best: #{}", block, client.info()?.chain.best_number); + + Ok(()) +} + +/// Revert the chain. +pub fn revert_chain(config: FactoryFullConfiguration, blocks: FactoryBlockNumber) -> error::Result<()> + where F: ServiceFactory, +{ + let client = new_client::(config)?; + let reverted = client.revert(blocks)?; + let info = client.info()?.chain; + info!("Reverted {} blocks. Best: #{} ({})", reverted, info.best_number, info.best_hash); + Ok(()) +} + +/// Build a chain spec json +pub fn build_spec(spec: ChainSpec, raw: bool) -> error::Result + where G: RuntimeGenesis, +{ + Ok(spec.to_json(raw)?) +} diff --git a/core/service/src/chain_spec.rs b/core/service/src/chain_spec.rs new file mode 100644 index 0000000000000..6ccd0545b4435 --- /dev/null +++ b/core/service/src/chain_spec.rs @@ -0,0 +1,170 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Substrate chain configurations. + +use std::collections::HashMap; +use std::fs::File; +use std::path::PathBuf; +use primitives::storage::{StorageKey, StorageData}; +use runtime_primitives::{BuildStorage, StorageMap}; +use serde_json as json; +use components::RuntimeGenesis; + +enum GenesisSource { + File(PathBuf), + Embedded(&'static [u8]), + Factory(fn() -> G), +} + +impl GenesisSource { + fn resolve(&self) -> Result, String> { + #[derive(Serialize, Deserialize)] + struct GenesisContainer { + genesis: Genesis, + } + + match *self { + GenesisSource::File(ref path) => { + let file = File::open(path).map_err(|e| format!("Error opening spec file: {}", e))?; + let genesis: GenesisContainer = json::from_reader(file).map_err(|e| format!("Error parsing spec file: {}", e))?; + Ok(genesis.genesis) + }, + GenesisSource::Embedded(buf) => { + let genesis: GenesisContainer = json::from_reader(buf).map_err(|e| format!("Error parsing embedded file: {}", e))?; + Ok(genesis.genesis) + }, + GenesisSource::Factory(f) => Ok(Genesis::Runtime(f())), + } + } +} + +impl<'a, G: RuntimeGenesis> BuildStorage for &'a ChainSpec { + fn build_storage(self) -> Result { + match self.genesis.resolve()? { + Genesis::Runtime(gc) => gc.build_storage(), + Genesis::Raw(map) => Ok(map.into_iter().map(|(k, v)| (k.0, v.0)).collect()), + } + } +} + +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +#[serde(deny_unknown_fields)] +enum Genesis { + Runtime(G), + Raw(HashMap), +} + +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct ChainSpecFile { + pub name: String, + pub id: String, + pub boot_nodes: Vec, + pub telemetry_url: Option, +} + +/// A configuration of a chain. Can be used to build a genesis block. +pub struct ChainSpec { + spec: ChainSpecFile, + genesis: GenesisSource, +} + +impl ChainSpec { + pub fn boot_nodes(&self) -> &[String] { + &self.spec.boot_nodes + } + + pub fn name(&self) -> &str { + &self.spec.name + } + + pub fn id(&self) -> &str { + &self.spec.id + } + + pub fn telemetry_url(&self) -> Option<&str> { + self.spec.telemetry_url.as_ref().map(String::as_str) + } + + /// Parse json content into a `ChainSpec` + pub fn from_embedded(json: &'static [u8]) -> Result { + let spec = json::from_slice(json).map_err(|e| format!("Error parsing spec file: {}", e))?; + Ok(ChainSpec { + spec, + genesis: GenesisSource::Embedded(json), + }) + } + + /// Parse json file into a `ChainSpec` + pub fn from_json_file(path: PathBuf) -> Result { + let file = File::open(&path).map_err(|e| format!("Error opening spec file: {}", e))?; + let spec = json::from_reader(file).map_err(|e| format!("Error parsing spec file: {}", e))?; + Ok(ChainSpec { + spec, + genesis: GenesisSource::File(path), + }) + } + + /// Create hardcoded spec. + pub fn from_genesis( + name: &str, + id: &str, + constructor: fn() -> G, + boot_nodes: Vec, + telemetry_url: Option<&str> + ) -> Self + { + let spec = ChainSpecFile { + name: name.to_owned(), + id: id.to_owned(), + boot_nodes: boot_nodes, + telemetry_url: telemetry_url.map(str::to_owned), + }; + ChainSpec { + spec, + genesis: GenesisSource::Factory(constructor), + } + } + + /// Dump to json string. + pub fn to_json(self, raw: bool) -> Result { + #[derive(Serialize, Deserialize)] + struct Container { + #[serde(flatten)] + spec: ChainSpecFile, + #[serde(flatten)] + genesis: Genesis, + + }; + let genesis = match (raw, self.genesis.resolve()?) { + (true, Genesis::Runtime(g)) => { + let storage = g.build_storage()?.into_iter() + .map(|(k, v)| (StorageKey(k), StorageData(v))) + .collect(); + + Genesis::Raw(storage) + }, + (_, genesis) => genesis, + }; + let spec = Container { + spec: self.spec, + genesis, + }; + json::to_string_pretty(&spec).map_err(|e| format!("Error generating spec json: {}", e)) + } +} diff --git a/core/service/src/components.rs b/core/service/src/components.rs new file mode 100644 index 0000000000000..38088b905069d --- /dev/null +++ b/core/service/src/components.rs @@ -0,0 +1,256 @@ +// Copyright 2017 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 . + +//! Polkadot service components. + +use std::fmt; +use std::sync::Arc; +use std::marker::PhantomData; +use serde::{Serialize, de::DeserializeOwned}; +use chain_spec::ChainSpec; +use client_db; +use client::{self, Client}; +use error; +use network::{self, OnDemand}; +use substrate_executor::{NativeExecutor, NativeExecutionDispatch}; +use extrinsic_pool::{self, Options as ExtrinsicPoolOptions, Pool as ExtrinsicPool}; +use runtime_primitives::{traits::Block as BlockT, traits::Header as HeaderT, BuildStorage}; +use config::Configuration; +use primitives::{Blake2Hasher, RlpCodec, H256}; + +// Type aliases. +// These exist mainly to avoid typing `::Foo` all over the code. +/// Network service type for a factory. +pub type NetworkService = network::Service< + ::Block, + ::NetworkProtocol, + ::ExtrinsicHash, +>; + +/// Code executor type for a factory. +pub type CodeExecutor = NativeExecutor<::RuntimeDispatch>; + +/// Full client backend type for a factory. +pub type FullBackend = client_db::Backend<::Block>; + +/// Full client executor type for a factory. +pub type FullExecutor = client::LocalCallExecutor< + client_db::Backend<::Block>, + CodeExecutor, +>; + +/// Light client backend type for a factory. +pub type LightBackend = client::light::backend::Backend< + client_db::light::LightStorage<::Block>, + network::OnDemand<::Block, NetworkService>, +>; + +/// Light client executor type for a factory. +pub type LightExecutor = client::light::call_executor::RemoteCallExecutor< + client::light::blockchain::Blockchain< + client_db::light::LightStorage<::Block>, + network::OnDemand<::Block, NetworkService> + >, + network::OnDemand<::Block, NetworkService>, + Blake2Hasher, + RlpCodec, +>; + +/// Full client type for a factory. +pub type FullClient = Client, FullExecutor, ::Block>; + +/// Light client type for a factory. +pub type LightClient = Client, LightExecutor, ::Block>; + +/// `ChainSpec` specialization for a factory. +pub type FactoryChainSpec = ChainSpec<::Genesis>; + +/// `Genesis` specialization for a factory. +pub type FactoryGenesis = ::Genesis; + +/// `Block` type for a factory. +pub type FactoryBlock = ::Block; + +/// `Number` type for a factory. +pub type FactoryBlockNumber = < as BlockT>::Header as HeaderT>::Number; + +/// Full `Configuration` type for a factory. +pub type FactoryFullConfiguration = Configuration<::Configuration, FactoryGenesis>; + +/// Client type for `Components`. +pub type ComponentClient = Client< + ::Backend, + ::Executor, + FactoryBlock<::Factory> +>; + +/// Block type for `Components` +pub type ComponentBlock = <::Factory as ServiceFactory>::Block; + +/// Extrinsic hash type for `Components` +pub type ComponentExHash = <::ExtrinsicPoolApi as extrinsic_pool::ChainApi>::Hash; + +/// Extrinsic type. +pub type ComponentExtrinsic = as BlockT>::Extrinsic; + +/// Extrinsic pool API type for `Components`. +pub type PoolApi = ::ExtrinsicPoolApi; + +/// A set of traits for the runtime genesis config. +pub trait RuntimeGenesis: Serialize + DeserializeOwned + BuildStorage {} +impl RuntimeGenesis for T {} + +/// A collection of types and methods to build a service on top of the substrate service. +pub trait ServiceFactory: 'static { + /// Block type. + type Block: BlockT; + /// Extrinsic hash type. + type ExtrinsicHash: ::std::hash::Hash + Eq + Copy + fmt::Debug + fmt::LowerHex + Serialize + DeserializeOwned + ::std::str::FromStr + Send + Sync + Default + 'static; + /// Network protocol extensions. + type NetworkProtocol: network::specialization::Specialization; + /// Chain runtime. + type RuntimeDispatch: NativeExecutionDispatch + Send + Sync + 'static; + /// Extrinsic pool backend type for the full client. + type FullExtrinsicPoolApi: extrinsic_pool::ChainApi + Send + 'static; + /// Extrinsic pool backend type for the light client. + type LightExtrinsicPoolApi: extrinsic_pool::ChainApi + 'static; + /// Genesis configuration for the runtime. + type Genesis: RuntimeGenesis; + /// Other configuration for service members. + type Configuration: Default; + + /// Network protocol id. + const NETWORK_PROTOCOL_ID: network::ProtocolId; + + //TODO: replace these with a constructor trait. that ExtrinsicPool implements. + /// Extrinsic pool constructor for the full client. + fn build_full_extrinsic_pool(config: ExtrinsicPoolOptions, client: Arc>) + -> Result, error::Error>; + /// Extrinsic pool constructor for the light client. + fn build_light_extrinsic_pool(config: ExtrinsicPoolOptions, client: Arc>) + -> Result, error::Error>; + + /// Build network protocol. + fn build_network_protocol(config: &FactoryFullConfiguration) + -> Result; +} + +/// A collection of types and function to generalise over full / light client type. +pub trait Components: 'static { + /// Associated service factory. + type Factory: ServiceFactory; + /// Client backend. + type Backend: 'static + client::backend::Backend, Blake2Hasher, RlpCodec>; + /// Client executor. + type Executor: 'static + client::CallExecutor, Blake2Hasher, RlpCodec> + Send + Sync; + /// Extrinsic pool type. + type ExtrinsicPoolApi: 'static + extrinsic_pool::ChainApi::ExtrinsicHash, Block=FactoryBlock>; + + /// Create client. + fn build_client( + config: &FactoryFullConfiguration, + executor: CodeExecutor, + ) + -> Result<( + Arc>, + Option, NetworkService>>> + ), error::Error>; + + /// Create extrinsic pool. + fn build_extrinsic_pool(config: ExtrinsicPoolOptions, client: Arc>) + -> Result, error::Error>; +} + +/// A struct that implement `Components` for the full client. +pub struct FullComponents { + _factory: PhantomData, +} + +impl Components for FullComponents { + type Factory = Factory; + type Executor = FullExecutor; + type Backend = FullBackend; + type ExtrinsicPoolApi = ::FullExtrinsicPoolApi; + + fn build_client( + config: &FactoryFullConfiguration, + executor: CodeExecutor, + ) + -> Result<( + Arc>, + Option, NetworkService>>> + ), error::Error> + { + let db_settings = client_db::DatabaseSettings { + cache_size: None, + path: config.database_path.as_str().into(), + pruning: config.pruning.clone(), + }; + Ok((Arc::new(client_db::new_client(db_settings, executor, &config.chain_spec, config.execution_strategy)?), None)) + } + + fn build_extrinsic_pool(config: ExtrinsicPoolOptions, client: Arc>) + -> Result, error::Error> + { + Factory::build_full_extrinsic_pool(config, client) + } +} + +/// A struct that implement `Components` for the light client. +pub struct LightComponents { + _factory: PhantomData, +} + +impl Components for LightComponents + where + <::Block as BlockT>::Hash: From, + H256: From<<::Block as BlockT>::Hash>, +{ + type Factory = Factory; + type Executor = LightExecutor; + type Backend = LightBackend; + type ExtrinsicPoolApi = ::LightExtrinsicPoolApi; + + fn build_client( + config: &FactoryFullConfiguration, + executor: CodeExecutor, + ) + -> Result<( + Arc>, + Option, + NetworkService>>> + ), error::Error> + { + let db_settings = client_db::DatabaseSettings { + cache_size: None, + path: config.database_path.as_str().into(), + pruning: config.pruning.clone(), + }; + let db_storage = client_db::light::LightStorage::new(db_settings)?; + let light_blockchain = client::light::new_light_blockchain(db_storage); + let fetch_checker = Arc::new(client::light::new_fetch_checker::<_, Blake2Hasher, RlpCodec>(executor)); + let fetcher = Arc::new(network::OnDemand::new(fetch_checker)); + let client_backend = client::light::new_light_backend(light_blockchain, fetcher.clone()); + let client = client::light::new_light(client_backend, fetcher.clone(), &config.chain_spec)?; + Ok((Arc::new(client), Some(fetcher))) + } + + fn build_extrinsic_pool(config: ExtrinsicPoolOptions, client: Arc>) + -> Result, error::Error> + { + Factory::build_light_extrinsic_pool(config, client) + } +} diff --git a/core/service/src/config.rs b/core/service/src/config.rs new file mode 100644 index 0000000000000..fd0db61f8dd9a --- /dev/null +++ b/core/service/src/config.rs @@ -0,0 +1,121 @@ +// Copyright 2017 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 . + +//! Service configuration. + +use std::net::SocketAddr; +use extrinsic_pool; +use chain_spec::ChainSpec; +pub use client::ExecutionStrategy; +pub use network::Roles; +pub use network::NetworkConfiguration; +pub use client_db::PruningMode; +use runtime_primitives::BuildStorage; +use serde::{Serialize, de::DeserializeOwned}; +use target_info::Target; + +/// Service configuration. +pub struct Configuration { + /// Implementation name + pub impl_name: &'static str, + /// Implementation version + pub impl_version: &'static str, + /// Git commit if any. + pub impl_commit: &'static str, + /// Node roles. + pub roles: Roles, + /// Extrinsic pool configuration. + pub extrinsic_pool: extrinsic_pool::Options, + /// Network configuration. + pub network: NetworkConfiguration, + /// Path to key files. + pub keystore_path: String, + /// Path to the database. + pub database_path: String, + /// Pruning settings. + pub pruning: PruningMode, + /// Additional key seeds. + pub keys: Vec, + /// Chain configuration. + pub chain_spec: ChainSpec, + /// Custom configuration. + pub custom: C, + /// Telemetry server URL, optional - only `Some` if telemetry reporting is enabled + pub telemetry: Option, + /// Node name. + pub name: String, + /// Execution strategy. + pub execution_strategy: ExecutionStrategy, + /// RPC over HTTP binding address. `None` if disabled. + pub rpc_http: Option, + /// RPC over Websockets binding address. `None` if disabled. + pub rpc_ws: Option, + /// Telemetry service URL. `None` if disabled. + pub telemetry_url: Option, +} + +impl Configuration { + /// Create default config for given chain spec. + pub fn default_with_spec(chain_spec: ChainSpec) -> Self { + let mut configuration = Configuration { + impl_name: "parity-substrate", + impl_version: "0.0.0", + impl_commit: "", + chain_spec, + name: Default::default(), + roles: Roles::FULL, + extrinsic_pool: Default::default(), + network: Default::default(), + keystore_path: Default::default(), + database_path: Default::default(), + keys: Default::default(), + custom: Default::default(), + telemetry: Default::default(), + pruning: PruningMode::default(), + execution_strategy: ExecutionStrategy::Both, + rpc_http: None, + rpc_ws: None, + telemetry_url: None, + }; + configuration.network.boot_nodes = configuration.chain_spec.boot_nodes().to_vec(); + configuration.telemetry_url = configuration.chain_spec.telemetry_url().map(str::to_owned); + configuration + } + + /// Returns full version string of this configuration. + pub fn full_version(&self) -> String { + full_version_from_strs(self.impl_version, self.impl_commit) + } + + /// Implementation id and version. + pub fn client_id(&self) -> String { + format!("{}/v{}", self.impl_name, self.full_version()) + } +} + +/// Returns platform info +pub fn platform() -> String { + let env = Target::env(); + let env_dash = if env.is_empty() { "" } else { "-" }; + format!("{}-{}{}{}", Target::arch(), Target::os(), env_dash, env) +} + +/// Returns full version string, using supplied version and commit. +pub fn full_version_from_strs(impl_version: &str, impl_commit: &str) -> String { + let commit_dash = if impl_commit.is_empty() { "" } else { "-" }; + format!("{}{}{}-{}", impl_version, commit_dash, impl_commit, platform()) +} + diff --git a/core/service/src/error.rs b/core/service/src/error.rs new file mode 100644 index 0000000000000..abc09cb0c3c45 --- /dev/null +++ b/core/service/src/error.rs @@ -0,0 +1,35 @@ +// Copyright 2017 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 . + +//! Errors that can occur during the service operation. + +use client; +use network; +use keystore; + +error_chain! { + foreign_links { + Io(::std::io::Error) #[doc="IO error"]; + } + links { + Client(client::error::Error, client::error::ErrorKind) #[doc="Client error"]; + Network(network::error::Error, network::error::ErrorKind) #[doc="Network error"]; + Keystore(keystore::Error, keystore::ErrorKind) #[doc="Keystore error"]; + } + + errors { + } +} diff --git a/core/service/src/lib.rs b/core/service/src/lib.rs new file mode 100644 index 0000000000000..d7c1e90e599d9 --- /dev/null +++ b/core/service/src/lib.rs @@ -0,0 +1,424 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +// tag::description[] +//! Substrate service. Starts a thread that spins the network, the client and the extrinsic pool. +//! Manages communication between them. +// end::description[] + +#![warn(unused_extern_crates)] + +extern crate futures; +extern crate exit_future; +extern crate serde; +extern crate serde_json; +extern crate substrate_keystore as keystore; +extern crate substrate_primitives as primitives; +extern crate substrate_runtime_primitives as runtime_primitives; +extern crate substrate_network as network; +extern crate substrate_executor; +extern crate substrate_client as client; +extern crate substrate_client_db as client_db; +extern crate substrate_codec as codec; +extern crate substrate_extrinsic_pool as extrinsic_pool; +extern crate substrate_rpc; +extern crate substrate_rpc_servers as rpc; +extern crate target_info; +extern crate tokio; + +#[macro_use] +extern crate substrate_telemetry as tel; +#[macro_use] +extern crate error_chain; +#[macro_use] +extern crate slog; // needed until we can reexport `slog_info` from `substrate_telemetry` +#[macro_use] +extern crate log; +#[macro_use] +extern crate serde_derive; + +mod components; +mod error; +mod chain_spec; +pub mod config; +pub mod chain_ops; + +use std::io; +use std::net::SocketAddr; +use std::sync::Arc; +use std::collections::HashMap; +use futures::prelude::*; +use keystore::Store as Keystore; +use client::BlockchainEvents; +use runtime_primitives::traits::{Header, As}; +use runtime_primitives::generic::BlockId; +use exit_future::Signal; +use tokio::runtime::TaskExecutor; +use substrate_executor::NativeExecutor; +use codec::{Encode, Decode}; + +pub use self::error::{ErrorKind, Error}; +pub use config::{Configuration, Roles, PruningMode}; +pub use chain_spec::ChainSpec; +pub use extrinsic_pool::{Pool as ExtrinsicPool, Options as ExtrinsicPoolOptions, ChainApi, VerifiedTransaction, IntoPoolError}; +pub use client::ExecutionStrategy; + +pub use components::{ServiceFactory, FullBackend, FullExecutor, LightBackend, + LightExecutor, Components, PoolApi, ComponentClient, + ComponentBlock, FullClient, LightClient, FullComponents, LightComponents, + CodeExecutor, NetworkService, FactoryChainSpec, FactoryBlock, + FactoryFullConfiguration, RuntimeGenesis, FactoryGenesis, + ComponentExHash, ComponentExtrinsic, +}; + +/// Substrate service. +pub struct Service { + client: Arc>, + network: Option>>, + extrinsic_pool: Arc>, + keystore: Keystore, + exit: ::exit_future::Exit, + signal: Option, + _rpc_http: Option, + _rpc_ws: Option, + _telemetry: Option, +} + +/// Creates bare client without any networking. +pub fn new_client(config: FactoryFullConfiguration) + -> Result>>, error::Error> +{ + let executor = NativeExecutor::new(); + let (client, _) = components::FullComponents::::build_client( + &config, + executor, + )?; + Ok(client) +} + +impl Service + where + Components: components::Components, +{ + /// Creates a new service. + pub fn new( + config: FactoryFullConfiguration, + task_executor: TaskExecutor, + ) + -> Result + { + let (signal, exit) = ::exit_future::signal(); + + // Create client + let executor = NativeExecutor::new(); + + let mut keystore = Keystore::open(config.keystore_path.as_str().into())?; + 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("")?; + let public_key = key.public(); + info!("Generated a new keypair: {:?}", public_key); + + public_key + } + }; + + let (client, on_demand) = Components::build_client(&config, executor)?; + let best_header = client.best_block_header()?; + + let version = config.full_version(); + info!("Best block: #{}", best_header.number()); + telemetry!("node.start"; "height" => best_header.number().as_(), "best" => ?best_header.hash()); + + let network_protocol = ::build_network_protocol(&config)?; + let extrinsic_pool = Arc::new( + Components::build_extrinsic_pool(config.extrinsic_pool, client.clone())? + ); + let extrinsic_pool_adapter = ExtrinsicPoolAdapter:: { + imports_external_transactions: !config.roles == Roles::LIGHT, + pool: extrinsic_pool.clone(), + client: client.clone(), + }; + + let network_params = network::Params { + config: network::ProtocolConfig { + roles: config.roles, + }, + network_config: config.network, + chain: client.clone(), + on_demand: on_demand.clone() + .map(|d| d as Arc>>), + transaction_pool: Arc::new(extrinsic_pool_adapter), + specialization: network_protocol, + }; + + let network = network::Service::new(network_params, Components::Factory::NETWORK_PROTOCOL_ID)?; + on_demand.map(|on_demand| on_demand.set_service_link(Arc::downgrade(&network))); + + { + // block notifications + let network = network.clone(); + let txpool = extrinsic_pool.clone(); + + let events = client.import_notification_stream() + .for_each(move |notification| { + network.on_block_imported(notification.hash, ¬ification.header); + txpool.cull(&BlockId::hash(notification.hash)) + .map_err(|e| warn!("Error removing extrinsics: {:?}", e))?; + Ok(()) + }) + .select(exit.clone()) + .then(|_| Ok(())); + task_executor.spawn(events); + } + + { + // extrinsic notifications + let network = network.clone(); + let events = extrinsic_pool.import_notification_stream() + // TODO [ToDr] Consider throttling? + .for_each(move |_| { + network.trigger_repropagate(); + Ok(()) + }) + .select(exit.clone()) + .then(|_| Ok(())); + + task_executor.spawn(events); + } + + // RPC + let rpc_config = RpcConfig { + chain_name: config.chain_spec.name().to_string(), + impl_name: config.impl_name, + impl_version: config.impl_version, + }; + + let (rpc_http, rpc_ws) = { + let handler = || { + let client = client.clone(); + let chain = rpc::apis::chain::Chain::new(client.clone(), task_executor.clone()); + let state = rpc::apis::state::State::new(client.clone(), task_executor.clone()); + let author = rpc::apis::author::Author::new(client.clone(), extrinsic_pool.clone(), task_executor.clone()); + rpc::rpc_handler::, ComponentExHash, _, _, _, _, _>( + state, + chain, + author, + rpc_config.clone(), + ) + }; + ( + maybe_start_server(config.rpc_http, |address| rpc::start_http(address, handler()))?, + maybe_start_server(config.rpc_ws, |address| rpc::start_ws(address, handler()))?, + ) + }; + + // Telemetry + let telemetry = match config.telemetry_url { + Some(url) => { + let is_authority = config.roles == Roles::AUTHORITY; + let pubkey = format!("{}", public_key); + let name = config.name.clone(); + let impl_name = config.impl_name.to_owned(); + let version = version.clone(); + let chain_name = config.chain_spec.name().to_owned(); + Some(tel::init_telemetry(tel::TelemetryConfig { + url: url, + on_connect: Box::new(move || { + telemetry!("system.connected"; + "name" => name.clone(), + "implementation" => impl_name.clone(), + "version" => version.clone(), + "config" => "", + "chain" => chain_name.clone(), + "pubkey" => &pubkey, + "authority" => is_authority + ); + }), + })) + }, + None => None, + }; + + Ok(Service { + client: client, + network: Some(network), + extrinsic_pool: extrinsic_pool, + signal: Some(signal), + keystore: keystore, + exit, + _rpc_http: rpc_http, + _rpc_ws: rpc_ws, + _telemetry: telemetry, + }) + } + + /// Get shared client instance. + pub fn client(&self) -> Arc> { + self.client.clone() + } + + /// Get shared network instance. + pub fn network(&self) -> Arc> { + self.network.as_ref().expect("self.network always Some").clone() + } + + /// Get shared extrinsic pool instance. + pub fn extrinsic_pool(&self) -> Arc> { + self.extrinsic_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 Drop for Service where Components: components::Components { + fn drop(&mut self) { + debug!(target: "service", "Substrate service shutdown"); + + drop(self.network.take()); + + if let Some(signal) = self.signal.take() { + signal.fire(); + } + } +} + +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, + }) +} + +#[derive(Clone)] +struct RpcConfig { + chain_name: String, + impl_name: &'static str, + impl_version: &'static str, +} + +impl substrate_rpc::system::SystemApi for RpcConfig { + fn system_name(&self) -> substrate_rpc::system::error::Result { + Ok(self.impl_name.into()) + } + + fn system_version(&self) -> substrate_rpc::system::error::Result { + Ok(self.impl_version.into()) + } + + fn system_chain(&self) -> substrate_rpc::system::error::Result { + Ok(self.chain_name.clone()) + } +} + +/// Transaction pool adapter. +pub struct ExtrinsicPoolAdapter { + imports_external_transactions: bool, + pool: Arc>, + client: Arc>, +} + +impl ExtrinsicPoolAdapter { + fn best_block_id(&self) -> Option>> { + self.client.info() + .map(|info| BlockId::hash(info.chain.best_hash)) + .map_err(|e| { + debug!("Error getting best block: {:?}", e); + }) + .ok() + } +} + +impl network::TransactionPool, ComponentBlock> for ExtrinsicPoolAdapter { + fn transactions(&self) -> Vec<(ComponentExHash, ComponentExtrinsic)> { + let best_block_id = match self.best_block_id() { + Some(id) => id, + None => return vec![], + }; + self.pool.cull_and_get_pending(&best_block_id, |pending| pending + .map(|t| { + let hash = t.hash().clone(); + let ex: ComponentExtrinsic = t.original.clone(); + (hash, ex) + }) + .collect() + ).unwrap_or_else(|e| { + warn!("Error retrieving pending set: {}", e); + vec![] + }) + } + + fn import(&self, transaction: &ComponentExtrinsic) -> Option> { + if !self.imports_external_transactions { + return None; + } + + let encoded = transaction.encode(); + if let Some(uxt) = Decode::decode(&mut &encoded[..]) { + let best_block_id = self.best_block_id()?; + match self.pool.submit_one(&best_block_id, uxt) { + Ok(xt) => Some(*xt.hash()), + Err(e) => match e.into_pool_error() { + Ok(e) => match e.kind() { + extrinsic_pool::ErrorKind::AlreadyImported(hash) => + Some(::std::str::FromStr::from_str(&hash).map_err(|_| {}) + .expect("Hash string is always valid")), + _ => { + debug!("Error adding transaction to the pool: {:?}", e); + None + }, + }, + Err(e) => { + debug!("Error converting pool error: {:?}", e); + None + } + } + } + } else { + debug!("Error decoding transaction"); + None + } + } + + fn on_broadcasted(&self, propagations: HashMap, Vec>) { + self.pool.on_broadcasted(propagations) + } +} diff --git a/core/state-db/Cargo.toml b/core/state-db/Cargo.toml new file mode 100644 index 0000000000000..95e4798192218 --- /dev/null +++ b/core/state-db/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "substrate-state-db" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +parking_lot = "0.5" +log = "0.4" +substrate-primitives = { path = "../primitives" } +substrate-codec = { path = "../codec" } +substrate-codec-derive = { path = "../codec/derive" } + +[dev-dependencies] +env_logger = "0.4" diff --git a/core/state-db/README.adoc b/core/state-db/README.adoc new file mode 100644 index 0000000000000..f9934ed8d9ac6 --- /dev/null +++ b/core/state-db/README.adoc @@ -0,0 +1,13 @@ + += State DB + +.Summary +[source, toml] +---- +include::Cargo.toml[lines=2..5] +---- + +.Description +---- +include::src/lib.rs[tag=description] +---- diff --git a/core/state-db/src/lib.rs b/core/state-db/src/lib.rs new file mode 100644 index 0000000000000..7e7dd3369c918 --- /dev/null +++ b/core/state-db/src/lib.rs @@ -0,0 +1,407 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +// tag::description[] +//! State database maintenance. Handles finalization and pruning in the database. The input to +//! this module is a `ChangeSet` which is basically a list of key-value pairs (trie nodes) that +//! were added or deleted during block execution. +//! +//! # Finalization. +//! Finalization window tracks a tree of blocks identified by header hash. The in-memory +//! overlay allows to get any node that was was inserted in any any of the blocks within the window. +//! The tree is journaled to the backing database and rebuilt on startup. +//! Finalization function select one root from the top of the tree and discards all other roots and +//! their subtrees. +//! +//! # Pruning. +//! See `RefWindow` for pruning algorithm details. `StateDb` prunes on each finalization until pruning +//! constraints are satisfied. +//! +// end::description[] + +#[macro_use] extern crate log; +#[macro_use] extern crate substrate_codec_derive; +extern crate parking_lot; +extern crate substrate_codec as codec; +extern crate substrate_primitives as primitives; + +mod unfinalized; +mod pruning; +#[cfg(test)] mod test; + +use std::fmt; +use parking_lot::RwLock; +use codec::Codec; +use std::collections::HashSet; +use unfinalized::UnfinalizedOverlay; +use pruning::RefWindow; + +/// Database value type. +pub type DBValue = Vec; + +/// Basic set of requirements for the Block hash and node key types. +pub trait Hash: Send + Sync + Sized + Eq + PartialEq + Clone + Default + fmt::Debug + Codec + std::hash::Hash + 'static {} +impl Hash for T {} + +/// Backend database trait. Read-only. +pub trait MetaDb { + type Error: fmt::Debug; + + /// Get meta value, such as the journal. + fn get_meta(&self, key: &[u8]) -> Result, Self::Error>; +} + + +/// Backend database trait. Read-only. +pub trait HashDb { + type Hash: Hash; + type Error: fmt::Debug; + + /// Get state trie node. + fn get(&self, key: &Self::Hash) -> Result, Self::Error>; +} + +/// Error type. +pub enum Error { + /// Database backend error. + Db(E), + /// `Codec` decoding error. + Decoding, +} + +impl fmt::Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match self { + Error::Db(e) => e.fmt(f), + Error::Decoding => write!(f, "Error decoding slicable value"), + } + } +} + +/// A set of state node changes. +#[derive(Default, Debug, Clone)] +pub struct ChangeSet { + /// Inserted nodes. + pub inserted: Vec<(H, DBValue)>, + /// Deleted nodes. + pub deleted: Vec, +} + + +/// A set of changes to the backing database. +#[derive(Default, Debug, Clone)] +pub struct CommitSet { + /// State node changes. + pub data: ChangeSet, + /// Metadata changes. + pub meta: ChangeSet>, +} + +/// Pruning constraints. If none are specified pruning is +#[derive(Default, Debug, Clone)] +pub struct Constraints { + /// Maximum blocks. Defaults to 0 when unspecified, effectively keeping only unfinalized states. + pub max_blocks: Option, + /// Maximum memory in the pruning overlay. + pub max_mem: Option, +} + +/// Pruning mode. +#[derive(Debug, Clone)] +pub enum PruningMode { + /// Maintain a pruning window. + Constrained(Constraints), + /// No pruning. Finalization is a no-op. + ArchiveAll, + /// Finalization discards unfinalized nodes. All the finalized nodes are kept in the DB. + ArchiveCanonical, +} + +impl PruningMode { + /// Create a mode that keeps given number of blocks. + pub fn keep_blocks(n: u32) -> PruningMode { + PruningMode::Constrained(Constraints { + max_blocks: Some(n), + max_mem: None, + }) + } +} + +impl Default for PruningMode { + fn default() -> Self { + PruningMode::keep_blocks(256) + } +} + +fn to_meta_key(suffix: &[u8], data: &S) -> Vec { + let mut buffer = data.encode(); + buffer.extend(suffix); + buffer +} + +struct StateDbSync { + mode: PruningMode, + unfinalized: UnfinalizedOverlay, + pruning: Option>, + pinned: HashSet, +} + +impl StateDbSync { + pub fn new(mode: PruningMode, db: &D) -> Result, Error> { + trace!("StateDb settings: {:?}", mode); + let unfinalized: UnfinalizedOverlay = UnfinalizedOverlay::new(db)?; + let pruning: Option> = match mode { + PruningMode::Constrained(Constraints { + max_mem: Some(_), + .. + }) => unimplemented!(), + PruningMode::Constrained(_) => Some(RefWindow::new(db)?), + PruningMode::ArchiveAll | PruningMode::ArchiveCanonical => None, + }; + Ok(StateDbSync { + mode, + unfinalized, + pruning: pruning, + pinned: Default::default(), + }) + } + + pub fn insert_block(&mut self, hash: &BlockHash, number: u64, parent_hash: &BlockHash, mut changeset: ChangeSet) -> CommitSet { + if number == 0 { + return CommitSet { + data: changeset, + meta: Default::default(), + } + } + match self.mode { + PruningMode::ArchiveAll => { + changeset.deleted.clear(); + // write changes immediately + CommitSet { + data: changeset, + meta: Default::default(), + } + }, + PruningMode::Constrained(_) | PruningMode::ArchiveCanonical => { + self.unfinalized.insert(hash, number, parent_hash, changeset) + } + } + } + + pub fn finalize_block(&mut self, hash: &BlockHash) -> CommitSet { + // clear the temporary overlay from the previous finalization. + self.unfinalized.clear_overlay(); + let mut commit = match self.mode { + PruningMode::ArchiveAll => { + CommitSet::default() + }, + PruningMode::ArchiveCanonical => { + let mut commit = self.unfinalized.finalize(hash); + commit.data.deleted.clear(); + commit + }, + PruningMode::Constrained(_) => { + self.unfinalized.finalize(hash) + }, + }; + if let Some(ref mut pruning) = self.pruning { + pruning.note_finalized(hash, &mut commit); + } + self.prune(&mut commit); + commit + } + + pub fn best_finalized(&self) -> u64 { + return self.unfinalized.last_finalized_block_number() + } + + pub fn is_pruned(&self, number: u64) -> bool { + self.pruning.as_ref().map_or(false, |pruning| number < pruning.pending()) + } + + fn prune(&mut self, commit: &mut CommitSet) { + if let (&mut Some(ref mut pruning), &PruningMode::Constrained(ref constraints)) = (&mut self.pruning, &self.mode) { + loop { + if pruning.window_size() <= constraints.max_blocks.unwrap_or(0) as u64 { + break; + } + + if constraints.max_mem.map_or(false, |m| pruning.mem_used() > m) { + break; + } + + let pinned = &self.pinned; + if pruning.next_hash().map_or(false, |h| pinned.contains(&h)) { + break; + } + pruning.prune_one(commit); + } + } + } + + /// Revert all unfinalized blocks with the best block number. + /// Returns a database commit or `None` if not possible. + /// For archive an empty commit set is returned. + pub fn revert_one(&mut self) -> Option> { + match self.mode { + PruningMode::ArchiveAll => { + Some(CommitSet::default()) + }, + PruningMode::ArchiveCanonical | PruningMode::Constrained(_) => { + self.unfinalized.revert_one() + }, + } + } + + pub fn pin(&mut self, hash: &BlockHash) { + self.pinned.insert(hash.clone()); + } + + pub fn unpin(&mut self, hash: &BlockHash) { + self.pinned.remove(hash); + } + + pub fn get>(&self, key: &Key, db: &D) -> Result, Error> { + if let Some(value) = self.unfinalized.get(key) { + return Ok(Some(value)); + } + db.get(key).map_err(|e| Error::Db(e)) + } +} + +/// State DB maintenance. See module description. +/// Can be shared across threads. +pub struct StateDb { + db: RwLock>, +} + +impl StateDb { + /// Creates a new instance. Does not expect any metadata in the database. + pub fn new(mode: PruningMode, db: &D) -> Result, Error> { + Ok(StateDb { + db: RwLock::new(StateDbSync::new(mode, db)?) + }) + } + + /// Add a new unfinalized block. + pub fn insert_block(&self, hash: &BlockHash, number: u64, parent_hash: &BlockHash, changeset: ChangeSet) -> CommitSet { + self.db.write().insert_block(hash, number, parent_hash, changeset) + } + + /// Finalize a previously inserted block. + pub fn finalize_block(&self, hash: &BlockHash) -> CommitSet { + self.db.write().finalize_block(hash) + } + + /// Prevents pruning of specified block and its descendants. + pub fn pin(&self, hash: &BlockHash) { + self.db.write().pin(hash) + } + + /// Allows pruning of specified block. + pub fn unpin(&self, hash: &BlockHash) { + self.db.write().unpin(hash) + } + + /// Get a value from unfinalized/pruning overlay or the backing DB. + pub fn get>(&self, key: &Key, db: &D) -> Result, Error> { + self.db.read().get(key, db) + } + + /// Revert all unfinalized blocks with the best block number. + /// Returns a database commit or `None` if not possible. + /// For archive an empty commit set is returned. + pub fn revert_one(&self) -> Option> { + self.db.write().revert_one() + } + + /// Returns last finalized block number. + pub fn best_finalized(&self) -> u64 { + return self.db.read().best_finalized() + } + + /// Check if block is pruned away. + pub fn is_pruned(&self, number: u64) -> bool { + return self.db.read().is_pruned(number) + } +} + +#[cfg(test)] +mod tests { + use primitives::H256; + use {StateDb, PruningMode, Constraints}; + use test::{make_db, make_changeset, TestDb}; + + fn make_test_db(settings: PruningMode) -> (TestDb, StateDb) { + let mut db = make_db(&[91, 921, 922, 93, 94]); + let state_db = StateDb::new(settings, &db).unwrap(); + + db.commit(&state_db.insert_block(&H256::from(1), 1, &H256::from(0), make_changeset(&[1], &[91]))); + db.commit(&state_db.insert_block(&H256::from(21), 2, &H256::from(1), make_changeset(&[21], &[921, 1]))); + db.commit(&state_db.insert_block(&H256::from(22), 2, &H256::from(1), make_changeset(&[22], &[922]))); + db.commit(&state_db.insert_block(&H256::from(3), 3, &H256::from(21), make_changeset(&[3], &[93]))); + db.commit(&state_db.finalize_block(&H256::from(1))); + db.commit(&state_db.insert_block(&H256::from(4), 4, &H256::from(3), make_changeset(&[4], &[94]))); + db.commit(&state_db.finalize_block(&H256::from(21))); + db.commit(&state_db.finalize_block(&H256::from(3))); + + (db, state_db) + } + + #[test] + fn full_archive_keeps_everything() { + let (db, _) = make_test_db(PruningMode::ArchiveAll); + assert!(db.data_eq(&make_db(&[1, 21, 22, 3, 4, 91, 921, 922, 93, 94]))); + } + + #[test] + fn canonical_archive_keeps_canonical() { + let (db, _) = make_test_db(PruningMode::ArchiveCanonical); + assert!(db.data_eq(&make_db(&[1, 21, 3, 91, 921, 922, 93, 94]))); + } + + #[test] + fn prune_window_0() { + let (db, _) = make_test_db(PruningMode::Constrained(Constraints { + max_blocks: Some(0), + max_mem: None, + })); + assert!(db.data_eq(&make_db(&[21, 3, 922, 94]))); + } + + #[test] + fn prune_window_1() { + let (db, sdb) = make_test_db(PruningMode::Constrained(Constraints { + max_blocks: Some(1), + max_mem: None, + })); + assert!(sdb.is_pruned(0)); + assert!(sdb.is_pruned(1)); + assert!(!sdb.is_pruned(2)); + assert!(db.data_eq(&make_db(&[21, 3, 922, 93, 94]))); + } + + #[test] + fn prune_window_2() { + let (db, sdb) = make_test_db(PruningMode::Constrained(Constraints { + max_blocks: Some(2), + max_mem: None, + })); + assert!(sdb.is_pruned(0)); + assert!(!sdb.is_pruned(1)); + assert!(db.data_eq(&make_db(&[1, 21, 3, 921, 922, 93, 94]))); + } +} diff --git a/core/state-db/src/pruning.rs b/core/state-db/src/pruning.rs new file mode 100644 index 0000000000000..aaacbd09b66ad --- /dev/null +++ b/core/state-db/src/pruning.rs @@ -0,0 +1,267 @@ +// Copyright 2017 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 . + +//! Pruning window. +//! +//! For each block we maintain a list of nodes pending deletion. +//! There is also a global index of node key to block number. +//! If a node is re-inserted into the window it gets removed from +//! the death list. +//! The changes are journaled in the DB. + +use std::collections::{HashMap, HashSet, VecDeque}; +use codec::{Encode, Decode}; +use {CommitSet, Error, MetaDb, to_meta_key, Hash}; + +const LAST_PRUNED: &[u8] = b"last_pruned"; +const PRUNING_JOURNAL: &[u8] = b"pruning_journal"; + +/// See module documentation. +pub struct RefWindow { + death_rows: VecDeque>, + death_index: HashMap, + pending_number: u64, +} + +#[derive(Debug, PartialEq, Eq)] +struct DeathRow { + hash: BlockHash, + journal_key: Vec, + deleted: HashSet, +} + +#[derive(Encode, Decode)] +struct JournalRecord { + hash: BlockHash, + inserted: Vec, + deleted: Vec, +} + +fn to_journal_key(block: u64) -> Vec { + to_meta_key(PRUNING_JOURNAL, &block) +} + +impl RefWindow { + pub fn new(db: &D) -> Result, Error> { + let last_pruned = db.get_meta(&to_meta_key(LAST_PRUNED, &())) + .map_err(|e| Error::Db(e))?; + let pending_number: u64 = match last_pruned { + Some(buffer) => u64::decode(&mut buffer.as_slice()).ok_or(Error::Decoding)? + 1, + None => 0, + }; + let mut block = pending_number; + let mut pruning = RefWindow { + death_rows: Default::default(), + death_index: Default::default(), + pending_number: pending_number, + }; + // read the journal + trace!(target: "state-db", "Reading pruning journal. Pending #{}", pending_number); + loop { + let journal_key = to_journal_key(block); + match db.get_meta(&journal_key).map_err(|e| Error::Db(e))? { + Some(record) => { + let record: JournalRecord = Decode::decode(&mut record.as_slice()).ok_or(Error::Decoding)?; + trace!(target: "state-db", "Pruning journal entry {} ({} inserted, {} deleted)", block, record.inserted.len(), record.deleted.len()); + pruning.import(&record.hash, journal_key, record.inserted.into_iter(), record.deleted); + }, + None => break, + } + block += 1; + } + Ok(pruning) + } + + fn import>(&mut self, hash: &BlockHash, journal_key: Vec, inserted: I, deleted: Vec) { + // remove all re-inserted keys from death rows + for k in inserted { + if let Some(block) = self.death_index.remove(&k) { + self.death_rows[(block - self.pending_number) as usize].deleted.remove(&k); + } + } + + // add new keys + let imported_block = self.pending_number + self.death_rows.len() as u64; + for k in deleted.iter() { + self.death_index.insert(k.clone(), imported_block); + } + self.death_rows.push_back( + DeathRow { + hash: hash.clone(), + deleted: deleted.into_iter().collect(), + journal_key: journal_key, + } + ); + } + + pub fn window_size(&self) -> u64 { + self.death_rows.len() as u64 + } + + pub fn next_hash(&self) -> Option { + self.death_rows.front().map(|r| r.hash.clone()) + } + + pub fn mem_used(&self) -> usize { + 0 + } + + pub fn pending(&self) -> u64 { + self.pending_number + } + + /// Prune next block. Expects at least one block in the window. Adds changes to `commit`. + pub fn prune_one(&mut self, commit: &mut CommitSet) { + let pruned = self.death_rows.pop_front().expect("prune_one is only called with a non-empty window"); + trace!(target: "state-db", "Pruning {:?} ({} deleted)", pruned.hash, pruned.deleted.len()); + for k in pruned.deleted.iter() { + self.death_index.remove(&k); + } + commit.data.deleted.extend(pruned.deleted.into_iter()); + commit.meta.inserted.push((to_meta_key(LAST_PRUNED, &()), self.pending_number.encode())); + commit.meta.deleted.push(pruned.journal_key); + self.pending_number += 1; + } + + /// Add a change set to the window. Creates a journal record and pushes it to `commit` + pub fn note_finalized(&mut self, hash: &BlockHash, commit: &mut CommitSet) { + trace!(target: "state-db", "Adding to pruning window: {:?} ({} inserted, {} deleted)", hash, commit.data.inserted.len(), commit.data.deleted.len()); + let inserted = commit.data.inserted.iter().map(|(k, _)| k.clone()).collect(); + let deleted = ::std::mem::replace(&mut commit.data.deleted, Vec::new()); + let journal_record = JournalRecord { + hash: hash.clone(), + inserted, + deleted, + }; + let block = self.pending_number + self.window_size(); + let journal_key = to_journal_key(block); + commit.meta.inserted.push((journal_key.clone(), journal_record.encode())); + + self.import(hash, journal_key, journal_record.inserted.into_iter(), journal_record.deleted); + } +} + +#[cfg(test)] +mod tests { + use super::RefWindow; + use primitives::H256; + use {CommitSet}; + use test::{make_db, make_commit, TestDb}; + + fn check_journal(pruning: &RefWindow, db: &TestDb) { + let restored: RefWindow = RefWindow::new(db).unwrap(); + assert_eq!(pruning.pending_number, restored.pending_number); + assert_eq!(pruning.death_rows, restored.death_rows); + assert_eq!(pruning.death_index, restored.death_index); + } + + #[test] + fn created_from_empty_db() { + let db = make_db(&[]); + let pruning: RefWindow = RefWindow::new(&db).unwrap(); + assert_eq!(pruning.pending_number, 0); + assert!(pruning.death_rows.is_empty()); + assert!(pruning.death_index.is_empty()); + } + + #[test] + #[should_panic] + fn prune_empty_panics() { + let db = make_db(&[]); + let mut pruning: RefWindow = RefWindow::new(&db).unwrap(); + let mut commit = CommitSet::default(); + pruning.prune_one(&mut commit); + } + + #[test] + fn prune_one() { + let mut db = make_db(&[1, 2, 3]); + let mut pruning: RefWindow = RefWindow::new(&db).unwrap(); + let mut commit = make_commit(&[4, 5], &[1, 3]); + let h = H256::random(); + pruning.note_finalized(&h, &mut commit); + db.commit(&commit); + assert!(commit.data.deleted.is_empty()); + assert_eq!(pruning.death_rows.len(), 1); + assert_eq!(pruning.death_index.len(), 2); + assert!(db.data_eq(&make_db(&[1, 2, 3, 4, 5]))); + check_journal(&pruning, &db); + + let mut commit = CommitSet::default(); + pruning.prune_one(&mut commit); + db.commit(&commit); + assert!(db.data_eq(&make_db(&[2, 4, 5]))); + assert!(pruning.death_rows.is_empty()); + assert!(pruning.death_index.is_empty()); + assert_eq!(pruning.pending_number, 1); + } + + #[test] + fn prune_two() { + let mut db = make_db(&[1, 2, 3]); + let mut pruning: RefWindow = RefWindow::new(&db).unwrap(); + let mut commit = make_commit(&[4], &[1]); + pruning.note_finalized(&H256::random(), &mut commit); + db.commit(&commit); + let mut commit = make_commit(&[5], &[2]); + pruning.note_finalized(&H256::random(), &mut commit); + db.commit(&commit); + assert!(db.data_eq(&make_db(&[1, 2, 3, 4, 5]))); + + check_journal(&pruning, &db); + + let mut commit = CommitSet::default(); + pruning.prune_one(&mut commit); + db.commit(&commit); + assert!(db.data_eq(&make_db(&[2, 3, 4, 5]))); + let mut commit = CommitSet::default(); + pruning.prune_one(&mut commit); + db.commit(&commit); + assert!(db.data_eq(&make_db(&[3, 4, 5]))); + assert_eq!(pruning.pending_number, 2); + } + + #[test] + fn reinserted_survives() { + let mut db = make_db(&[1, 2, 3]); + let mut pruning: RefWindow = RefWindow::new(&db).unwrap(); + let mut commit = make_commit(&[], &[2]); + pruning.note_finalized(&H256::random(), &mut commit); + db.commit(&commit); + let mut commit = make_commit(&[2], &[]); + pruning.note_finalized(&H256::random(), &mut commit); + db.commit(&commit); + let mut commit = make_commit(&[], &[2]); + pruning.note_finalized(&H256::random(), &mut commit); + db.commit(&commit); + assert!(db.data_eq(&make_db(&[1, 2, 3]))); + + check_journal(&pruning, &db); + + let mut commit = CommitSet::default(); + pruning.prune_one(&mut commit); + db.commit(&commit); + assert!(db.data_eq(&make_db(&[1, 2, 3]))); + let mut commit = CommitSet::default(); + pruning.prune_one(&mut commit); + db.commit(&commit); + assert!(db.data_eq(&make_db(&[1, 2, 3]))); + pruning.prune_one(&mut commit); + db.commit(&commit); + assert!(db.data_eq(&make_db(&[1, 3]))); + assert_eq!(pruning.pending_number, 3); + } +} diff --git a/core/state-db/src/test.rs b/core/state-db/src/test.rs new file mode 100644 index 0000000000000..4931ed950698a --- /dev/null +++ b/core/state-db/src/test.rs @@ -0,0 +1,83 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Test utils + +use std::collections::HashMap; +use primitives::H256; +use {DBValue, ChangeSet, CommitSet, MetaDb, HashDb}; + +#[derive(Default, Debug, Clone, PartialEq, Eq)] +pub struct TestDb { + pub data: HashMap, + pub meta: HashMap, DBValue>, +} + +impl MetaDb for TestDb { + type Error = (); + + fn get_meta(&self, key: &[u8]) -> Result, ()> { + Ok(self.meta.get(key).cloned()) + } +} + +impl HashDb for TestDb { + type Error = (); + type Hash = H256; + + fn get(&self, key: &H256) -> Result, ()> { + Ok(self.data.get(key).cloned()) + } +} + +impl TestDb { + pub fn commit(&mut self, commit: &CommitSet) { + self.data.extend(commit.data.inserted.iter().cloned()); + for k in commit.data.deleted.iter() { + self.data.remove(k); + } + self.meta.extend(commit.meta.inserted.iter().cloned()); + for k in commit.meta.deleted.iter() { + self.meta.remove(k); + } + } + + pub fn data_eq(&self, other: &TestDb) -> bool { + self.data == other.data + } +} + +pub fn make_changeset(inserted: &[u64], deleted: &[u64]) -> ChangeSet { + ChangeSet { + inserted: inserted.iter().map(|v| (H256::from(*v), H256::from(*v).to_vec())).collect(), + deleted: deleted.iter().map(|v| H256::from(*v)).collect(), + } +} + +pub fn make_commit(inserted: &[u64], deleted: &[u64]) -> CommitSet { + CommitSet { + data: make_changeset(inserted, deleted), + meta: ChangeSet::default(), + } +} + +pub fn make_db(inserted: &[u64]) -> TestDb { + TestDb { + data: inserted.iter().map(|v| (H256::from(*v), H256::from(*v).to_vec())).collect(), + meta: Default::default(), + } +} + diff --git a/core/state-db/src/unfinalized.rs b/core/state-db/src/unfinalized.rs new file mode 100644 index 0000000000000..728c21ae4c172 --- /dev/null +++ b/core/state-db/src/unfinalized.rs @@ -0,0 +1,533 @@ +// Copyright 2017 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 . + +//! Finalization window. +//! Maintains trees of block overlays and allows discarding trees/roots +//! The overlays are added in `insert` and removed in `finalize`. +//! Last finalized overlay is kept in memory until next call to `finalize` or +//! `clear_overlay` + +use std::collections::{HashMap, VecDeque}; +use super::{Error, DBValue, ChangeSet, CommitSet, MetaDb, Hash, to_meta_key}; +use codec::{Decode, Encode}; + +const UNFINALIZED_JOURNAL: &[u8] = b"unfinalized_journal"; +const LAST_FINALIZED: &[u8] = b"last_finalized"; + +/// See module documentation. +pub struct UnfinalizedOverlay { + last_finalized: Option<(BlockHash, u64)>, + levels: VecDeque>>, + parents: HashMap, + last_finalized_overlay: HashMap, +} + +#[derive(Encode, Decode)] +struct JournalRecord { + hash: BlockHash, + parent_hash: BlockHash, + inserted: Vec<(Key, DBValue)>, + deleted: Vec, +} + +fn to_journal_key(block: u64, index: u64) -> Vec { + to_meta_key(UNFINALIZED_JOURNAL, &(block, index)) +} + +#[cfg_attr(test, derive(PartialEq, Debug))] +struct BlockOverlay { + hash: BlockHash, + journal_key: Vec, + values: HashMap, + deleted: Vec, +} + +impl UnfinalizedOverlay { + /// Creates a new instance. Does not expect any metadata to be present in the DB. + pub fn new(db: &D) -> Result, Error> { + let last_finalized = db.get_meta(&to_meta_key(LAST_FINALIZED, &())) + .map_err(|e| Error::Db(e))?; + let last_finalized = match last_finalized { + Some(buffer) => Some(<(BlockHash, u64)>::decode(&mut buffer.as_slice()).ok_or(Error::Decoding)?), + None => None, + }; + let mut levels = VecDeque::new(); + let mut parents = HashMap::new(); + if let Some((ref hash, mut block)) = last_finalized { + // read the journal + trace!(target: "state-db", "Reading unfinalized journal. Last finalized #{} ({:?})", block, hash); + let mut total: u64 = 0; + block += 1; + loop { + let mut index: u64 = 0; + let mut level = Vec::new(); + loop { + let journal_key = to_journal_key(block, index); + match db.get_meta(&journal_key).map_err(|e| Error::Db(e))? { + Some(record) => { + let record: JournalRecord = Decode::decode(&mut record.as_slice()).ok_or(Error::Decoding)?; + let overlay = BlockOverlay { + hash: record.hash.clone(), + journal_key, + values: record.inserted.into_iter().collect(), + deleted: record.deleted, + }; + trace!(target: "state-db", "Unfinalized journal entry {}.{} ({} inserted, {} deleted)", block, index, overlay.values.len(), overlay.deleted.len()); + level.push(overlay); + parents.insert(record.hash, record.parent_hash); + index += 1; + total += 1; + }, + None => break, + } + } + if level.is_empty() { + break; + } + levels.push_back(level); + block += 1; + } + trace!(target: "state-db", "Finished reading unfinalized journal, {} entries", total); + } + Ok(UnfinalizedOverlay { + last_finalized: last_finalized, + levels, + parents, + last_finalized_overlay: Default::default(), + }) + } + + /// Insert a new block into the overlay. If inserted on the second level or lover expects parent to be present in the window. + pub fn insert(&mut self, hash: &BlockHash, number: u64, parent_hash: &BlockHash, changeset: ChangeSet) -> CommitSet { + let mut commit = CommitSet::default(); + if self.levels.is_empty() && self.last_finalized.is_none() { + // assume that parent was finalized + let last_finalized = (parent_hash.clone(), number - 1); + commit.meta.inserted.push((to_meta_key(LAST_FINALIZED, &()), last_finalized.encode())); + self.last_finalized = Some(last_finalized); + } else if self.last_finalized.is_some() { + assert!(number >= self.front_block_number() && number < (self.front_block_number() + self.levels.len() as u64 + 1)); + // check for valid parent if inserting on second level or higher + if number == self.front_block_number() { + assert!(self.last_finalized.as_ref().map_or(false, |&(ref h, n)| h == parent_hash && n == number - 1)); + } else { + assert!(self.parents.contains_key(&parent_hash)); + } + } + let level = if self.levels.is_empty() || number == self.front_block_number() + self.levels.len() as u64 { + self.levels.push_back(Vec::new()); + self.levels.back_mut().expect("can't be empty after insertion; qed") + } else { + let front_block_number = self.front_block_number(); + self.levels.get_mut((number - front_block_number) as usize) + .expect("number is [front_block_number .. front_block_number + levels.len()) is asserted in precondition; qed") + }; + + let index = level.len() as u64; + let journal_key = to_journal_key(number, index); + + let overlay = BlockOverlay { + hash: hash.clone(), + journal_key: journal_key.clone(), + values: changeset.inserted.iter().cloned().collect(), + deleted: changeset.deleted.clone(), + }; + level.push(overlay); + self.parents.insert(hash.clone(), parent_hash.clone()); + let journal_record = JournalRecord { + hash: hash.clone(), + parent_hash: parent_hash.clone(), + inserted: changeset.inserted, + deleted: changeset.deleted, + }; + trace!(target: "state-db", "Inserted unfinalized changeset {}.{} ({} inserted, {} deleted)", number, index, journal_record.inserted.len(), journal_record.deleted.len()); + let journal_record = journal_record.encode(); + commit.meta.inserted.push((journal_key, journal_record)); + commit + } + + fn discard( + levels: &mut [Vec>], + parents: &mut HashMap, + discarded_journals: &mut Vec>, + number: u64, + hash: &BlockHash, + ) { + if let Some((level, sublevels)) = levels.split_first_mut() { + level.retain(|ref overlay| { + let parent = parents.get(&overlay.hash).expect("there is a parent entry for each entry in levels; qed").clone(); + if parent == *hash { + parents.remove(&overlay.hash); + discarded_journals.push(overlay.journal_key.clone()); + Self::discard(sublevels, parents, discarded_journals, number + 1, &overlay.hash); + false + } else { + true + } + }); + } + } + + fn front_block_number(&self) -> u64 { + self.last_finalized.as_ref().map(|&(_, n)| n + 1).unwrap_or(0) + } + + pub fn last_finalized_block_number(&self) -> u64 { + self.last_finalized.as_ref().map(|&(_, n)| n).unwrap_or(0) + } + + /// This may be called when the last finalization commit was applied to the database. + pub fn clear_overlay(&mut self) { + self.last_finalized_overlay.clear(); + } + + /// Select a top-level root and finalized it. Discards all sibling subtrees and the root. + /// Returns a set of changes that need to be added to the DB. + pub fn finalize(&mut self, hash: &BlockHash) -> CommitSet { + trace!(target: "state-db", "Finalizing {:?}", hash); + let level = self.levels.pop_front().expect("no blocks to finalize"); + let index = level.iter().position(|overlay| overlay.hash == *hash) + .expect("attempting to finalize unknown block"); + + let mut commit = CommitSet::default(); + let mut discarded_journals = Vec::new(); + for (i, overlay) in level.into_iter().enumerate() { + self.parents.remove(&overlay.hash); + if i == index { + self.last_finalized_overlay = overlay.values; + // that's the one we need to finalize + commit.data.inserted = self.last_finalized_overlay.iter().map(|(k, v)| (k.clone(), v.clone())).collect(); + commit.data.deleted = overlay.deleted; + } else { + // TODO: borrow checker won't allow us to split out mutable refernces + // required for recursive processing. A more efficient implementaion + // that does not require converting to vector is possible + let mut vec: Vec<_> = self.levels.drain(..).collect(); + Self::discard(&mut vec, &mut self.parents, &mut discarded_journals, 0, &overlay.hash); + self.levels.extend(vec.into_iter()); + } + // cleanup journal entry + discarded_journals.push(overlay.journal_key); + } + commit.meta.deleted.append(&mut discarded_journals); + let last_finalized = (hash.clone(), self.front_block_number()); + commit.meta.inserted.push((to_meta_key(LAST_FINALIZED, &()), last_finalized.encode())); + self.last_finalized = Some(last_finalized); + trace!(target: "state-db", "Discarded {} records", commit.meta.deleted.len()); + commit + } + + /// Get a value from the node overlay. This searches in every existing changeset. + pub fn get(&self, key: &Key) -> Option { + if let Some(value) = self.last_finalized_overlay.get(&key) { + return Some(value.clone()); + } + for level in self.levels.iter() { + for overlay in level.iter() { + if let Some(value) = overlay.values.get(&key) { + return Some(value.clone()); + } + } + } + None + } + + /// Revert a single level. Returns commit set that deletes the journal or `None` if not possible. + pub fn revert_one(&mut self) -> Option> { + self.levels.pop_back().map(|level| { + let mut commit = CommitSet::default(); + for overlay in level.into_iter() { + commit.meta.deleted.push(overlay.journal_key); + self.parents.remove(&overlay.hash); + } + commit + }) + } +} + +#[cfg(test)] +mod tests { + use super::UnfinalizedOverlay; + use {ChangeSet}; + use primitives::H256; + use test::{make_db, make_changeset}; + + fn contains(overlay: &UnfinalizedOverlay, key: u64) -> bool { + overlay.get(&H256::from(key)) == Some(H256::from(key).to_vec()) + } + + #[test] + fn created_from_empty_db() { + let db = make_db(&[]); + let overlay: UnfinalizedOverlay = UnfinalizedOverlay::new(&db).unwrap(); + assert_eq!(overlay.last_finalized, None); + assert!(overlay.levels.is_empty()); + assert!(overlay.parents.is_empty()); + } + + #[test] + #[should_panic] + fn finalize_empty_panics() { + let db = make_db(&[]); + let mut overlay = UnfinalizedOverlay::::new(&db).unwrap(); + overlay.finalize(&H256::default()); + } + + #[test] + #[should_panic] + fn insert_ahead_panics() { + let db = make_db(&[]); + let h1 = H256::random(); + let h2 = H256::random(); + let mut overlay = UnfinalizedOverlay::::new(&db).unwrap(); + overlay.insert(&h1, 2, &H256::default(), ChangeSet::default()); + overlay.insert(&h2, 1, &h1, ChangeSet::default()); + } + + #[test] + #[should_panic] + fn insert_behind_panics() { + let h1 = H256::random(); + let h2 = H256::random(); + let db = make_db(&[]); + let mut overlay = UnfinalizedOverlay::::new(&db).unwrap(); + overlay.insert(&h1, 1, &H256::default(), ChangeSet::default()); + overlay.insert(&h2, 3, &h1, ChangeSet::default()); + } + + #[test] + #[should_panic] + fn insert_unknown_parent_panics() { + let db = make_db(&[]); + let h1 = H256::random(); + let h2 = H256::random(); + let mut overlay = UnfinalizedOverlay::::new(&db).unwrap(); + overlay.insert(&h1, 1, &H256::default(), ChangeSet::default()); + overlay.insert(&h2, 2, &H256::default(), ChangeSet::default()); + } + + #[test] + #[should_panic] + fn finalize_unknown_panics() { + let h1 = H256::random(); + let h2 = H256::random(); + let db = make_db(&[]); + let mut overlay = UnfinalizedOverlay::::new(&db).unwrap(); + overlay.insert(&h1, 1, &H256::default(), ChangeSet::default()); + overlay.finalize(&h2); + } + + #[test] + fn insert_finalize_one() { + let h1 = H256::random(); + let mut db = make_db(&[1, 2]); + let mut overlay = UnfinalizedOverlay::::new(&db).unwrap(); + let changeset = make_changeset(&[3, 4], &[2]); + let insertion = overlay.insert(&h1, 1, &H256::default(), changeset.clone()); + assert_eq!(insertion.data.inserted.len(), 0); + assert_eq!(insertion.data.deleted.len(), 0); + assert_eq!(insertion.meta.inserted.len(), 2); + assert_eq!(insertion.meta.deleted.len(), 0); + db.commit(&insertion); + let finalization = overlay.finalize(&h1); + assert_eq!(finalization.data.inserted.len(), changeset.inserted.len()); + assert_eq!(finalization.data.deleted.len(), changeset.deleted.len()); + assert_eq!(finalization.meta.inserted.len(), 1); + assert_eq!(finalization.meta.deleted.len(), 1); + db.commit(&finalization); + assert!(db.data_eq(&make_db(&[1, 3, 4]))); + } + + #[test] + fn restore_from_journal() { + let h1 = H256::random(); + let h2 = H256::random(); + let mut db = make_db(&[1, 2]); + let mut overlay = UnfinalizedOverlay::::new(&db).unwrap(); + db.commit(&overlay.insert(&h1, 10, &H256::default(), make_changeset(&[3, 4], &[2]))); + db.commit(&overlay.insert(&h2, 11, &h1, make_changeset(&[5], &[3]))); + assert_eq!(db.meta.len(), 3); + + let overlay2 = UnfinalizedOverlay::::new(&db).unwrap(); + assert_eq!(overlay.levels, overlay2.levels); + assert_eq!(overlay.parents, overlay2.parents); + assert_eq!(overlay.last_finalized, overlay2.last_finalized); + } + + #[test] + fn restore_from_journal_after_finalize() { + let h1 = H256::random(); + let h2 = H256::random(); + let mut db = make_db(&[1, 2]); + let mut overlay = UnfinalizedOverlay::::new(&db).unwrap(); + db.commit(&overlay.insert(&h1, 10, &H256::default(), make_changeset(&[3, 4], &[2]))); + db.commit(&overlay.insert(&h2, 11, &h1, make_changeset(&[5], &[3]))); + db.commit(&overlay.finalize(&h1)); + assert_eq!(overlay.levels.len(), 1); + + let overlay2 = UnfinalizedOverlay::::new(&db).unwrap(); + assert_eq!(overlay.levels, overlay2.levels); + assert_eq!(overlay.parents, overlay2.parents); + assert_eq!(overlay.last_finalized, overlay2.last_finalized); + } + + #[test] + fn insert_finalize_two() { + let h1 = H256::random(); + let h2 = H256::random(); + let mut db = make_db(&[1, 2, 3, 4]); + let mut overlay = UnfinalizedOverlay::::new(&db).unwrap(); + let changeset1 = make_changeset(&[5, 6], &[2]); + let changeset2 = make_changeset(&[7, 8], &[5, 3]); + db.commit(&overlay.insert(&h1, 1, &H256::default(), changeset1)); + assert!(contains(&overlay, 5)); + db.commit(&overlay.insert(&h2, 2, &h1, changeset2)); + assert!(contains(&overlay, 7)); + assert!(contains(&overlay, 5)); + assert_eq!(overlay.levels.len(), 2); + assert_eq!(overlay.parents.len(), 2); + db.commit(&overlay.finalize(&h1)); + assert_eq!(overlay.levels.len(), 1); + assert_eq!(overlay.parents.len(), 1); + assert!(contains(&overlay, 5)); + overlay.clear_overlay(); + assert!(!contains(&overlay, 5)); + assert!(contains(&overlay, 7)); + db.commit(&overlay.finalize(&h2)); + overlay.clear_overlay(); + assert_eq!(overlay.levels.len(), 0); + assert_eq!(overlay.parents.len(), 0); + assert!(db.data_eq(&make_db(&[1, 4, 6, 7, 8]))); + } + + + #[test] + fn complex_tree() { + let mut db = make_db(&[]); + + // - 1 - 1_1 - 1_1_1 + // \ 1_2 - 1_2_1 + // \ 1_2_2 + // \ 1_2_3 + // + // - 2 - 2_1 - 2_1_1 + // \ 2_2 + // + // 1_2_2 is the winner + + let (h_1, c_1) = (H256::random(), make_changeset(&[1], &[])); + let (h_2, c_2) = (H256::random(), make_changeset(&[2], &[])); + + let (h_1_1, c_1_1) = (H256::random(), make_changeset(&[11], &[])); + let (h_1_2, c_1_2) = (H256::random(), make_changeset(&[12], &[])); + let (h_2_1, c_2_1) = (H256::random(), make_changeset(&[21], &[])); + let (h_2_2, c_2_2) = (H256::random(), make_changeset(&[22], &[])); + + let (h_1_1_1, c_1_1_1) = (H256::random(), make_changeset(&[111], &[])); + let (h_1_2_1, c_1_2_1) = (H256::random(), make_changeset(&[121], &[])); + let (h_1_2_2, c_1_2_2) = (H256::random(), make_changeset(&[122], &[])); + let (h_1_2_3, c_1_2_3) = (H256::random(), make_changeset(&[123], &[])); + let (h_2_1_1, c_2_1_1) = (H256::random(), make_changeset(&[211], &[])); + + let mut overlay = UnfinalizedOverlay::::new(&db).unwrap(); + db.commit(&overlay.insert(&h_1, 1, &H256::default(), c_1)); + + db.commit(&overlay.insert(&h_1_1, 2, &h_1, c_1_1)); + db.commit(&overlay.insert(&h_1_2, 2, &h_1, c_1_2)); + + db.commit(&overlay.insert(&h_2, 1, &H256::default(), c_2)); + + db.commit(&overlay.insert(&h_2_1, 2, &h_2, c_2_1)); + db.commit(&overlay.insert(&h_2_2, 2, &h_2, c_2_2)); + + db.commit(&overlay.insert(&h_1_1_1, 3, &h_1_1, c_1_1_1)); + db.commit(&overlay.insert(&h_1_2_1, 3, &h_1_2, c_1_2_1)); + db.commit(&overlay.insert(&h_1_2_2, 3, &h_1_2, c_1_2_2)); + db.commit(&overlay.insert(&h_1_2_3, 3, &h_1_2, c_1_2_3)); + db.commit(&overlay.insert(&h_2_1_1, 3, &h_2_1, c_2_1_1)); + + assert!(contains(&overlay, 2)); + assert!(contains(&overlay, 11)); + assert!(contains(&overlay, 21)); + assert!(contains(&overlay, 111)); + assert!(contains(&overlay, 122)); + assert!(contains(&overlay, 211)); + assert_eq!(overlay.levels.len(), 3); + assert_eq!(overlay.parents.len(), 11); + assert_eq!(overlay.last_finalized, Some((H256::default(), 0))); + + // check if restoration from journal results in the same tree + let overlay2 = UnfinalizedOverlay::::new(&db).unwrap(); + assert_eq!(overlay.levels, overlay2.levels); + assert_eq!(overlay.parents, overlay2.parents); + assert_eq!(overlay.last_finalized, overlay2.last_finalized); + + // finalize 1. 2 and all its children should be discarded + db.commit(&overlay.finalize(&h_1)); + overlay.clear_overlay(); + assert_eq!(overlay.levels.len(), 2); + assert_eq!(overlay.parents.len(), 6); + assert!(!contains(&overlay, 1)); + assert!(!contains(&overlay, 2)); + assert!(!contains(&overlay, 21)); + assert!(!contains(&overlay, 22)); + assert!(!contains(&overlay, 211)); + assert!(contains(&overlay, 111)); + + // finalize 1_2. 1_1 and all its children should be discarded + db.commit(&overlay.finalize(&h_1_2)); + overlay.clear_overlay(); + assert_eq!(overlay.levels.len(), 1); + assert_eq!(overlay.parents.len(), 3); + assert!(!contains(&overlay, 11)); + assert!(!contains(&overlay, 111)); + assert!(contains(&overlay, 121)); + assert!(contains(&overlay, 122)); + assert!(contains(&overlay, 123)); + + // finalize 1_2_2 + db.commit(&overlay.finalize(&h_1_2_2)); + overlay.clear_overlay(); + assert_eq!(overlay.levels.len(), 0); + assert_eq!(overlay.parents.len(), 0); + assert!(db.data_eq(&make_db(&[1, 12, 122]))); + assert_eq!(overlay.last_finalized, Some((h_1_2_2, 3))); + } + + #[test] + fn insert_revert() { + let h1 = H256::random(); + let h2 = H256::random(); + let mut db = make_db(&[1, 2, 3, 4]); + let mut overlay = UnfinalizedOverlay::::new(&db).unwrap(); + assert!(overlay.revert_one().is_none()); + let changeset1 = make_changeset(&[5, 6], &[2]); + let changeset2 = make_changeset(&[7, 8], &[5, 3]); + db.commit(&overlay.insert(&h1, 1, &H256::default(), changeset1)); + db.commit(&overlay.insert(&h2, 2, &h1, changeset2)); + assert!(contains(&overlay, 7)); + db.commit(&overlay.revert_one().unwrap()); + assert_eq!(overlay.parents.len(), 1); + assert!(contains(&overlay, 5)); + assert!(!contains(&overlay, 7)); + db.commit(&overlay.revert_one().unwrap()); + assert_eq!(overlay.levels.len(), 0); + assert_eq!(overlay.parents.len(), 0); + assert!(overlay.revert_one().is_none()); + } + +} + diff --git a/core/state-machine/Cargo.toml b/core/state-machine/Cargo.toml new file mode 100644 index 0000000000000..3635627999f35 --- /dev/null +++ b/core/state-machine/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "substrate-state-machine" +version = "0.1.0" +authors = ["Parity Technologies "] +description = "Substrate State Machine" + +[dependencies] +byteorder = "1.1" +hex-literal = "0.1.0" +log = "0.3" +parking_lot = "0.4" +heapsize = "0.4" +hashdb = "0.2.1" +memorydb = "0.2.1" +patricia-trie = "0.2.1" +triehash = "0.2" +rlp = "0.2.4" + +substrate-primitives = { path = "../primitives", version = "0.1.0" } +substrate-codec = { path = "../codec", default_features = false } + diff --git a/core/state-machine/README.adoc b/core/state-machine/README.adoc new file mode 100644 index 0000000000000..aad08bed989db --- /dev/null +++ b/core/state-machine/README.adoc @@ -0,0 +1,13 @@ + += State Machine + +.Summary +[source, toml] +---- +include::Cargo.toml[lines=2..5] +---- + +.Description +---- +include::src/lib.rs[tag=description] +---- diff --git a/core/state-machine/src/backend.rs b/core/state-machine/src/backend.rs new file mode 100644 index 0000000000000..7db1e64d3f25f --- /dev/null +++ b/core/state-machine/src/backend.rs @@ -0,0 +1,193 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! State machine backends. These manage the code and storage of contracts. + +use std::{error, fmt}; +use std::cmp::Ord; +use std::collections::HashMap; +use std::marker::PhantomData; +use std::sync::Arc; + +use hashdb::Hasher; +use rlp::Encodable; +use trie_backend::{TryIntoTrieBackend, TrieBackend}; +use patricia_trie::{TrieDBMut, TrieMut, NodeCodec}; +use heapsize::HeapSizeOf; + +/// A state backend is used to read state data and can have changes committed +/// to it. +/// +/// The clone operation (if implemented) should be cheap. +pub trait Backend>: TryIntoTrieBackend { + /// An error type when fetching data is not possible. + type Error: super::Error; + + /// Changes to be applied if committing + type Transaction; + + /// Get keyed storage associated with specific address, or None if there is nothing associated. + fn storage(&self, key: &[u8]) -> Result>, Self::Error>; + + /// true if a key exists in storage. + fn exists_storage(&self, key: &[u8]) -> Result { + Ok(self.storage(key)?.is_some()) + } + + /// Retrieve all entries keys of which start with the given prefix and + /// call `f` for each of those keys. + fn for_keys_with_prefix(&self, prefix: &[u8], f: F); + + /// Calculate the storage root, with given delta over what is already stored in + /// the backend, and produce a "transaction" that can be used to commit. + fn storage_root(&self, delta: I) -> (H::Out, Self::Transaction) + where + I: IntoIterator, Option>)>, + H::Out: Ord + Encodable; + + /// Get all key/value pairs into a Vec. + fn pairs(&self) -> Vec<(Vec, Vec)>; +} + +/// Error impossible. +// TODO: use `!` type when stabilized. +#[derive(Debug)] +pub enum Void {} + +impl fmt::Display for Void { + fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result { + match *self {} + } +} + +impl error::Error for Void { + fn description(&self) -> &str { "unreachable error" } +} + +/// In-memory backend. Fully recomputes tries on each commit but useful for +/// tests. +#[derive(Eq)] +pub struct InMemory { + inner: Arc, Vec>>, + _hasher: PhantomData, + _codec: PhantomData, +} + +impl Default for InMemory { + fn default() -> Self { + InMemory { + inner: Arc::new(Default::default()), + _hasher: PhantomData, + _codec: PhantomData, + } + } +} + +impl Clone for InMemory { + fn clone(&self) -> Self { + InMemory { + inner: self.inner.clone(), _hasher: PhantomData, _codec: PhantomData, + } + } +} + +impl PartialEq for InMemory { + fn eq(&self, other: &Self) -> bool { + self.inner.eq(&other.inner) + } +} + +impl> InMemory where H::Out: HeapSizeOf { + /// Copy the state, with applied updates + pub fn update(&self, changes: >::Transaction) -> Self { + let mut inner: HashMap<_, _> = (&*self.inner).clone(); + for (key, val) in changes { + match val { + Some(v) => { inner.insert(key, v); }, + None => { inner.remove(&key); }, + } + } + + inner.into() + } +} + +impl From, Vec>> for InMemory { + fn from(inner: HashMap, Vec>) -> Self { + InMemory { + inner: Arc::new(inner), _hasher: PhantomData, _codec: PhantomData + } + } +} + +impl super::Error for Void {} + +impl> Backend for InMemory where H::Out: HeapSizeOf { + type Error = Void; + type Transaction = Vec<(Vec, Option>)>; + + fn storage(&self, key: &[u8]) -> Result>, Self::Error> { + Ok(self.inner.get(key).map(Clone::clone)) + } + + fn exists_storage(&self, key: &[u8]) -> Result { + Ok(self.inner.get(key).is_some()) + } + + fn for_keys_with_prefix(&self, prefix: &[u8], f: F) { + self.inner.keys().filter(|key| key.starts_with(prefix)).map(|k| &**k).for_each(f); + } + + fn storage_root(&self, delta: I) -> (H::Out, Self::Transaction) + where + I: IntoIterator, Option>)>, + ::Out: Ord + Encodable, + { + let existing_pairs = self.inner.iter().map(|(k, v)| (k.clone(), Some(v.clone()))); + + let transaction: Vec<_> = delta.into_iter().collect(); + let root = ::triehash::trie_root::(existing_pairs.chain(transaction.iter().cloned()) + .collect::>() + .into_iter() + .filter_map(|(k, maybe_val)| maybe_val.map(|val| (k, val))) + ); + + (root, transaction) + } + + fn pairs(&self) -> Vec<(Vec, Vec)> { + self.inner.iter().map(|(k, v)| (k.clone(), v.clone())).collect() + } +} + +impl> TryIntoTrieBackend for InMemory where H::Out: HeapSizeOf { + fn try_into_trie_backend(self) -> Option> { + use memorydb::MemoryDB; + let mut root = ::Out::default(); + let mut mdb = MemoryDB::new(); + { + let mut trie = TrieDBMut::::new(&mut mdb, &mut root); + for (key, value) in self.inner.iter() { + if let Err(e) = trie.insert(&key, &value) { + warn!(target: "trie", "Failed to write to trie: {}", e); + return None; + } + } + } + + Some(TrieBackend::with_memorydb(mdb, root)) + } +} diff --git a/core/state-machine/src/ext.rs b/core/state-machine/src/ext.rs new file mode 100644 index 0000000000000..8504f42b59392 --- /dev/null +++ b/core/state-machine/src/ext.rs @@ -0,0 +1,171 @@ +// Copyright 2017 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 . + +//! Conrete externalities implementation. + +use std::{error, fmt, cmp::Ord}; +use backend::Backend; +use {Externalities, OverlayedChanges}; +use hashdb::Hasher; +use rlp::Encodable; +use patricia_trie::NodeCodec; + +/// Errors that can occur when interacting with the externalities. +#[derive(Debug, Copy, Clone)] +pub enum Error { + /// Failure to load state data from the backend. + #[allow(unused)] + Backend(B), + /// Failure to execute a function. + #[allow(unused)] + Executor(E), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Error::Backend(ref e) => write!(f, "Storage backend error: {}", e), + Error::Executor(ref e) => write!(f, "Sub-call execution error: {}", e), + } + } +} + +impl error::Error for Error { + fn description(&self) -> &str { + match *self { + Error::Backend(..) => "backend error", + Error::Executor(..) => "executor error", + } + } +} + +/// Wraps a read-only backend, call executor, and current overlayed changes. +pub struct Ext<'a, H, C, B> +where + H: Hasher, + C: NodeCodec, + B: 'a + Backend, +{ + // The overlayed changes to write to. + overlay: &'a mut OverlayedChanges, + // The storage backend to read from. + backend: &'a B, + // The transaction necessary to commit to the backend. + transaction: Option<(B::Transaction, H::Out)>, +} + +impl<'a, H, C, B> Ext<'a, H, C, B> +where + H: Hasher, + C: NodeCodec, + B: 'a + Backend, + H::Out: Ord + Encodable +{ + /// Create a new `Ext` from overlayed changes and read-only backend + pub fn new(overlay: &'a mut OverlayedChanges, backend: &'a B) -> Self { + Ext { + overlay, + backend, + transaction: None, + } + } + + /// Get the transaction necessary to update the backend. + pub fn transaction(mut self) -> B::Transaction { + let _ = self.storage_root(); + self.transaction.expect("transaction always set after calling storage root; qed").0 + } + + /// Invalidates the currently cached storage root and the db transaction. + /// + /// Called when there are changes that likely will invalidate the storage root. + fn mark_dirty(&mut self) { + self.transaction = None; + } +} + +#[cfg(test)] +impl<'a, H, C, B> Ext<'a, H, C, B> +where + H: Hasher, + C: NodeCodec, + B: 'a + Backend, +{ + pub fn storage_pairs(&self) -> Vec<(Vec, Vec)> { + use std::collections::HashMap; + + self.backend.pairs().iter() + .map(|&(ref k, ref v)| (k.to_vec(), Some(v.to_vec()))) + .chain(self.overlay.committed.clone().into_iter()) + .chain(self.overlay.prospective.clone().into_iter()) + .collect::>() + .into_iter() + .filter_map(|(k, maybe_val)| maybe_val.map(|val| (k, val))) + .collect() + } +} + +impl<'a, B: 'a, H, C> Externalities for Ext<'a, H, C, B> +where + H: Hasher, + C: NodeCodec, + B: 'a + Backend, + H::Out: Ord + Encodable +{ + fn storage(&self, key: &[u8]) -> Option> { + self.overlay.storage(key).map(|x| x.map(|x| x.to_vec())).unwrap_or_else(|| + self.backend.storage(key).expect("Externalities not allowed to fail within runtime")) + } + + fn exists_storage(&self, key: &[u8]) -> bool { + match self.overlay.storage(key) { + Some(x) => x.is_some(), + _ => self.backend.exists_storage(key).expect("Externalities not allowed to fail within runtime"), + } + } + + fn place_storage(&mut self, key: Vec, value: Option>) { + self.mark_dirty(); + self.overlay.set_storage(key, value); + } + + fn clear_prefix(&mut self, prefix: &[u8]) { + self.mark_dirty(); + self.overlay.clear_prefix(prefix); + self.backend.for_keys_with_prefix(prefix, |key| { + self.overlay.set_storage(key.to_vec(), None); + }); + } + + fn chain_id(&self) -> u64 { + 42 + } + + fn storage_root(&mut self) -> H::Out { + if let Some((_, ref root)) = self.transaction { + return root.clone(); + } + + // compute and memoize + let delta = self.overlay.committed.iter() + .chain(self.overlay.prospective.iter()) + .map(|(k, v)| (k.clone(), v.clone())); + + let (root, transaction) = self.backend.storage_root(delta); + self.transaction = Some((transaction, root)); + root + } +} diff --git a/core/state-machine/src/lib.rs b/core/state-machine/src/lib.rs new file mode 100644 index 0000000000000..7dab6e6af6375 --- /dev/null +++ b/core/state-machine/src/lib.rs @@ -0,0 +1,691 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +// tag::description[] +//! Substrate state machine implementation. +// end::description[] + +#![warn(missing_docs)] + +#[cfg_attr(test, macro_use)] +extern crate hex_literal; + +#[macro_use] +extern crate log; + +extern crate hashdb; +extern crate memorydb; +extern crate triehash; +extern crate patricia_trie; +extern crate byteorder; +extern crate parking_lot; +extern crate rlp; +extern crate heapsize; +#[cfg(test)] +extern crate substrate_primitives as primitives; +extern crate substrate_codec as codec; + +use std::collections::HashMap; +use std::fmt; +use hashdb::Hasher; +use patricia_trie::NodeCodec; +use rlp::Encodable; +use heapsize::HeapSizeOf; +use codec::Decode; + +pub mod backend; +mod ext; +mod testing; +mod proving_backend; +mod trie_backend; + +pub use testing::TestExternalities; +pub use ext::Ext; +pub use backend::Backend; +pub use trie_backend::{TryIntoTrieBackend, TrieBackend, Storage, DBValue}; + +/// The overlayed changes to state to be queried on top of the backend. +/// +/// A transaction shares all prospective changes within an inner overlay +/// that can be cleared. +#[derive(Debug, Default, Clone)] +pub struct OverlayedChanges { + prospective: HashMap, Option>>, + committed: HashMap, Option>>, +} + +impl OverlayedChanges { + /// Returns a double-Option: None if the key is unknown (i.e. and the query should be refered + /// to the backend); Some(None) if the key has been deleted. Some(Some(...)) for a key whose + /// value has been set. + pub fn storage(&self, key: &[u8]) -> Option> { + self.prospective.get(key) + .or_else(|| self.committed.get(key)) + .map(|x| x.as_ref().map(AsRef::as_ref)) + } + + /// Inserts the given key-value pair into the prospective change set. + /// + /// `None` can be used to delete a value specified by the given key. + fn set_storage(&mut self, key: Vec, val: Option>) { + self.prospective.insert(key, val); + } + + /// Removes all key-value pairs which keys share the given prefix. + /// + /// NOTE that this doesn't take place immediately but written into the prospective + /// change set, and still can be reverted by [`discard_prospective`]. + /// + /// [`discard_prospective`]: #method.discard_prospective + fn clear_prefix(&mut self, prefix: &[u8]) { + // Iterate over all prospective and mark all keys that share + // the given prefix as removed (None). + for (key, value) in self.prospective.iter_mut() { + if key.starts_with(prefix) { + *value = None; + } + } + + // Then do the same with keys from commited changes. + // NOTE that we are making changes in the prospective change set. + for key in self.committed.keys() { + if key.starts_with(prefix) { + self.prospective.insert(key.to_owned(), None); + } + } + } + + /// Discard prospective changes to state. + pub fn discard_prospective(&mut self) { + self.prospective.clear(); + } + + /// Commit prospective changes to state. + pub fn commit_prospective(&mut self) { + if self.committed.is_empty() { + ::std::mem::swap(&mut self.prospective, &mut self.committed); + } else { + self.committed.extend(self.prospective.drain()); + } + } + + /// Drain committed changes to an iterator. + /// + /// Panics: + /// Will panic if there are any uncommitted prospective changes. + pub fn drain<'a>(&'a mut self) -> impl Iterator, Option>)> + 'a { + assert!(self.prospective.is_empty()); + self.committed.drain() + } + + /// Consume `OverlayedChanges` and take committed set. + /// + /// Panics: + /// Will panic if there are any uncommitted prospective changes. + pub fn into_committed(self) -> impl Iterator, Option>)> { + assert!(self.prospective.is_empty()); + self.committed.into_iter() + } +} + +/// State Machine Error bound. +/// +/// This should reflect WASM error type bound for future compatibility. +pub trait Error: 'static + fmt::Debug + fmt::Display + Send {} + +impl Error for ExecutionError {} + +/// Externalities Error. +/// +/// Externalities are not really allowed to have errors, since it's assumed that dependent code +/// would not be executed unless externalities were available. This is included for completeness, +/// and as a transition away from the pre-existing framework. +#[derive(Debug, Eq, PartialEq)] +pub enum ExecutionError { + /// The entry `:code` doesn't exist in storage so there's no way we can execute anything. + CodeEntryDoesNotExist, + /// Backend is incompatible with execution proof generation process. + UnableToGenerateProof, + /// Invalid execution proof. + InvalidProof, +} + +impl fmt::Display for ExecutionError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Externalities Error") } +} + +/// Externalities: pinned to specific active address. +pub trait Externalities { + /// Read storage of current contract being called. + fn storage(&self, key: &[u8]) -> Option>; + + /// Set storage entry `key` of current contract being called (effective immediately). + fn set_storage(&mut self, key: Vec, value: Vec) { + self.place_storage(key, Some(value)); + } + + /// Clear a storage entry (`key`) of current contract being called (effective immediately). + fn clear_storage(&mut self, key: &[u8]) { + self.place_storage(key.to_vec(), None); + } + + /// Clear a storage entry (`key`) of current contract being called (effective immediately). + fn exists_storage(&self, key: &[u8]) -> bool { + self.storage(key).is_some() + } + + /// Clear storage entries which keys are start with the given prefix. + fn clear_prefix(&mut self, prefix: &[u8]); + + /// Set or clear a storage entry (`key`) of current contract being called (effective immediately). + fn place_storage(&mut self, key: Vec, value: Option>); + + /// Get the identity of the chain. + fn chain_id(&self) -> u64; + + /// Get the trie root of the current storage map. + fn storage_root(&mut self) -> H::Out where H::Out: Ord + Encodable; +} + +/// Code execution engine. +pub trait CodeExecutor: Sized + Send + Sync { + /// Externalities error type. + type Error: Error; + + /// Call a given method in the runtime. Returns a tuple of the result (either the output data + /// or an execution error) together with a `bool`, which is true if native execution was used. + fn call>( + &self, + ext: &mut E, + heap_pages: usize, + code: &[u8], + method: &str, + data: &[u8], + use_native: bool + ) -> (Result, Self::Error>, bool); +} + +/// Strategy for executing a call into the runtime. +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum ExecutionStrategy { + /// Execute with the native equivalent if it is compatible with the given wasm module; otherwise fall back to the wasm. + NativeWhenPossible, + /// Use the given wasm module. + AlwaysWasm, + /// Run with both the wasm and the native variant (if compatible). Report any discrepency as an error. + Both, +} + +/// Like `ExecutionStrategy` only it also stores a handler in case of consensus failure. +pub enum ExecutionManager { + /// Execute with the native equivalent if it is compatible with the given wasm module; otherwise fall back to the wasm. + NativeWhenPossible, + /// Use the given wasm module. + AlwaysWasm, + /// Run with both the wasm and the native variant (if compatible). Call `F` in the case of any discrepency. + Both(F), +} + +impl<'a, F> From<&'a ExecutionManager> for ExecutionStrategy { + fn from(s: &'a ExecutionManager) -> Self { + match *s { + ExecutionManager::NativeWhenPossible => ExecutionStrategy::NativeWhenPossible, + ExecutionManager::AlwaysWasm => ExecutionStrategy::AlwaysWasm, + ExecutionManager::Both(_) => ExecutionStrategy::Both, + } + } +} + +/// Evaluate to ExecutionManager::NativeWhenPossible, without having to figure out the type. +pub fn native_when_possible() -> ExecutionManager, E>, Result, E>)->Result, E>> { + ExecutionManager::NativeWhenPossible +} + +/// Evaluate to ExecutionManager::NativeWhenPossible, without having to figure out the type. +pub fn always_wasm() -> ExecutionManager, E>, Result, E>)->Result, E>> { + ExecutionManager::AlwaysWasm +} + +/// Execute a call using the given state backend, overlayed changes, and call executor. +/// Produces a state-backend-specific "transaction" which can be used to apply the changes +/// to the backing store, such as the disk. +/// +/// On an error, no prospective changes are written to the overlay. +/// +/// Note: changes to code will be in place if this call is made again. For running partial +/// blocks (e.g. a transaction at a time), ensure a different method is used. +pub fn execute( + backend: &B, + overlay: &mut OverlayedChanges, + exec: &Exec, + method: &str, + call_data: &[u8], + strategy: ExecutionStrategy, +) -> Result<(Vec, B::Transaction), Box> +where + H: Hasher, + C: NodeCodec, + Exec: CodeExecutor, + B: Backend, + H::Out: Ord + Encodable +{ + execute_using_consensus_failure_handler( + backend, + overlay, + exec, + method, + call_data, + match strategy { + ExecutionStrategy::AlwaysWasm => ExecutionManager::AlwaysWasm, + ExecutionStrategy::NativeWhenPossible => ExecutionManager::NativeWhenPossible, + ExecutionStrategy::Both => ExecutionManager::Both(|wasm_result, native_result| { + warn!("Consensus error between wasm {:?} and native {:?}. Using wasm.", wasm_result, native_result); + wasm_result + }), + }, + ) +} + +/// Execute a call using the given state backend, overlayed changes, and call executor. +/// Produces a state-backend-specific "transaction" which can be used to apply the changes +/// to the backing store, such as the disk. +/// +/// On an error, no prospective changes are written to the overlay. +/// +/// Note: changes to code will be in place if this call is made again. For running partial +/// blocks (e.g. a transaction at a time), ensure a different method is used. +pub fn execute_using_consensus_failure_handler( + backend: &B, + overlay: &mut OverlayedChanges, + exec: &Exec, + method: &str, + call_data: &[u8], + manager: ExecutionManager, +) -> Result<(Vec, B::Transaction), Box> +where + H: Hasher, + C: NodeCodec, + Exec: CodeExecutor, + B: Backend, + H::Out: Ord + Encodable, + Handler: FnOnce(Result, Exec::Error>, Result, Exec::Error>) -> Result, Exec::Error> +{ + let strategy: ExecutionStrategy = (&manager).into(); + + // make a copy. + let code = ext::Ext::new(overlay, backend).storage(b":code") + .ok_or_else(|| Box::new(ExecutionError::CodeEntryDoesNotExist) as Box)? + .to_vec(); + + let heap_pages = ext::Ext::new(overlay, backend).storage(b":heappages") + .and_then(|v| u64::decode(&mut &v[..])).unwrap_or(8) as usize; + + let result = { + let mut orig_prospective = overlay.prospective.clone(); + + let (result, was_native, delta) = { + let ((result, was_native), delta) = { + let mut externalities = ext::Ext::new(overlay, backend); + ( + exec.call( + &mut externalities, + heap_pages, + &code, + method, + call_data, + // attempt to run native first, if we're not directed to run wasm only + strategy != ExecutionStrategy::AlwaysWasm, + ), + externalities.transaction() + ) + }; + (result, was_native, delta) + }; + + // run wasm separately if we did run native the first time and we're meant to run both + let (result, delta) = if let (true, ExecutionManager::Both(on_consensus_failure)) = + (was_native, manager) + { + overlay.prospective = orig_prospective.clone(); + + let (wasm_result, wasm_delta) = { + let ((result, _), delta) = { + let mut externalities = ext::Ext::new(overlay, backend); + ( + exec.call( + &mut externalities, + heap_pages, + &code, + method, + call_data, + false, + ), + externalities.transaction() + ) + }; + (result, delta) + }; + + if (result.is_ok() && wasm_result.is_ok() && result.as_ref().unwrap() == wasm_result.as_ref().unwrap()/* && delta == wasm_delta*/) + || (result.is_err() && wasm_result.is_err()) + { + (result, delta) + } else { + // Consensus error. + (on_consensus_failure(wasm_result, result), wasm_delta) + } + } else { + (result, delta) + }; + result.map(move |out| (out, delta)) + }; + + result.map_err(|e| Box::new(e) as _) +} + +/// Prove execution using the given state backend, overlayed changes, and call executor. +/// Produces a state-backend-specific "transaction" which can be used to apply the changes +/// to the backing store, such as the disk. +/// Execution proof is the set of all 'touched' storage DBValues from the backend. +/// +/// On an error, no prospective changes are written to the overlay. +/// +/// Note: changes to code will be in place if this call is made again. For running partial +/// blocks (e.g. a transaction at a time), ensure a different method is used. +pub fn prove_execution( + backend: B, + overlay: &mut OverlayedChanges, + exec: &Exec, + method: &str, + call_data: &[u8], +) -> Result<(Vec, Vec>, as Backend>::Transaction), Box> +where + H: Hasher, + Exec: CodeExecutor, + C: NodeCodec, + B: TryIntoTrieBackend, + H::Out: Ord + Encodable + HeapSizeOf, +{ + let trie_backend = backend.try_into_trie_backend() + .ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box)?; + let proving_backend = proving_backend::ProvingBackend::new(trie_backend); + let (result, transaction) = execute::(&proving_backend, overlay, exec, method, call_data, ExecutionStrategy::NativeWhenPossible)?; + let proof = proving_backend.extract_proof(); + Ok((result, proof, transaction)) +} + +/// Check execution proof, generated by `prove_execution` call. +pub fn execution_proof_check( + root: H::Out, + proof: Vec>, + overlay: &mut OverlayedChanges, + exec: &Exec, + method: &str, + call_data: &[u8], +) -> Result<(Vec, memorydb::MemoryDB), Box> +where +H: Hasher, +C: NodeCodec, +Exec: CodeExecutor, +H::Out: Ord + Encodable + HeapSizeOf, +{ + let backend = proving_backend::create_proof_check_backend::(root.into(), proof)?; + execute::(&backend, overlay, exec, method, call_data, ExecutionStrategy::NativeWhenPossible) +} + +/// Generate storage read proof. +pub fn prove_read( + backend: B, + key: &[u8] +) -> Result<(Option>, Vec>), Box> +where + B: TryIntoTrieBackend, + H: Hasher, + C: NodeCodec, + H::Out: Ord + Encodable + HeapSizeOf +{ + let trie_backend = backend.try_into_trie_backend() + .ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box)?; + let proving_backend = proving_backend::ProvingBackend::::new(trie_backend); + let result = proving_backend.storage(key).map_err(|e| Box::new(e) as Box)?; + Ok((result, proving_backend.extract_proof())) +} + +/// Check storage read proof, generated by `prove_read` call. +pub fn read_proof_check( + root: H::Out, + proof: Vec>, + key: &[u8], +) -> Result>, Box> +where + H: Hasher, + C: NodeCodec, + H::Out: Ord + Encodable + HeapSizeOf +{ + let backend = proving_backend::create_proof_check_backend::(root, proof)?; + backend.storage(key).map_err(|e| Box::new(e) as Box) +} + +#[cfg(test)] +mod tests { + use super::*; + use super::backend::InMemory; + use super::ext::Ext; + use primitives::{Blake2Hasher, RlpCodec, H256}; + + struct DummyCodeExecutor { + native_available: bool, + native_succeeds: bool, + fallback_succeeds: bool, + } + + impl CodeExecutor for DummyCodeExecutor { + type Error = u8; + + fn call>( + &self, + ext: &mut E, + _heap_pages: usize, + _code: &[u8], + _method: &str, + _data: &[u8], + use_native: bool + ) -> (Result, Self::Error>, bool) { + let using_native = use_native && self.native_available; + match (using_native, self.native_succeeds, self.fallback_succeeds) { + (true, true, _) | (false, _, true) => + (Ok(vec![ext.storage(b"value1").unwrap()[0] + ext.storage(b"value2").unwrap()[0]]), using_native), + _ => (Err(0), using_native), + } + } + } + + impl Error for u8 {} + + #[test] + fn overlayed_storage_works() { + let mut overlayed = OverlayedChanges::default(); + + let key = vec![42, 69, 169, 142]; + + assert!(overlayed.storage(&key).is_none()); + + overlayed.set_storage(key.clone(), Some(vec![1, 2, 3])); + assert_eq!(overlayed.storage(&key).unwrap(), Some(&[1, 2, 3][..])); + + overlayed.commit_prospective(); + assert_eq!(overlayed.storage(&key).unwrap(), Some(&[1, 2, 3][..])); + + overlayed.set_storage(key.clone(), Some(vec![])); + assert_eq!(overlayed.storage(&key).unwrap(), Some(&[][..])); + + overlayed.set_storage(key.clone(), None); + assert!(overlayed.storage(&key).unwrap().is_none()); + + overlayed.discard_prospective(); + assert_eq!(overlayed.storage(&key).unwrap(), Some(&[1, 2, 3][..])); + + overlayed.set_storage(key.clone(), None); + overlayed.commit_prospective(); + assert!(overlayed.storage(&key).unwrap().is_none()); + } + + macro_rules! map { + ($( $name:expr => $value:expr ),*) => ( + vec![ $( ( $name, $value ) ),* ].into_iter().collect() + ) + } + + #[test] + fn overlayed_storage_root_works() { + let initial: HashMap<_, _> = map![ + b"doe".to_vec() => b"reindeer".to_vec(), + b"dog".to_vec() => b"puppyXXX".to_vec(), + b"dogglesworth".to_vec() => b"catXXX".to_vec(), + b"doug".to_vec() => b"notadog".to_vec() + ]; + let backend = InMemory::::from(initial); + let mut overlay = OverlayedChanges { + committed: map![ + b"dog".to_vec() => Some(b"puppy".to_vec()), + b"dogglesworth".to_vec() => Some(b"catYYY".to_vec()), + b"doug".to_vec() => Some(vec![]) + ], + prospective: map![ + b"dogglesworth".to_vec() => Some(b"cat".to_vec()), + b"doug".to_vec() => None + ], + }; + let mut ext = Ext::new(&mut overlay, &backend); + const ROOT: [u8; 32] = hex!("6ca394ff9b13d6690a51dea30b1b5c43108e52944d30b9095227c49bae03ff8b"); + assert_eq!(ext.storage_root(), H256(ROOT)); + } + + #[test] + fn execute_works() { + assert_eq!(execute( + &trie_backend::tests::test_trie(), + &mut Default::default(), + &DummyCodeExecutor { + native_available: true, + native_succeeds: true, + fallback_succeeds: true, + }, + "test", + &[], + ExecutionStrategy::NativeWhenPossible + ).unwrap().0, vec![66]); + } + + #[test] + fn dual_execution_strategy_detects_consensus_failure() { + let mut consensus_failed = false; + assert!(execute_using_consensus_failure_handler( + &trie_backend::tests::test_trie(), + &mut Default::default(), + &DummyCodeExecutor { + native_available: true, + native_succeeds: true, + fallback_succeeds: false, + }, + "test", + &[], + ExecutionManager::Both(|we, _ne| { + consensus_failed = true; + println!("HELLO!"); + we + }), + ).is_err()); + assert!(consensus_failed); + } + + #[test] + fn prove_execution_and_proof_check_works() { + let executor = DummyCodeExecutor { + native_available: true, + native_succeeds: true, + fallback_succeeds: true, + }; + + // fetch execution proof from 'remote' full node + let remote_backend = trie_backend::tests::test_trie(); + let remote_root = remote_backend.storage_root(::std::iter::empty()).0; + let (remote_result, remote_proof, _) = prove_execution(remote_backend, + &mut Default::default(), &executor, "test", &[]).unwrap(); + + // check proof locally + let (local_result, _) = execution_proof_check::(remote_root, remote_proof, + &mut Default::default(), &executor, "test", &[]).unwrap(); + + // check that both results are correct + assert_eq!(remote_result, vec![66]); + assert_eq!(remote_result, local_result); + } + + #[test] + fn clear_prefix_in_ext_works() { + let initial: HashMap<_, _> = map![ + b"aaa".to_vec() => b"0".to_vec(), + b"abb".to_vec() => b"1".to_vec(), + b"abc".to_vec() => b"2".to_vec(), + b"bbb".to_vec() => b"3".to_vec() + ]; + let backend = InMemory::::from(initial).try_into_trie_backend().unwrap(); + let mut overlay = OverlayedChanges { + committed: map![ + b"aba".to_vec() => Some(b"1312".to_vec()), + b"bab".to_vec() => Some(b"228".to_vec()) + ], + prospective: map![ + b"abd".to_vec() => Some(b"69".to_vec()), + b"bbd".to_vec() => Some(b"42".to_vec()) + ], + }; + + { + let mut ext = Ext::new(&mut overlay, &backend); + ext.clear_prefix(b"ab"); + } + overlay.commit_prospective(); + + assert_eq!( + overlay.committed, + map![ + b"abb".to_vec() => None, + b"abc".to_vec() => None, + b"aba".to_vec() => None, + b"abd".to_vec() => None, + + b"bab".to_vec() => Some(b"228".to_vec()), + b"bbd".to_vec() => Some(b"42".to_vec()) + ], + ); + } + + #[test] + fn prove_read_and_proof_check_works() { + // fetch read proof from 'remote' full node + let remote_backend = trie_backend::tests::test_trie(); + let remote_root = remote_backend.storage_root(::std::iter::empty()).0; + let remote_proof = prove_read(remote_backend, b"value2").unwrap().1; + // check proof locally + let local_result1 = read_proof_check::(remote_root, remote_proof.clone(), b"value2").unwrap(); + let local_result2 = read_proof_check::(remote_root, remote_proof.clone(), &[0xff]).is_ok(); + // check that results are correct + assert_eq!(local_result1, Some(vec![24])); + assert_eq!(local_result2, false); + } +} diff --git a/core/state-machine/src/proving_backend.rs b/core/state-machine/src/proving_backend.rs new file mode 100644 index 0000000000000..25a2f49c75a34 --- /dev/null +++ b/core/state-machine/src/proving_backend.rs @@ -0,0 +1,183 @@ +// Copyright 2017 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 . + +//! Proving state machine backend. + +use std::cell::RefCell; +use hashdb::{Hasher, HashDB}; +use memorydb::MemoryDB; +use patricia_trie::{TrieDB, Trie, Recorder, NodeCodec}; +use trie_backend::{TrieBackend, Ephemeral}; +use {Error, ExecutionError, Backend, TryIntoTrieBackend}; +use rlp::Encodable; +use heapsize::HeapSizeOf; + +/// Patricia trie-based backend which also tracks all touched storage trie values. +/// These can be sent to remote node and used as a proof of execution. +pub struct ProvingBackend> { + backend: TrieBackend, + proof_recorder: RefCell>, +} + +impl> ProvingBackend { + /// Create new proving backend. + pub fn new(backend: TrieBackend) -> Self { + ProvingBackend { + backend, + proof_recorder: RefCell::new(Recorder::new()), + } + } + + /// Consume the backend, extracting the gathered proof in lexicographical order + /// by value. + pub fn extract_proof(self) -> Vec> { + self.proof_recorder.into_inner().drain() + .into_iter() + .map(|n| n.data.to_vec()) + .collect() + } +} + +impl Backend for ProvingBackend +where + H: Hasher, + C: NodeCodec, + H::Out: Ord + Encodable + HeapSizeOf +{ + type Error = String; + type Transaction = MemoryDB; + + fn storage(&self, key: &[u8]) -> Result>, Self::Error> { + let mut read_overlay = MemoryDB::new(); + let eph = Ephemeral::new( + self.backend.backend_storage(), + &mut read_overlay, + ); + let map_e = |e| format!("Trie lookup error: {}", e); + + let mut proof_recorder = self.proof_recorder.try_borrow_mut() + .expect("only fails when already borrowed; storage() is non-reentrant; qed"); + TrieDB::::new(&eph, &self.backend.root()).map_err(map_e)? + .get_with(key, &mut *proof_recorder).map(|x| x.map(|val| val.to_vec())).map_err(map_e) + } + + fn for_keys_with_prefix(&self, prefix: &[u8], f: F) { + self.backend.for_keys_with_prefix(prefix, f) + } + + fn pairs(&self) -> Vec<(Vec, Vec)> { + self.backend.pairs() + } + + fn storage_root(&self, delta: I) -> (H::Out, MemoryDB) + where I: IntoIterator, Option>)> + { + self.backend.storage_root(delta) + } +} + +impl> TryIntoTrieBackend for ProvingBackend { + fn try_into_trie_backend(self) -> Option> { + None + } +} + +/// Create proof check backend. +pub fn create_proof_check_backend( + root: H::Out, + proof: Vec> +) -> Result, Box> +where + H: Hasher, + C: NodeCodec, + H::Out: HeapSizeOf, +{ + let mut db = MemoryDB::new(); + for item in proof { + db.insert(&item); + } + + if !db.contains(&root) { + return Err(Box::new(ExecutionError::InvalidProof) as Box); + } + + + Ok(TrieBackend::with_memorydb(db, root)) +} + +#[cfg(test)] +mod tests { + use backend::{InMemory}; + use trie_backend::tests::test_trie; + use super::*; + use primitives::{Blake2Hasher, RlpCodec}; + + fn test_proving() -> ProvingBackend { + ProvingBackend::new(test_trie()) + } + + #[test] + fn proof_is_empty_until_value_is_read() { + assert!(test_proving().extract_proof().is_empty()); + } + + #[test] + fn proof_is_non_empty_after_value_is_read() { + let backend = test_proving(); + assert_eq!(backend.storage(b"key").unwrap(), Some(b"value".to_vec())); + assert!(!backend.extract_proof().is_empty()); + } + + #[test] + fn proof_is_invalid_when_does_not_contains_root() { + assert!(create_proof_check_backend::(1.into(), vec![]).is_err()); + } + + #[test] + fn passes_throgh_backend_calls() { + let trie_backend = test_trie(); + let proving_backend = test_proving(); + assert_eq!(trie_backend.storage(b"key").unwrap(), proving_backend.storage(b"key").unwrap()); + assert_eq!(trie_backend.pairs(), proving_backend.pairs()); + + let (trie_root, mut trie_mdb) = trie_backend.storage_root(::std::iter::empty()); + let (proving_root, mut proving_mdb) = proving_backend.storage_root(::std::iter::empty()); + assert_eq!(trie_root, proving_root); + assert_eq!(trie_mdb.drain(), proving_mdb.drain()); + } + + #[test] + fn proof_recorded_and_checked() { + let contents = (0..64).map(|i| (vec![i], Some(vec![i]))).collect::>(); + let in_memory = InMemory::::default(); + let in_memory = in_memory.update(contents); + let in_memory_root = in_memory.storage_root(::std::iter::empty()).0; + (0..64).for_each(|i| assert_eq!(in_memory.storage(&[i]).unwrap().unwrap(), vec![i])); + + let trie = in_memory.try_into_trie_backend().unwrap(); + let trie_root = trie.storage_root(::std::iter::empty()).0; + assert_eq!(in_memory_root, trie_root); + (0..64).for_each(|i| assert_eq!(trie.storage(&[i]).unwrap().unwrap(), vec![i])); + + let proving = ProvingBackend::new(trie); + assert_eq!(proving.storage(&[42]).unwrap().unwrap(), vec![42]); + + let proof = proving.extract_proof(); + + let proof_check = create_proof_check_backend::(in_memory_root.into(), proof).unwrap(); + assert_eq!(proof_check.storage(&[42]).unwrap().unwrap(), vec![42]); + } +} diff --git a/core/state-machine/src/testing.rs b/core/state-machine/src/testing.rs new file mode 100644 index 0000000000000..bcb02f6bb06e0 --- /dev/null +++ b/core/state-machine/src/testing.rs @@ -0,0 +1,118 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Test implementation for Externalities. + +use std::collections::HashMap; +use std::cmp::Ord; +use super::Externalities; +use triehash::trie_root; +use hashdb::Hasher; +use rlp::Encodable; +use std::marker::PhantomData; +use std::iter::FromIterator; + +/// Simple HashMap-based Externalities impl. +#[derive(Debug)] +pub struct TestExternalities { + inner: HashMap, Vec>, + _hasher: PhantomData, +} + +impl TestExternalities { + /// Create a new instance of `TestExternalities` + pub fn new() -> Self { + TestExternalities {inner: HashMap::new(), _hasher: PhantomData} + } + /// Insert key/value + pub fn insert(&mut self, k: Vec, v: Vec) -> Option> { + self.inner.insert(k, v) + } +} + +impl PartialEq for TestExternalities { + fn eq(&self, other: &TestExternalities) -> bool { + self.inner.eq(&other.inner) + } +} + +impl FromIterator<(Vec, Vec)> for TestExternalities { + fn from_iter, Vec)>>(iter: I) -> Self { + let mut t = Self::new(); + for i in iter { + t.inner.insert(i.0, i.1); + } + t + } +} + +impl Default for TestExternalities { + fn default() -> Self { Self::new() } +} + +impl From> for HashMap, Vec> { + fn from(tex: TestExternalities) -> Self { + tex.inner.into() + } +} + +impl From< HashMap, Vec> > for TestExternalities { + fn from(hashmap: HashMap, Vec>) -> Self { + TestExternalities { inner: hashmap, _hasher: PhantomData } + } +} + + +impl Externalities for TestExternalities where H::Out: Ord + Encodable { + fn storage(&self, key: &[u8]) -> Option> { + self.inner.get(key).map(|x| x.to_vec()) + } + + fn place_storage(&mut self, key: Vec, maybe_value: Option>) { + match maybe_value { + Some(value) => { self.inner.insert(key, value); } + None => { self.inner.remove(&key); } + } + } + + fn clear_prefix(&mut self, prefix: &[u8]) { + self.inner.retain(|key, _| + !key.starts_with(prefix) + ) + } + + fn chain_id(&self) -> u64 { 42 } + + fn storage_root(&mut self) -> H::Out { + trie_root::(self.inner.clone()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use primitives::{Blake2Hasher, H256}; + + #[test] + fn commit_should_work() { + let mut ext = TestExternalities::::new(); + ext.set_storage(b"doe".to_vec(), b"reindeer".to_vec()); + ext.set_storage(b"dog".to_vec(), b"puppy".to_vec()); + ext.set_storage(b"dogglesworth".to_vec(), b"cat".to_vec()); + const ROOT: [u8; 32] = hex!("6ca394ff9b13d6690a51dea30b1b5c43108e52944d30b9095227c49bae03ff8b"); + assert_eq!(ext.storage_root(), H256(ROOT)); + } +} diff --git a/core/state-machine/src/trie_backend.rs b/core/state-machine/src/trie_backend.rs new file mode 100644 index 0000000000000..565cc5387f486 --- /dev/null +++ b/core/state-machine/src/trie_backend.rs @@ -0,0 +1,362 @@ +// Copyright 2017 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 . + +//! Trie-based state machine backend. +use Backend; +use hashdb::{Hasher, HashDB, AsHashDB}; +use memorydb::MemoryDB; +use patricia_trie::{TrieDB, TrieDBMut, TrieError, Trie, TrieMut, NodeCodec}; +use std::collections::HashMap; +use std::sync::Arc; +use std::marker::PhantomData; +use heapsize::HeapSizeOf; + +pub use hashdb::DBValue; + +/// Backend trie storage trait. +pub trait Storage: Send + Sync { + /// Get a trie node. + fn get(&self, key: &H::Out) -> Result, String>; +} + +/// Try convert into trie-based backend. +pub trait TryIntoTrieBackend> { + /// Try to convert self into trie backend. + fn try_into_trie_backend(self) -> Option>; +} + +/// Patricia trie-based backend. Transaction type is an overlay of changes to commit. +#[derive(Clone)] +pub struct TrieBackend> { + storage: TrieBackendStorage, + root: H::Out, + _codec: PhantomData +} + +impl> TrieBackend where H::Out: HeapSizeOf { + /// Create new trie-based backend. + pub fn with_storage(db: Arc>, root: H::Out) -> Self { + TrieBackend { + storage: TrieBackendStorage::Storage(db), + root, + _codec: PhantomData, + } + } + + /// Create new trie-based backend for genesis block. + pub fn with_storage_for_genesis(db: Arc>) -> Self { + let mut root = ::Out::default(); + let mut mdb = MemoryDB::::new(); + TrieDBMut::::new(&mut mdb, &mut root); + + Self::with_storage(db, root) + } + + /// Create new trie-based backend backed by MemoryDb storage. + pub fn with_memorydb(db: MemoryDB, root: H::Out) -> Self { + // TODO: check that root is a part of db??? + TrieBackend { + storage: TrieBackendStorage::MemoryDb(db), + root, + _codec: PhantomData, + } + } + + /// Get backend storage reference. + pub fn backend_storage(&self) -> &TrieBackendStorage { + &self.storage + } + + /// Get trie root. + pub fn root(&self) -> &H::Out { + &self.root + } +} + +impl super::Error for String {} + +impl> Backend for TrieBackend where H::Out: HeapSizeOf { + type Error = String; + type Transaction = MemoryDB; + + fn storage(&self, key: &[u8]) -> Result>, Self::Error> { + let mut read_overlay = MemoryDB::new(); + let eph = Ephemeral { + storage: &self.storage, + overlay: &mut read_overlay, + }; + + let map_e = |e| format!("Trie lookup error: {}", e); + TrieDB::::new(&eph, &self.root).map_err(map_e)? + .get(key).map(|x| x.map(|val| val.to_vec())).map_err(map_e) + } + + fn for_keys_with_prefix(&self, prefix: &[u8], mut f: F) { + let mut read_overlay = MemoryDB::new(); + let eph = Ephemeral { + storage: &self.storage, + overlay: &mut read_overlay, + }; + + let mut iter = move || -> Result<(), Box>> { + let trie = TrieDB::::new(&eph, &self.root)?; + let mut iter = trie.iter()?; + + iter.seek(prefix)?; + + for x in iter { + let (key, _) = x?; + + if !key.starts_with(prefix) { + break; + } + + f(&key); + } + + Ok(()) + }; + + if let Err(e) = iter() { + debug!(target: "trie", "Error while iterating by prefix: {}", e); + } + } + + fn pairs(&self) -> Vec<(Vec, Vec)> { + let mut read_overlay = MemoryDB::new(); + let eph = Ephemeral { + storage: &self.storage, + overlay: &mut read_overlay, + }; + + let collect_all = || -> Result<_, Box>> { + let trie = TrieDB::::new(&eph, &self.root)?; + let mut v = Vec::new(); + for x in trie.iter()? { + let (key, value) = x?; + v.push((key.to_vec(), value.to_vec())); + } + + Ok(v) + }; + + match collect_all() { + Ok(v) => v, + Err(e) => { + debug!(target: "trie", "Error extracting trie values: {}", e); + Vec::new() + } + } + } + + fn storage_root(&self, delta: I) -> (H::Out, MemoryDB) + where I: IntoIterator, Option>)> + { + let mut write_overlay = MemoryDB::new(); + let mut root = self.root; + { + let mut eph = Ephemeral { + storage: &self.storage, + overlay: &mut write_overlay, + }; + + let mut trie = TrieDBMut::::from_existing(&mut eph, &mut root).expect("prior state root to exist"); // TODO: handle gracefully + for (key, change) in delta { + let result = match change { + Some(val) => trie.insert(&key, &val), + None => trie.remove(&key), // TODO: archive mode + }; + + if let Err(e) = result { + warn!(target: "trie", "Failed to write to trie: {}", e); + } + } + } + + (root, write_overlay) + } +} + +impl> TryIntoTrieBackend for TrieBackend { + fn try_into_trie_backend(self) -> Option> { + Some(self) + } +} + +pub struct Ephemeral<'a, H: 'a + Hasher> { + storage: &'a TrieBackendStorage, + overlay: &'a mut MemoryDB, +} + +impl<'a, H: Hasher> AsHashDB for Ephemeral<'a, H> where H::Out: HeapSizeOf { + fn as_hashdb(&self) -> &HashDB { self } + fn as_hashdb_mut(&mut self) -> &mut HashDB { self } +} + +impl<'a, H: Hasher> Ephemeral<'a, H> { + pub fn new(storage: &'a TrieBackendStorage, overlay: &'a mut MemoryDB) -> Self { + Ephemeral { + storage, + overlay, + } + } +} + +impl<'a, H: Hasher> HashDB for Ephemeral<'a, H> where H::Out: HeapSizeOf { + fn keys(&self) -> HashMap { + self.overlay.keys() // TODO: iterate backing + } + + fn get(&self, key: &H::Out) -> Option { + match self.overlay.raw(key) { + Some((val, i)) => { + if i <= 0 { + None + } else { + Some(val) + } + } + None => match self.storage.get(key) { + Ok(x) => x, + Err(e) => { + warn!(target: "trie", "Failed to read from DB: {}", e); + None + }, + }, + } + } + + fn contains(&self, key: &H::Out) -> bool { + self.get(key).is_some() + } + + fn insert(&mut self, value: &[u8]) -> H::Out { + self.overlay.insert(value) + } + + fn emplace(&mut self, key: H::Out, value: DBValue) { + self.overlay.emplace(key, value) + } + + fn remove(&mut self, key: &H::Out) { + self.overlay.remove(key) + } +} + +#[derive(Clone)] +pub enum TrieBackendStorage { + /// Key value db + storage column. + Storage(Arc>), + /// Hash db. + MemoryDb(MemoryDB), +} + +impl TrieBackendStorage { + pub fn get(&self, key: &H::Out) -> Result, String> { + match *self { + TrieBackendStorage::Storage(ref db) => + db.get(key) + .map_err(|e| format!("Trie lookup error: {}", e)), + TrieBackendStorage::MemoryDb(ref db) => + Ok(db.get(key)), + } + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + use std::collections::HashSet; + use primitives::{Blake2Hasher, RlpCodec, H256}; + + fn test_db() -> (MemoryDB, H256) { + let mut root = H256::default(); + let mut mdb = MemoryDB::::new(); + { + let mut trie = TrieDBMut::<_, RlpCodec>::new(&mut mdb, &mut root); + trie.insert(b"key", b"value").expect("insert failed"); + trie.insert(b"value1", &[42]).expect("insert failed"); + trie.insert(b"value2", &[24]).expect("insert failed"); + trie.insert(b":code", b"return 42").expect("insert failed"); + for i in 128u8..255u8 { + trie.insert(&[i], &[i]).unwrap(); + } + } + (mdb, root) + } + + pub(crate) fn test_trie() -> TrieBackend { + let (mdb, root) = test_db(); + TrieBackend::with_memorydb(mdb, root) + } + + #[test] + fn read_from_storage_returns_some() { + assert_eq!(test_trie().storage(b"key").unwrap(), Some(b"value".to_vec())); + } + + #[test] + fn read_from_storage_returns_none() { + assert_eq!(test_trie().storage(b"non-existing-key").unwrap(), None); + } + + #[test] + fn pairs_are_not_empty_on_non_empty_storage() { + assert!(!test_trie().pairs().is_empty()); + } + + #[test] + fn pairs_are_empty_on_empty_storage() { + let db = TrieBackend::::with_memorydb( + MemoryDB::new(), + Default::default() + ); + assert!(db.pairs().is_empty()); + } + + #[test] + fn storage_root_is_non_default() { + assert!(test_trie().storage_root(::std::iter::empty()).0 != H256([0; 32])); + } + + #[test] + fn storage_root_transaction_is_empty() { + assert!(test_trie().storage_root(::std::iter::empty()).1.drain().is_empty()); + } + + #[test] + fn storage_root_transaction_is_non_empty() { + let (new_root, mut tx) = test_trie().storage_root(vec![(b"new-key".to_vec(), Some(b"new-value".to_vec()))]); + assert!(!tx.drain().is_empty()); + assert!(new_root != test_trie().storage_root(::std::iter::empty()).0); + } + + #[test] + fn prefix_walking_works() { + let trie = test_trie(); + + let mut seen = HashSet::new(); + trie.for_keys_with_prefix(b"value", |key| { + let for_first_time = seen.insert(key.to_vec()); + assert!(for_first_time, "Seen key '{:?}' more than once", key); + }); + + let mut expected = HashSet::new(); + expected.insert(b"value1".to_vec()); + expected.insert(b"value2".to_vec()); + assert_eq!(seen, expected); + } +} diff --git a/core/telemetry/Cargo.toml b/core/telemetry/Cargo.toml new file mode 100644 index 0000000000000..2e4e8db6d6bd3 --- /dev/null +++ b/core/telemetry/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "substrate-telemetry" +version = "0.3.0" +authors = ["Parity Technologies "] +description = "Telemetry utils" + +[dependencies] +parking_lot = "0.4" +lazy_static = "1.0" +log = "0.3" +slog = "^2" +slog-json = "^2" +slog-async = "^2" +slog-scope = "^4" +websocket = "^0.20" diff --git a/core/telemetry/README.adoc b/core/telemetry/README.adoc new file mode 100644 index 0000000000000..9759c5bc50bdd --- /dev/null +++ b/core/telemetry/README.adoc @@ -0,0 +1,13 @@ + += Telemetry + +.Summary +[source, toml] +---- +include::Cargo.toml[lines=2..5] +---- + +.Description +---- +include::src/lib.rs[tag=description] +---- diff --git a/core/telemetry/src/lib.rs b/core/telemetry/src/lib.rs new file mode 100644 index 0000000000000..21e8685b36031 --- /dev/null +++ b/core/telemetry/src/lib.rs @@ -0,0 +1,160 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +// tag::description[] +//! Telemetry utils. +//! +//! `telemetry` macro be used from whereever in the Substrate codebase +//! in order to send real-time logging information to the telemetry +//! server (if there is one). We use the async drain adapter of `slog` +//! so that the logging thread doesn't get held up at all. +// end::description[] + +extern crate parking_lot; +extern crate websocket as ws; +extern crate slog_async; +extern crate slog_json; +#[macro_use] +extern crate log; +#[macro_use(o, kv)] +extern crate slog; +extern crate slog_scope; + +use std::{io, time}; +use parking_lot::Mutex; +use slog::Drain; +pub use slog_scope::with_logger; + +/// Configuration for telemetry. +pub struct TelemetryConfig { + /// URL of the telemetry WebSocket server. + pub url: String, + /// What do do when we connect to the server. + pub on_connect: Box, +} + +/// Telemetry service guard. +pub type Telemetry = slog_scope::GlobalLoggerGuard; + +/// Size of the channel for passing messages to telemetry thread. +const CHANNEL_SIZE: usize = 262144; + +/// Initialise telemetry. +pub fn init_telemetry(config: TelemetryConfig) -> slog_scope::GlobalLoggerGuard { + let client = ws::ClientBuilder::new(&config.url).ok().and_then(|mut x| x.connect(None).ok()); + let log = slog::Logger::root( + slog_async::Async::new( + slog_json::Json::default( + TelemetryWriter { + buffer: vec![], + out: Mutex::new(client), + config, + last_time: None, // ensures that on_connect will be called. + } + ).fuse() + ).chan_size(CHANNEL_SIZE) + .overflow_strategy(slog_async::OverflowStrategy::DropAndReport) + .build().fuse(), o!() + ); + slog_scope::set_global_logger(log) +} + +/// Exactly equivalent to `slog_scope::info`, provided as a convenience. +#[macro_export] +macro_rules! telemetry { + ( $($t:tt)* ) => { $crate::with_logger(|l| slog_info!(l, $($t)* )) } +} + +struct TelemetryWriter { + buffer: Vec, + out: Mutex>>>, + config: TelemetryConfig, + last_time: Option, +} + +/// Every two minutes we reconnect to the telemetry server otherwise we don't get notified +/// of a flakey connection that has been dropped and needs to be reconnected. We can remove +/// this once we introduce a keepalive ping/pong. +const RECONNECT_PERIOD: u64 = 120; + +impl TelemetryWriter { + fn ensure_connected(&mut self) { + let mut client = self.out.lock(); + + let controlled_disconnect = if let Some(t) = self.last_time { + if t.elapsed().as_secs() > RECONNECT_PERIOD && client.is_some() { + trace!(target: "telemetry", "Performing controlled drop of the telemetry connection."); + let _ = client.as_mut().and_then(|socket| + socket.send_message(&ws::Message::text("{\"msg\":\"system.reconnect\"}")).ok() + ); + *client = None; + true + } else { + false + } + } else { + false + }; + + let just_connected = if client.is_none() { + if !controlled_disconnect { + info!(target: "telemetry", "Connection dropped unexpectedly. Reconnecting to telemetry server..."); + } + *client = ws::ClientBuilder::new(&self.config.url).ok().and_then(|mut x| x.connect(None).ok()); + client.is_some() + } else { + self.last_time.is_none() + }; + + drop(client); + if just_connected { + if !controlled_disconnect { + info!("Reconnected to telemetry server: {}", self.config.url); + } + self.last_time = Some(time::Instant::now()); + (self.config.on_connect)(); + } + } +} + +impl io::Write for TelemetryWriter { + fn write(&mut self, msg: &[u8]) -> io::Result { + if msg.iter().any(|x| *x == b'\n') { + let _ = self.flush(); + } else { + self.buffer.extend_from_slice(msg); + } + Ok(msg.len()) + } + + fn flush(&mut self) -> io::Result<()> { + self.ensure_connected(); + + let mut l = self.out.lock(); + let socket_closed = if let Some(ref mut socket) = *l { + if let Ok(s) = ::std::str::from_utf8(&self.buffer[..]) { + let r = socket.send_message(&ws::Message::text(s)); + trace!(target: "telemetry", "Sent to telemetry: {} -> {:?}", s, r); + r.is_err() + } else { false } + } else { false }; + if socket_closed { + *l = None; + } + self.buffer.clear(); + Ok(()) + } +} diff --git a/core/test-client/Cargo.toml b/core/test-client/Cargo.toml new file mode 100644 index 0000000000000..eae01feafe979 --- /dev/null +++ b/core/test-client/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "substrate-test-client" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +rhododendron = "0.3" +substrate-bft = { path = "../bft" } +substrate-client = { path = "../client" } +substrate-codec = { path = "../codec" } +substrate-executor = { path = "../executor" } +substrate-keyring = { path = "../../core/keyring" } +substrate-primitives = { path = "../primitives" } +substrate-runtime-support = { path = "../../runtime/support" } +substrate-test-runtime = { path = "../test-runtime" } +substrate-runtime-primitives = { path = "../../runtime/primitives" } +hashdb = "0.2.1" + diff --git a/core/test-client/README.adoc b/core/test-client/README.adoc new file mode 100644 index 0000000000000..e56c4c7f66e7c --- /dev/null +++ b/core/test-client/README.adoc @@ -0,0 +1,13 @@ + += Test client + +.Summary +[source, toml] +---- +include::Cargo.toml[lines=2..5] +---- + +.Description +---- +include::src/lib.rs[tag=description] +---- diff --git a/core/test-client/src/block_builder_ext.rs b/core/test-client/src/block_builder_ext.rs new file mode 100644 index 0000000000000..4cc0854771a2c --- /dev/null +++ b/core/test-client/src/block_builder_ext.rs @@ -0,0 +1,42 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Block Builder extensions for tests. + +use codec; +use client; +use keyring; +use runtime; + +use {Backend, Executor}; +use primitives::{Blake2Hasher, RlpCodec}; + +/// Extension trait for test block builder. +pub trait BlockBuilderExt { + /// Add transfer extrinsic to the block. + fn push_transfer(&mut self, transfer: runtime::Transfer) -> Result<(), client::error::Error>; +} + +impl BlockBuilderExt for client::block_builder::BlockBuilder { + fn push_transfer(&mut self, transfer: runtime::Transfer) -> Result<(), client::error::Error> { + self.push(sign_tx(transfer)) + } +} + +fn sign_tx(transfer: runtime::Transfer) -> runtime::Extrinsic { + let signature = keyring::Keyring::from_raw_public(transfer.from.0.clone()).unwrap().sign(&codec::Encode::encode(&transfer)).into(); + runtime::Extrinsic { transfer, signature } +} diff --git a/core/test-client/src/client_ext.rs b/core/test-client/src/client_ext.rs new file mode 100644 index 0000000000000..386df6915830e --- /dev/null +++ b/core/test-client/src/client_ext.rs @@ -0,0 +1,103 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Client extension for tests. + +use client::{self, Client}; +use keyring::Keyring; +use runtime_primitives::StorageMap; +use runtime::genesismap::{GenesisConfig, additional_storage_with_genesis}; +use executor::NativeExecutor; +use runtime; +use bft; +use {Backend, Executor}; + +/// Extension trait for a test client. +pub trait TestClient { + /// Crates new client instance for tests. + fn new_for_tests() -> Self; + + /// Justify and import block to the chain. + fn justify_and_import(&self, origin: client::BlockOrigin, block: runtime::Block) -> client::error::Result<()>; + + /// Returns hash of the genesis block. + fn genesis_hash(&self) -> runtime::Hash; +} + +impl TestClient for Client { + fn new_for_tests() -> Self { + client::new_in_mem(NativeExecutor::new(), genesis_storage()).unwrap() + } + + fn justify_and_import(&self, origin: client::BlockOrigin, block: runtime::Block) -> client::error::Result<()> { + let justification = fake_justify(&block.header); + let justified = self.check_justification(block.header, justification)?; + self.import_block(origin, justified, Some(block.extrinsics))?; + + Ok(()) + } + + fn genesis_hash(&self) -> runtime::Hash { + self.block_hash(0).unwrap().unwrap() + } +} + +/// Prepare fake justification for the header. +/// +/// since we are in the client module we can create falsely justified +/// headers. +/// TODO: remove this in favor of custom verification pipelines for the +/// client +fn fake_justify(header: &runtime::Header) -> bft::UncheckedJustification { + let hash = header.hash(); + let authorities = vec![ + Keyring::Alice.into(), + Keyring::Bob.into(), + Keyring::Charlie.into(), + ]; + + bft::UncheckedJustification::new( + hash, + authorities.iter().map(|key| { + let msg = bft::sign_message::( + ::rhododendron::Vote::Commit(1, hash).into(), + key, + header.parent_hash + ); + + match msg { + ::rhododendron::LocalizedMessage::Vote(vote) => vote.signature, + _ => panic!("signing vote leads to signed vote"), + } + }).collect(), + 1, + ) +} + +fn genesis_config() -> GenesisConfig { + GenesisConfig::new_simple(vec![ + Keyring::Alice.to_raw_public().into(), + Keyring::Bob.to_raw_public().into(), + Keyring::Charlie.to_raw_public().into(), + ], 1000) +} + +fn genesis_storage() -> StorageMap { + let mut storage = genesis_config().genesis_map(); + let block: runtime::Block = client::genesis::construct_genesis_block(&storage); + storage.extend(additional_storage_with_genesis(&block)); + storage +} diff --git a/core/test-client/src/lib.rs b/core/test-client/src/lib.rs new file mode 100644 index 0000000000000..3aeefed0fafb2 --- /dev/null +++ b/core/test-client/src/lib.rs @@ -0,0 +1,66 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +// tag::description[] +//! Client testing utilities. +// end::description[] + +#![warn(missing_docs)] + +extern crate rhododendron; +extern crate substrate_bft as bft; +extern crate substrate_codec as codec; +extern crate substrate_primitives as primitives; +extern crate substrate_runtime_support as runtime_support; +extern crate substrate_runtime_primitives as runtime_primitives; +#[macro_use] extern crate substrate_executor as executor; +extern crate hashdb; + +pub extern crate substrate_client as client; +pub extern crate substrate_keyring as keyring; +pub extern crate substrate_test_runtime as runtime; + +mod client_ext; +mod block_builder_ext; + +pub use client_ext::TestClient; +pub use block_builder_ext::BlockBuilderExt; + +use primitives::{Blake2Hasher, RlpCodec}; + +mod local_executor { + #![allow(missing_docs)] + use super::runtime; + // TODO: change the macro and pass in the `BlakeHasher` that dispatch needs from here instead + native_executor_instance!(pub LocalExecutor, runtime::api::dispatch, runtime::VERSION, include_bytes!("../../test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm")); +} + +/// Native executor used for tests. +pub use local_executor::LocalExecutor; + +/// Test client database backend. +pub type Backend = client::in_mem::Backend; + +/// Test client executor. +pub type Executor = client::LocalCallExecutor< + Backend, + executor::NativeExecutor, +>; + +/// Creates new client instance used for tests. +pub fn new() -> client::Client { + TestClient::new_for_tests() +} diff --git a/core/test-runtime/Cargo.toml b/core/test-runtime/Cargo.toml new file mode 100644 index 0000000000000..784c7252e277e --- /dev/null +++ b/core/test-runtime/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "substrate-test-runtime" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +log = { version = "0.3", optional = true } +hex-literal = { version = "0.1.0", optional = true } +serde = { version = "1.0", optional = true } +serde_derive = { version = "1.0", optional = true } +substrate-keyring = { path = "../keyring", optional = true } +substrate-codec = { path = "../codec", default-features = false } +substrate-codec-derive = { path = "../codec/derive", default-features = false } +substrate-runtime-std = { path = "../runtime-std", default-features = false } +substrate-runtime-io = { path = "../runtime-io", default-features = false } +substrate-runtime-support = { path = "../../runtime/support", default-features = false } +substrate-primitives = { path = "../primitives", default-features = false } +substrate-runtime-primitives = { path = "../../runtime/primitives", default-features = false } +substrate-runtime-version = { path = "../../runtime/version", default-features = false } + +[features] +default = ["std"] +std = [ + "log", + "hex-literal", + "serde", + "serde_derive", + "substrate-keyring", + "substrate-codec/std", + "substrate-runtime-std/std", + "substrate-runtime-io/std", + "substrate-runtime-support/std", + "substrate-primitives/std", + "substrate-runtime-primitives/std", + "substrate-runtime-version/std" +] diff --git a/core/test-runtime/README.adoc b/core/test-runtime/README.adoc new file mode 100644 index 0000000000000..15b3c4c4ac327 --- /dev/null +++ b/core/test-runtime/README.adoc @@ -0,0 +1,13 @@ + += Test runtime + +.Summary +[source, toml] +---- +include::Cargo.toml[lines=2..5] +---- + +.Description +---- +include::src/lib.rs[tag=description] +---- diff --git a/core/test-runtime/src/genesismap.rs b/core/test-runtime/src/genesismap.rs new file mode 100644 index 0000000000000..fe5fd84acd014 --- /dev/null +++ b/core/test-runtime/src/genesismap.rs @@ -0,0 +1,67 @@ +// Copyright 2017 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 . + +//! Tool for creating the genesis block. + +use std::collections::HashMap; +use runtime_io::twox_128; +use codec::{KeyedVec, Joiner}; +use primitives::AuthorityId; +use runtime_primitives::traits::Block; + +/// Configuration of a general Substrate test genesis block. +pub struct GenesisConfig { + pub authorities: Vec, + pub balances: Vec<(AuthorityId, u64)>, +} + +impl GenesisConfig { + pub fn new_simple(authorities: Vec, balance: u64) -> Self { + GenesisConfig { + authorities: authorities.clone(), + balances: authorities.into_iter().map(|a| (a, balance)).collect(), + } + } + + pub fn genesis_map(&self) -> HashMap, Vec> { + let wasm_runtime = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm").to_vec(); + self.balances.iter() + .map(|&(account, balance)| (account.to_keyed_vec(b"balance:"), vec![].and(&balance))) + .map(|(k, v)| (twox_128(&k[..])[..].to_vec(), v.to_vec())) + .chain(vec![ + (b":code"[..].into(), wasm_runtime), + (b":heappages"[..].into(), vec![].and(&(16 as u64))), + (b":auth:len"[..].into(), vec![].and(&(self.authorities.len() as u32))), + ].into_iter()) + .chain(self.authorities.iter() + .enumerate() + .map(|(i, account)| ((i as u32).to_keyed_vec(b":auth:"), vec![].and(account))) + ) + .collect() + } +} + +macro_rules! map { + ($( $name:expr => $value:expr ),*) => ( + vec![ $( ( $name, $value ) ),* ].into_iter().collect() + ) +} + +pub fn additional_storage_with_genesis(genesis_block: &::Block) -> HashMap, Vec> { + map![ + twox_128(&b"latest"[..]).to_vec() => genesis_block.hash().0.to_vec() + ] +} diff --git a/core/test-runtime/src/lib.rs b/core/test-runtime/src/lib.rs new file mode 100644 index 0000000000000..f8fe9d0137156 --- /dev/null +++ b/core/test-runtime/src/lib.rs @@ -0,0 +1,156 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +// tag::description[] +//! The Substrate runtime. This can be compiled with #[no_std], ready for Wasm. +// end::description[] + +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate substrate_runtime_std as rstd; +extern crate substrate_codec as codec; +extern crate substrate_runtime_primitives as runtime_primitives; + +#[cfg(feature = "std")] +extern crate serde; + +#[cfg(feature = "std")] +#[macro_use] +extern crate serde_derive; + +#[macro_use] +extern crate substrate_runtime_support as runtime_support; +#[macro_use] +extern crate substrate_codec_derive; +#[macro_use] +extern crate substrate_runtime_io as runtime_io; +#[macro_use] +extern crate substrate_runtime_version as runtime_version; + + +#[cfg(test)] +#[macro_use] +extern crate hex_literal; +#[cfg(test)] +extern crate substrate_keyring as keyring; +#[cfg_attr(test, macro_use)] +extern crate substrate_primitives as primitives; + +#[cfg(feature = "std")] pub mod genesismap; +pub mod system; + +use rstd::prelude::*; +use codec::{Encode, Decode}; + +use runtime_primitives::traits::{BlindCheckable, BlakeTwo256}; +use runtime_primitives::Ed25519Signature; +use runtime_version::RuntimeVersion; +pub use primitives::hash::H256; + +/// Test runtime version. +pub const VERSION: RuntimeVersion = RuntimeVersion { + spec_name: ver_str!("test"), + impl_name: ver_str!("parity-test"), + authoring_version: 1, + spec_version: 1, + impl_version: 1, +}; + +fn version() -> RuntimeVersion { + VERSION +} + +/// Calls in transactions. +#[derive(Clone, PartialEq, Eq, Encode, Decode)] +#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] +pub struct Transfer { + pub from: AccountId, + pub to: AccountId, + pub amount: u64, + pub nonce: u64, +} + +/// Extrinsic for test-runtime. +#[derive(Clone, PartialEq, Eq, Encode, Decode)] +#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] +pub struct Extrinsic { + pub transfer: Transfer, + pub signature: Ed25519Signature, +} + +impl BlindCheckable for Extrinsic { + type Checked = Self; + + fn check(self) -> Result { + if ::runtime_primitives::verify_encoded_lazy(&self.signature, &self.transfer, &self.transfer.from) { + Ok(self) + } else { + Err("bad signature") + } + } +} + +/// An identifier for an account on this system. +pub type AccountId = H256; +/// A simple hash type for all our hashing. +pub type Hash = H256; +/// The block number type used in this runtime. +pub type BlockNumber = u64; +/// Index of a transaction. +pub type Index = u64; +/// The item of a block digest. +pub type DigestItem = runtime_primitives::generic::DigestItem; +/// The digest of a block. +pub type Digest = runtime_primitives::generic::Digest; +/// A test block. +pub type Block = runtime_primitives::generic::Block; +/// A test block's header. +pub type Header = runtime_primitives::generic::Header; + +/// Run whatever tests we have. +pub fn run_tests(mut input: &[u8]) -> Vec { + use runtime_io::print; + + print("run_tests..."); + let block = Block::decode(&mut input).unwrap(); + print("deserialised block."); + let stxs = block.extrinsics.iter().map(Encode::encode).collect::>(); + print("reserialised transactions."); + [stxs.len() as u8].encode() +} + +fn test_event_json() -> &'static str { + "hallo" +} + +pub mod api { + use system; + impl_stubs!( + version => |()| super::version(), + json_metadata => |()| { + let mut vec = ::runtime_support::metadata::Vec::new(); + vec.push(::runtime_support::metadata::JSONMetadata::Events { + name: "Test", events: &[ ("event", super::test_event_json) ] + }); + vec + }, + authorities => |()| system::authorities(), + initialise_block => |header| system::initialise_block(header), + execute_block => |block| system::execute_block(block), + apply_extrinsic => |utx| system::execute_transaction(utx), + finalise_block => |()| system::finalise_block() + ); +} diff --git a/core/test-runtime/src/system.rs b/core/test-runtime/src/system.rs new file mode 100644 index 0000000000000..b60cfa7872c89 --- /dev/null +++ b/core/test-runtime/src/system.rs @@ -0,0 +1,281 @@ +// Copyright 2017 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 . + +//! System manager: Handles all of the top-level stuff; executing block/transaction, setting code +//! and depositing logs. + +use rstd::prelude::*; +use runtime_io::{storage_root, enumerated_trie_root}; +use runtime_support::storage::{self, StorageValue, StorageMap}; +use runtime_primitives::traits::{Hash as HashT, BlakeTwo256}; +use runtime_primitives::{ApplyError, ApplyOutcome, ApplyResult}; +use codec::{KeyedVec, Encode}; +use super::{AccountId, BlockNumber, Extrinsic, H256 as Hash, Block, Header}; +use primitives::Blake2Hasher; + +const NONCE_OF: &[u8] = b"nonce:"; +const BALANCE_OF: &[u8] = b"balance:"; +const AUTHORITY_AT: &'static[u8] = b":auth:"; +const AUTHORITY_COUNT: &'static[u8] = b":auth:len"; + +storage_items! { + ExtrinsicIndex: b"sys:xti" => required u32; + ExtrinsicData: b"sys:xtd" => required map [ u32 => Vec ]; + // The current block number being processed. Set by `execute_block`. + Number: b"sys:num" => required BlockNumber; + ParentHash: b"sys:pha" => required Hash; +} + +pub fn balance_of(who: AccountId) -> u64 { + storage::get_or(&who.to_keyed_vec(BALANCE_OF), 0) +} + +pub fn nonce_of(who: AccountId) -> u64 { + storage::get_or(&who.to_keyed_vec(NONCE_OF), 0) +} + +/// Get authorities ar given block. +pub fn authorities() -> Vec<::primitives::AuthorityId> { + let len: u32 = storage::unhashed::get(AUTHORITY_COUNT).expect("There are always authorities in test-runtime"); + (0..len) + .map(|i| storage::unhashed::get(&i.to_keyed_vec(AUTHORITY_AT)).expect("Authority is properly encoded in test-runtime")) + .collect() +} + +pub fn initialise_block(header: Header) { + // populate environment. + ::put(&header.number); + ::put(&header.parent_hash); + ::put(0); +} + +/// Actually execute all transitioning for `block`. +pub fn execute_block(block: Block) { + let ref header = block.header; + + // check transaction trie root represents the transactions. + let txs = block.extrinsics.iter().map(Encode::encode).collect::>(); + let txs = txs.iter().map(Vec::as_slice).collect::>(); + let txs_root = enumerated_trie_root::(&txs).into(); + info_expect_equal_hash(&txs_root, &header.extrinsics_root); + assert!(txs_root == header.extrinsics_root, "Transaction trie root must be valid."); + + // execute transactions + block.extrinsics.iter().for_each(|e| { execute_transaction_backend(e).map_err(|_| ()).expect("Extrinsic error"); }); + + // check storage root. + let storage_root = storage_root().into(); + info_expect_equal_hash(&storage_root, &header.state_root); + assert!(storage_root == header.state_root, "Storage root must match that calculated."); +} + +/// Execute a transaction outside of the block execution function. +/// This doesn't attempt to validate anything regarding the block. +pub fn execute_transaction(utx: Extrinsic) -> ApplyResult { + let extrinsic_index = ExtrinsicIndex::get(); + ExtrinsicData::insert(extrinsic_index, utx.encode()); + ExtrinsicIndex::put(extrinsic_index + 1); + execute_transaction_backend(&utx) +} + +/// Finalise the block. +pub fn finalise_block() -> Header { + let extrinsic_index = ExtrinsicIndex::take(); + let txs: Vec<_> = (0..extrinsic_index).map(ExtrinsicData::take).collect(); + let txs = txs.iter().map(Vec::as_slice).collect::>(); + let extrinsics_root = enumerated_trie_root::(&txs).into(); + + let number = ::take(); + let parent_hash = ::take(); + let storage_root = BlakeTwo256::storage_root(); + + Header { + number, + extrinsics_root, + state_root: storage_root, + parent_hash, + digest: Default::default(), + } +} + +fn execute_transaction_backend(utx: &Extrinsic) -> ApplyResult { + use runtime_primitives::traits::BlindCheckable; + + // check signature + let utx = match utx.clone().check() { + Ok(tx) => tx, + Err(_) => return Err(ApplyError::BadSignature), + }; + + let tx: ::Transfer = utx.transfer; + + // check nonce + let nonce_key = tx.from.to_keyed_vec(NONCE_OF); + let expected_nonce: u64 = storage::get_or(&nonce_key, 0); + if !(tx.nonce == expected_nonce) { + return Err(ApplyError::Stale) + } + + // increment nonce in storage + storage::put(&nonce_key, &(expected_nonce + 1)); + + // check sender balance + let from_balance_key = tx.from.to_keyed_vec(BALANCE_OF); + let from_balance: u64 = storage::get_or(&from_balance_key, 0); + + // enact transfer + if !(tx.amount <= from_balance) { + return Err(ApplyError::CantPay) + } + let to_balance_key = tx.to.to_keyed_vec(BALANCE_OF); + let to_balance: u64 = storage::get_or(&to_balance_key, 0); + storage::put(&from_balance_key, &(from_balance - tx.amount)); + storage::put(&to_balance_key, &(to_balance + tx.amount)); + Ok(ApplyOutcome::Success) +} + +#[cfg(feature = "std")] +fn info_expect_equal_hash(given: &Hash, expected: &Hash) { + use primitives::hexdisplay::HexDisplay; + if given != expected { + println!("Hash: given={}, expected={}", HexDisplay::from(&given.0), HexDisplay::from(&expected.0)); + } +} + +#[cfg(not(feature = "std"))] +fn info_expect_equal_hash(given: &Hash, expected: &Hash) { + if given != expected { + ::runtime_io::print("Hash not equal"); + ::runtime_io::print(&given.0[..]); + ::runtime_io::print(&expected.0[..]); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use runtime_io::{with_externalities, twox_128, TestExternalities}; + use codec::{Joiner, KeyedVec}; + use keyring::Keyring; + use ::{Header, Digest, Extrinsic, Transfer}; + use primitives::Blake2Hasher; + + fn new_test_ext() -> TestExternalities { + map![ + twox_128(b"latest").to_vec() => vec![69u8; 32], + twox_128(b":auth:len").to_vec() => vec![].and(&3u32), + twox_128(&0u32.to_keyed_vec(b":auth:")).to_vec() => Keyring::Alice.to_raw_public().to_vec(), + twox_128(&1u32.to_keyed_vec(b":auth:")).to_vec() => Keyring::Bob.to_raw_public().to_vec(), + twox_128(&2u32.to_keyed_vec(b":auth:")).to_vec() => Keyring::Charlie.to_raw_public().to_vec(), + twox_128(&Keyring::Alice.to_raw_public().to_keyed_vec(b"balance:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0] + ] + } + + fn construct_signed_tx(tx: Transfer) -> Extrinsic { + let signature = Keyring::from_raw_public(tx.from.0).unwrap().sign(&tx.encode()).into(); + Extrinsic { transfer: tx, signature } + } + + #[test] + fn block_import_works() { + let mut t = new_test_ext(); + + let h = Header { + parent_hash: [69u8; 32].into(), + number: 1, + state_root: hex!("0c22599e15fb5e052c84f79a2aab179ba6bb238218fd86bdd4a74ebcc87adfcd").into(), + extrinsics_root: hex!("45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0").into(), + digest: Digest { logs: vec![], }, + }; + + let b = Block { + header: h, + extrinsics: vec![], + }; + + with_externalities(&mut t, || { + execute_block(b); + }); + } + + #[test] + fn block_import_with_transaction_works() { + let mut t = new_test_ext(); + + with_externalities(&mut t, || { + assert_eq!(balance_of(Keyring::Alice.to_raw_public().into()), 111); + assert_eq!(balance_of(Keyring::Bob.to_raw_public().into()), 0); + }); + + let b = Block { + header: Header { + parent_hash: [69u8; 32].into(), + number: 1, + state_root: hex!("0425393fd07e2a806cfd7e990ee91dc92fe6bba34eab2bf45d5be7d67e24d467").into(), + extrinsics_root: hex!("83fd59e8fe7cee53d7421713a09fe0abae1aec5f4db94fe5193737b12195f013").into(), + digest: Digest { logs: vec![], }, + }, + extrinsics: vec![ + construct_signed_tx(Transfer { + from: Keyring::Alice.to_raw_public().into(), + to: Keyring::Bob.to_raw_public().into(), + amount: 69, + nonce: 0, + }) + ], + }; + + with_externalities(&mut t, || { + execute_block(b.clone()); + + assert_eq!(balance_of(Keyring::Alice.to_raw_public().into()), 42); + assert_eq!(balance_of(Keyring::Bob.to_raw_public().into()), 69); + }); + + let b = Block { + header: Header { + parent_hash: b.header.hash(), + number: 2, + state_root: hex!("e32dd1d84d9133ca48078d2d83f2b0db19f9d47229ba98bf5ced0e9f86fac2c7").into(), + extrinsics_root: hex!("5d2d0a93201744f0df878c33b07da40cd38e24ac2358cc2811ea640835c31b68").into(), + digest: Digest { logs: vec![], }, + }, + extrinsics: vec![ + construct_signed_tx(Transfer { + from: Keyring::Bob.to_raw_public().into(), + to: Keyring::Alice.to_raw_public().into(), + amount: 27, + nonce: 0, + }), + construct_signed_tx(Transfer { + from: Keyring::Alice.to_raw_public().into(), + to: Keyring::Charlie.to_raw_public().into(), + amount: 69, + nonce: 1, + }), + ], + }; + + with_externalities(&mut t, || { + execute_block(b); + + assert_eq!(balance_of(Keyring::Alice.to_raw_public().into()), 0); + assert_eq!(balance_of(Keyring::Bob.to_raw_public().into()), 42); + assert_eq!(balance_of(Keyring::Charlie.to_raw_public().into()), 69); + }); + } +} diff --git a/core/test-runtime/wasm/Cargo.lock b/core/test-runtime/wasm/Cargo.lock new file mode 100644 index 0000000000000..82f7da6df048b --- /dev/null +++ b/core/test-runtime/wasm/Cargo.lock @@ -0,0 +1,709 @@ +[[package]] +name = "arrayvec" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nodrop 0.1.12 (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 = "bitflags" +version = "1.0.3" +source = "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.7 (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 = "byteorder" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cfg-if" +version = "0.1.3" +source = "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 = "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-epoch" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.3 (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.0.1 (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.12 (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-utils" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.3 (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 = "ed25519" +version = "0.1.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)", + "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ring 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-primitives 0.1.0", + "untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "elastic-array" +version = "0.10.0" +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 = "ethbloom" +version = "0.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)", + "ethereum-types-serialize 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fixed-hash 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ethereum-types" +version = "0.4.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)", + "ethbloom 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types-serialize 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fixed-hash 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "uint 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ethereum-types-serialize" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fixed-hash" +version = "0.2.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)", + "libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hex 1.0.0 (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.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)", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "gcc" +version = "0.3.54" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "hashdb" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "elastic-array 0.10.0 (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.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hex-literal" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "hex-literal-impl 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hex-literal-impl" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "integer-sqrt" +version = "0.1.0" +source = "git+https://github.com/paritytech/integer-sqrt-rs.git#886e9cb983c46498003878afe965d55caa762025" + +[[package]] +name = "lazy_static" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "lazy_static" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.41" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "log" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "log" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.3 (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_units" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "nan-preserving-float" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "nodrop" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "num-traits" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "num_cpus" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parity-bytes" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "parity-wasm" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "patricia-trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hashdb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "plain_hasher" +version = "0.2.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)", +] + +[[package]] +name = "proc-macro-hack" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro-hack-impl 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro-hack-impl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "proc-macro2" +version = "0.4.4" +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 = "pwasm-alloc" +version = "0.1.0" +dependencies = [ + "pwasm-libc 0.1.0", + "rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pwasm-libc" +version = "0.1.0" + +[[package]] +name = "quote" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.3.22" +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)", + "libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.4.2" +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)", + "libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rayon" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rayon-core 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rayon-core" +version = "1.4.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)", + "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ring" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rlp" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hex 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc-hex" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc-hex" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc_version" +version = "0.2.2" +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 = "scopeguard" +version = "0.3.3" +source = "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.64" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde_derive" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "substrate-codec" +version = "0.1.0" +dependencies = [ + "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "substrate-codec-derive" +version = "0.1.0" +dependencies = [ + "proc-macro2 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "substrate-primitives" +version = "0.1.0" +dependencies = [ + "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "fixed-hash 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hashdb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "patricia-trie 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "plain_hasher 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rlp 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hex 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-codec-derive 0.1.0", + "substrate-runtime-std 0.1.0", + "twox-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "uint 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "substrate-runtime-io" +version = "0.1.0" +dependencies = [ + "hashdb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-std 0.1.0", +] + +[[package]] +name = "substrate-runtime-primitives" +version = "0.1.0" +dependencies = [ + "integer-sqrt 0.1.0 (git+https://github.com/paritytech/integer-sqrt-rs.git)", + "num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-codec-derive 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-io 0.1.0", + "substrate-runtime-std 0.1.0", + "substrate-runtime-support 0.1.0", +] + +[[package]] +name = "substrate-runtime-std" +version = "0.1.0" +dependencies = [ + "pwasm-alloc 0.1.0", + "pwasm-libc 0.1.0", + "rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "substrate-runtime-support" +version = "0.1.0" +dependencies = [ + "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-io 0.1.0", + "substrate-runtime-std 0.1.0", +] + +[[package]] +name = "substrate-runtime-version" +version = "0.1.0" +dependencies = [ + "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-codec-derive 0.1.0", + "substrate-runtime-std 0.1.0", + "substrate-runtime-support 0.1.0", +] + +[[package]] +name = "substrate-test-runtime" +version = "0.1.0" +dependencies = [ + "ed25519 0.1.0", + "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-codec-derive 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-io 0.1.0", + "substrate-runtime-primitives 0.1.0", + "substrate-runtime-std 0.1.0", + "substrate-runtime-support 0.1.0", + "substrate-runtime-version 0.1.0", +] + +[[package]] +name = "syn" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.3 (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 = "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 = "twox-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "uint" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crunchy 0.1.6 (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.0 (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 = "untrusted" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "wasmi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "nan-preserving-float 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-wasm 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" +"checksum base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83" +"checksum bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c54bb8f454c567f21197eefcdbf5679d0bd99f2ddbe52e84c77061952e6789" +"checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" +"checksum byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "74c0b906e9446b0a2e4f760cdb3fa4b2c48cdc6db8766a845c54b6ff063fd2e9" +"checksum cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "405216fd8fe65f718daa7102ea808a946b6ce40c742998fbfd3463645552de18" +"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e" +"checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" +"checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150" +"checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" +"checksum crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" +"checksum elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "88d4851b005ef16de812ea9acdb7bece2f0a40dd86c07b85631d7dafa54537bb" +"checksum ethbloom 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a93a43ce2e9f09071449da36bfa7a1b20b950ee344b6904ff23de493b03b386" +"checksum ethereum-types 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "35b3c5a18bc5e73a32a110ac743ec04b02bbbcd3b71d3118d40a6113d509378a" +"checksum ethereum-types-serialize 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ac59a21a9ce98e188f3dace9eb67a6c4a3c67ec7fbc7218cb827852679dc002" +"checksum fixed-hash 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0d5ec8112f00ea8a483e04748a85522184418fd1cf02890b626d8fc28683f7de" +"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 gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb" +"checksum hashdb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f1c71fc577cde89b3345d5f2880fecaf462a32e96c619f431279bdaf1ba5ddb1" +"checksum heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1679e6ea370dee694f91f1dc469bf94cf8f52051d147aec3e1f9497c6fc22461" +"checksum hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4da5f0e01bd8a71a224a4eedecaacfcabda388dbb7a80faf04d3514287572d95" +"checksum hex-literal-impl 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1d340b6514f232f6db1bd16db65302a5278a04fef9ce867cb932e7e5fa21130a" +"checksum integer-sqrt 0.1.0 (git+https://github.com/paritytech/integer-sqrt-rs.git)" = "" +"checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" +"checksum lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e6412c5e2ad9584b0b8e979393122026cdd6d2a80b933f890dcd694ddbe73739" +"checksum libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)" = "ac8ebf8343a981e2fa97042b14768f02ed3e1d602eac06cae6166df3c8ced206" +"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" +"checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2" +"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" +"checksum memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" +"checksum nan-preserving-float 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34d4f00fcc2f4c9efa8cc971db0da9e28290e28e97af47585e48691ef10ff31f" +"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" +"checksum num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "775393e285254d2f5004596d69bb8bc1149754570dcc08cf30cabeba67955e28" +"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" +"checksum parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa5168b4cf41f3835e4bc6ffb32f51bc9365dc50cb351904595b3931d917fd0c" +"checksum parity-wasm 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1c91199d14bd5b78ecade323d4a891d094799749c1b9e82d9c590c2e2849a40" +"checksum patricia-trie 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fa27fc4a972a03d64e5170d7facd2c84c6ed425b38ce62ad98dcfee2f7845b3b" +"checksum plain_hasher 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "95fa6386b1d34aaf0adb9b7dd2885dbe7c34190e6263785e5a7ec2b19044a90f" +"checksum proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ba8d4f9257b85eb6cdf13f055cea3190520aab1409ca2ab43493ea4820c25f0" +"checksum proc-macro-hack-impl 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d5cb6f960ad471404618e9817c0e5d10b1ae74cfdf01fab89ea0641fe7fb2892" +"checksum proc-macro2 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1fa93823f53cfd0f5ac117b189aed6cfdfb2cfc0a9d82e956dd7927595ed7d46" +"checksum quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e44651a0dc4cdd99f71c83b561e221f714912d11af1a4dff0631f923d53af035" +"checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1" +"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" +"checksum rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b614fe08b6665cb9a231d07ac1364b0ef3cb3698f1239ee0c4c3a88a524f54c8" +"checksum rayon-core 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d24ad214285a7729b174ed6d3bcfcb80177807f959d95fafd5bfc5c4f201ac8" +"checksum ring 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6f7d28b30a72c01b458428e0ae988d4149c20d902346902be881e3edc4bb325c" +"checksum rlp 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "524c5ad554859785dfc8469df3ed5e0b5784d4d335877ed47c8d90fc0eb238fe" +"checksum rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0ceb8ce7a5e520de349e1fa172baeba4a9e8d5ef06c47471863530bc4972ee1e" +"checksum rustc-hex 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d2b03280c2813907a030785570c577fb27d3deec8da4c18566751ade94de0ace" +"checksum rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a54aa04a10c68c1c4eacb4337fd883b435997ede17a9385784b990777686b09a" +"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" +"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.64 (registry+https://github.com/rust-lang/crates.io-index)" = "fba5be06346c5200249c8c8ca4ccba4a09e8747c71c16e420bd359a0db4d8f91" +"checksum serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)" = "79e4620ba6fbe051fc7506fab6f84205823564d55da18d55b695160fb3479cd8" +"checksum syn 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6dfd71b2be5a58ee30a6f8ea355ba8290d397131c00dfa55c3d34e6e13db5101" +"checksum tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e9175261fbdb60781fcd388a4d6cc7e14764a2b629a7ad94abb439aed223a44f" +"checksum twox-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "475352206e7a290c5fccc27624a163e8d0d115f7bb60ca18a64fc9ce056d7435" +"checksum uint 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "754ba11732b9161b94c41798e5197e5e75388d012f760c42adb5000353e98646" +"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +"checksum untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f392d7819dbe58833e26872f5f6f0d68b7bbbe90fc3667e98731c4a15ad9a7ae" +"checksum wasmi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "522fe3fdd44a56f25cd5ddcd8ccdb1cf2e982ceb28fcb00f41d8a018ae5245a8" +"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/core/test-runtime/wasm/Cargo.toml b/core/test-runtime/wasm/Cargo.toml new file mode 100644 index 0000000000000..65cf0f29f201f --- /dev/null +++ b/core/test-runtime/wasm/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "substrate-test-runtime" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +log = { version = "0.3", optional = true } +hex-literal = { version = "0.1.0", optional = true } +substrate-codec = { path = "../../codec", default-features = false } +substrate-codec-derive = { path = "../../codec/derive", default-features = false } +substrate-runtime-std = { path = "../../runtime-std", default-features = false } +substrate-runtime-io = { path = "../../runtime-io", default-features = false } +substrate-primitives = { path = "../../primitives", default-features = false } +substrate-runtime-support = { path = "../../../runtime/support", default-features = false } +substrate-runtime-version = { path = "../../../runtime/version", default-features = false } +substrate-runtime-primitives = { path = "../../../runtime/primitives", default-features = false } + +[lib] +crate-type = ["cdylib"] + +[profile.release] +panic = "abort" +lto = true + +[workspace] +members = [] diff --git a/core/test-runtime/wasm/build.sh b/core/test-runtime/wasm/build.sh new file mode 100755 index 0000000000000..63d9347bf461c --- /dev/null +++ b/core/test-runtime/wasm/build.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -e + +cargo +nightly 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/test-runtime/wasm/src b/core/test-runtime/wasm/src new file mode 120000 index 0000000000000..5cd551cf2693e --- /dev/null +++ b/core/test-runtime/wasm/src @@ -0,0 +1 @@ +../src \ No newline at end of file diff --git a/framework/client/Cargo.toml b/framework/client/Cargo.toml new file mode 100644 index 0000000000000..886410f74eebd --- /dev/null +++ b/framework/client/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "substrate-client" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +error-chain = "0.12" +fnv = "1.0" +log = "0.3" +parking_lot = "0.4" +triehash = "0.2" +hex-literal = "0.1" +futures = "0.1.17" + +slog = "^2" +heapsize = "0.4" +substrate-bft = { path = "../bft" } +substrate-codec = { path = "../codec" } +substrate-executor = { path = "../executor" } +substrate-primitives = { path = "../primitives" } +substrate-runtime-io = { path = "../runtime-io" } +substrate-runtime-support = { path = "../../runtime/support" } +substrate-runtime-primitives = { path = "../../runtime/primitives" } +substrate-state-machine = { path = "../state-machine" } +substrate-keyring = { path = "../keyring" } +substrate-telemetry = { path = "../telemetry" } +hashdb = "0.2.1" +patricia-trie = "0.2.1" +rlp = "0.2.4" + +[dev-dependencies] +substrate-test-client = { path = "../test-client" } diff --git a/framework/executor/Cargo.toml b/framework/executor/Cargo.toml new file mode 100644 index 0000000000000..4c30827dc1b7b --- /dev/null +++ b/framework/executor/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "substrate-executor" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +error-chain = "0.12" +substrate-codec = { path = "../codec" } +substrate-runtime-io = { path = "../runtime-io" } +substrate-primitives = { path = "../primitives" } +substrate-serializer = { path = "../serializer" } +substrate-state-machine = { path = "../state-machine" } +substrate-runtime-version = { path = "../../runtime/version" } + +serde = "1.0" +serde_derive = "1.0" +wasmi = "0.4" +byteorder = "1.1" +triehash = "0.2" +twox-hash = "1.1.0" +lazy_static = "1.0" +parking_lot = "*" +log = "0.3" +hashdb = "0.2.1" + +[dev-dependencies] +assert_matches = "1.1" +wabt = "0.4" +hex-literal = "0.1.0" + +[features] +default = [] +wasm-extern-trace = [] diff --git a/framework/network/Cargo.toml b/framework/network/Cargo.toml new file mode 100644 index 0000000000000..fdd31aa7d02a0 --- /dev/null +++ b/framework/network/Cargo.toml @@ -0,0 +1,30 @@ +[package] +description = "Polkadot network protocol" +name = "substrate-network" +version = "0.1.0" +license = "GPL-3.0" +authors = ["Parity Technologies "] + +[lib] + +[dependencies] +log = "0.3" +parking_lot = "0.4" +error-chain = "0.12" +bitflags = "1.0" +futures = "0.1.17" +linked-hash-map = "0.5" +rustc-hex = "1.0" +ethcore-io = { git = "https://github.com/paritytech/parity.git" } + +substrate-primitives = { path = "../primitives" } +substrate-client = { path = "../client" } +substrate-runtime-primitives = { path = "../../runtime/primitives" } +substrate-codec = { path = "../codec" } +substrate-codec-derive = { path = "../codec/derive" } +substrate-network-libp2p = { path = "../network-libp2p" } + +[dev-dependencies] +env_logger = "0.4" +substrate-keyring = { path = "../keyring" } +substrate-test-client = { path = "../test-client" } diff --git a/framework/runtime-support/Cargo.toml b/framework/runtime-support/Cargo.toml new file mode 100644 index 0000000000000..bf7794a0832f0 --- /dev/null +++ b/framework/runtime-support/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "substrate-runtime-support" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +hex-literal = { version = "0.1.0", optional = true } +serde = { version = "1.0", default_features = false } +serde_derive = { version = "1.0", optional = true } +substrate-runtime-std = { path = "../runtime-std", default_features = false } +substrate-runtime-io = { path = "../runtime-io", default_features = false } +substrate-primitives = { path = "../primitives", default_features = false } +substrate-codec = { path = "../codec", default_features = false } + +[dev-dependencies] +pretty_assertions = "0.5.1" +serde_json = { version = "1.0" } +substrate-codec-derive = { path = "../codec/derive" } + +[features] +default = ["std"] +std = [ + "hex-literal", + "serde/std", + "serde_derive", + "substrate-primitives/std", + "substrate-runtime-io/std", + "substrate-codec/std", + "substrate-runtime-std/std", +] +nightly = [] +strict = [] diff --git a/framework/test-runtime/Cargo.toml b/framework/test-runtime/Cargo.toml new file mode 100644 index 0000000000000..784c7252e277e --- /dev/null +++ b/framework/test-runtime/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "substrate-test-runtime" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +log = { version = "0.3", optional = true } +hex-literal = { version = "0.1.0", optional = true } +serde = { version = "1.0", optional = true } +serde_derive = { version = "1.0", optional = true } +substrate-keyring = { path = "../keyring", optional = true } +substrate-codec = { path = "../codec", default-features = false } +substrate-codec-derive = { path = "../codec/derive", default-features = false } +substrate-runtime-std = { path = "../runtime-std", default-features = false } +substrate-runtime-io = { path = "../runtime-io", default-features = false } +substrate-runtime-support = { path = "../../runtime/support", default-features = false } +substrate-primitives = { path = "../primitives", default-features = false } +substrate-runtime-primitives = { path = "../../runtime/primitives", default-features = false } +substrate-runtime-version = { path = "../../runtime/version", default-features = false } + +[features] +default = ["std"] +std = [ + "log", + "hex-literal", + "serde", + "serde_derive", + "substrate-keyring", + "substrate-codec/std", + "substrate-runtime-std/std", + "substrate-runtime-io/std", + "substrate-runtime-support/std", + "substrate-primitives/std", + "substrate-runtime-primitives/std", + "substrate-runtime-version/std" +] diff --git a/node/Cargo.toml b/node/Cargo.toml new file mode 100644 index 0000000000000..8746954b5a849 --- /dev/null +++ b/node/Cargo.toml @@ -0,0 +1,18 @@ +[[bin]] +name = "substrate" +path = "src/main.rs" + +[package] +name = "substrate" +version = "0.1.0" +authors = ["Parity Technologies "] +build = "build.rs" + +[dependencies] +error-chain = "0.12" +node-cli = { path = "cli" } +futures = "0.1" +ctrlc = { version = "3.0", features = ["termination"] } + +[build-dependencies] +vergen = "0.1" diff --git a/node/api/Cargo.toml b/node/api/Cargo.toml new file mode 100644 index 0000000000000..5a0cee1387772 --- /dev/null +++ b/node/api/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "node-api" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +node-runtime = { path = "../runtime" } +node-primitives = { path = "../primitives" } +substrate-client = { path = "../../core/client" } +substrate-primitives = { path = "../../core/primitives" } + +[dev-dependencies] +substrate-keyring = { path = "../../core/keyring" } diff --git a/node/api/src/lib.rs b/node/api/src/lib.rs new file mode 100644 index 0000000000000..81cf7ffae305d --- /dev/null +++ b/node/api/src/lib.rs @@ -0,0 +1,155 @@ +// Copyright 2018 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 . + +//! Strongly typed API for Substrate runtime. + +#![warn(missing_docs)] +#![warn(unused_extern_crates)] + +extern crate node_primitives as primitives; +extern crate node_runtime as runtime; +extern crate substrate_client as client; +extern crate substrate_primitives; + +pub use client::error::{Error, ErrorKind, Result}; +use runtime::Address; +use client::backend::Backend; +use client::block_builder::BlockBuilder as ClientBlockBuilder; +use client::{Client, CallExecutor}; +use primitives::{ + AccountId, Block, BlockId, Hash, Index, InherentData, + SessionKey, Timestamp, UncheckedExtrinsic, +}; +use substrate_primitives::{Blake2Hasher, RlpCodec}; + +/// Build new blocks. +pub trait BlockBuilder { + /// Push an extrinsic onto the block. Fails if the extrinsic is invalid. + fn push_extrinsic(&mut self, extrinsic: UncheckedExtrinsic) -> Result<()>; + + /// Bake the block with provided extrinsics. + fn bake(self) -> Result; +} + +/// Trait encapsulating the demo API. +/// +/// All calls should fail when the exact runtime is unknown. +pub trait Api { + /// The block builder for this API type. + type BlockBuilder: BlockBuilder; + + /// Get session keys at a given block. + fn session_keys(&self, at: &BlockId) -> Result>; + + /// Get validators at a given block. + fn validators(&self, at: &BlockId) -> Result>; + + /// Get the value of the randomness beacon at a given block. + fn random_seed(&self, at: &BlockId) -> Result; + + /// Get the timestamp registered at a block. + fn timestamp(&self, at: &BlockId) -> Result; + + /// Get the nonce (nĂ© index) of an account at a block. + fn index(&self, at: &BlockId, account: AccountId) -> Result; + + /// Get the account id of an address at a block. + fn lookup(&self, at: &BlockId, address: Address) -> Result>; + + /// Evaluate a block. Returns true if the block is good, false if it is known to be bad, + /// and an error if we can't evaluate for some reason. + fn evaluate_block(&self, at: &BlockId, block: Block) -> Result; + + /// Build a block on top of the given, with inherent extrinsics pre-pushed. + fn build_block(&self, at: &BlockId, inherent_data: InherentData) -> Result; + + /// Attempt to produce the (encoded) inherent extrinsics for a block being built upon the given. + /// This may vary by runtime and will fail if a runtime doesn't follow the same API. + fn inherent_extrinsics(&self, at: &BlockId, inherent_data: InherentData) -> Result>; +} + +impl BlockBuilder for ClientBlockBuilder +where + B: Backend, + E: CallExecutor+ Clone, +{ + fn push_extrinsic(&mut self, extrinsic: UncheckedExtrinsic) -> Result<()> { + self.push(extrinsic).map_err(Into::into) + } + + /// Bake the block with provided extrinsics. + fn bake(self) -> Result { + ClientBlockBuilder::bake(self).map_err(Into::into) + } +} + +impl Api for Client +where + B: Backend, + E: CallExecutor + Clone, +{ + type BlockBuilder = ClientBlockBuilder; + + fn session_keys(&self, at: &BlockId) -> Result> { + Ok(self.authorities_at(at)?) + } + + fn validators(&self, at: &BlockId) -> Result> { + self.call_api(at, "validators", &()) + } + + fn random_seed(&self, at: &BlockId) -> Result { + self.call_api(at, "random_seed", &()) + } + + fn timestamp(&self, at: &BlockId) -> Result { + self.call_api(at, "timestamp", &()) + } + + fn evaluate_block(&self, at: &BlockId, block: Block) -> Result { + let res: Result<()> = self.call_api(at, "execute_block", &block); + match res { + Ok(()) => Ok(true), + Err(err) => match err.kind() { + &client::error::ErrorKind::Execution(_) => Ok(false), + _ => Err(err) + } + } + } + + fn index(&self, at: &BlockId, account: AccountId) -> Result { + self.call_api(at, "account_nonce", &account) + } + + fn lookup(&self, at: &BlockId, address: Address) -> Result> { + self.call_api(at, "lookup_address", &address) + } + + fn build_block(&self, at: &BlockId, inherent_data: InherentData) -> Result { + let mut block_builder = self.new_block_at(at)?; + for inherent in self.inherent_extrinsics(at, inherent_data)? { + block_builder.push(inherent)?; + } + + Ok(block_builder) + } + + fn inherent_extrinsics(&self, at: &BlockId, inherent_data: InherentData) -> Result> { + let runtime_version = self.runtime_version_at(at)?; + self.call_api(at, "inherent_extrinsics", &(inherent_data, runtime_version.spec_version)) + } +} + diff --git a/node/build.rs b/node/build.rs new file mode 100644 index 0000000000000..4e48353aeac42 --- /dev/null +++ b/node/build.rs @@ -0,0 +1,24 @@ +// Copyright 2015-2018 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 . + +extern crate vergen; + +const ERROR_MSG: &'static str = "Failed to generate metadata files"; + +fn main() { + vergen::vergen(vergen::SHORT_SHA).expect(ERROR_MSG); + println!("cargo:rerun-if-changed=../../.git/HEAD"); +} diff --git a/node/cli/Cargo.toml b/node/cli/Cargo.toml new file mode 100644 index 0000000000000..d26a47aade099 --- /dev/null +++ b/node/cli/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "node-cli" +version = "0.1.0" +authors = ["Parity Technologies "] +description = "Substrate node implementation in Rust." + +[dependencies] +log = "0.3" +tokio = "0.1.7" +exit-future = "0.1" +substrate-cli = { path = "../../core/cli" } +node-service = { path = "../service" } diff --git a/node/cli/src/cli.yml b/node/cli/src/cli.yml new file mode 100644 index 0000000000000..6c1fe186e793b --- /dev/null +++ b/node/cli/src/cli.yml @@ -0,0 +1,12 @@ +name: substrate-node +author: "Parity Team " +about: Substrate Node Rust Implementation +args: + - log: + short: l + value_name: LOG_PATTERN + help: Sets a custom logging + takes_value: true +subcommands: + - validator: + about: Run validator node diff --git a/node/cli/src/error.rs b/node/cli/src/error.rs new file mode 100644 index 0000000000000..a83466fbe679d --- /dev/null +++ b/node/cli/src/error.rs @@ -0,0 +1,29 @@ +// Copyright 2018 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 . + +//! Initialization errors. + +use client; + +error_chain! { + foreign_links { + Io(::std::io::Error) #[doc="IO error"]; + Cli(::clap::Error) #[doc="CLI error"]; + } + links { + Client(client::error::Error, client::error::ErrorKind) #[doc="Client error"]; + } +} diff --git a/node/cli/src/lib.rs b/node/cli/src/lib.rs new file mode 100644 index 0000000000000..75cbf5d003f7e --- /dev/null +++ b/node/cli/src/lib.rs @@ -0,0 +1,122 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Substrate CLI library. + +#![warn(missing_docs)] +#![warn(unused_extern_crates)] + +extern crate tokio; + +extern crate substrate_cli as cli; +extern crate node_service as service; +extern crate exit_future; + +#[macro_use] +extern crate log; + +pub use cli::error; + +use tokio::runtime::Runtime; +pub use service::{Components as ServiceComponents, Service, CustomConfiguration}; +pub use cli::{VersionInfo, IntoExit}; + +/// The chain specification option. +#[derive(Clone, Debug)] +pub enum ChainSpec { + /// Whatever the current runtime is, with just Alice as an auth. + Development, + /// Whatever the current runtime is, with simple Alice/Bob auths. + LocalTestnet, + /// The PoC-1 & PoC-2 era testnet. + Testnet, + /// Whatever the current runtime is with the "global testnet" defaults. + StagingTestnet, +} + +/// Get a chain config from a spec setting. +impl ChainSpec { + pub(crate) fn load(self) -> Result { + Ok(match self { + ChainSpec::Testnet => service::chain_spec::testnet_config()?, + ChainSpec::Development => service::chain_spec::development_config(), + ChainSpec::LocalTestnet => service::chain_spec::local_testnet_config(), + ChainSpec::StagingTestnet => service::chain_spec::staging_testnet_config(), + }) + } + + pub(crate) fn from(s: &str) -> Option { + match s { + "dev" => Some(ChainSpec::Development), + "local" => Some(ChainSpec::LocalTestnet), + "" | "test" => Some(ChainSpec::Testnet), + "staging" => Some(ChainSpec::StagingTestnet), + _ => None, + } + } +} + +fn load_spec(id: &str) -> Result, String> { + Ok(match ChainSpec::from(id) { + Some(spec) => Some(spec.load()?), + None => None, + }) +} + +/// Parse command line arguments into service configuration. +pub fn run(args: I, exit: E, version: cli::VersionInfo) -> error::Result<()> where + I: IntoIterator, + T: Into + Clone, + E: IntoExit, +{ + match cli::prepare_execution::(args, exit, version, load_spec, "substrate-node")? { + cli::Action::ExecutedInternally => (), + cli::Action::RunService((config, exit)) => { + info!("Parity ·:· Substrate"); + info!(" version {}", config.full_version()); + info!(" by Parity Technologies, 2017, 2018"); + info!("Chain specification: {}", config.chain_spec.name()); + info!("Node name: {}", config.name); + info!("Roles: {:?}", config.roles); + let mut runtime = Runtime::new()?; + let executor = runtime.executor(); + match config.roles == service::Roles::LIGHT { + true => run_until_exit(&mut runtime, service::new_light(config, executor)?, exit)?, + false => run_until_exit(&mut runtime, service::new_full(config, executor)?, exit)?, + } + } + } + Ok(()) +} + +fn run_until_exit( + runtime: &mut Runtime, + service: service::Service, + e: E, +) -> error::Result<()> + where + C: service::Components, + E: IntoExit, +{ + let (exit_send, exit) = exit_future::signal(); + + let executor = runtime.executor(); + cli::informant::start(&service, exit.clone(), executor.clone()); + + let _ = runtime.block_on(e.into_exit()); + exit_send.fire(); + Ok(()) +} diff --git a/node/consensus/Cargo.toml b/node/consensus/Cargo.toml new file mode 100644 index 0000000000000..e6c6ac741706f --- /dev/null +++ b/node/consensus/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "node-consensus" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +futures = "0.1.17" +parking_lot = "0.4" +tokio = "0.1.7" +error-chain = "0.12" +log = "0.3" +exit-future = "0.1" +rhododendron = "0.3" +node-api = { path = "../api" } +node-primitives = { path = "../primitives" } +node-runtime = { path = "../runtime" } +node-transaction-pool = { path = "../transaction-pool" } +substrate-bft = { path = "../../core/bft" } +substrate-codec = { path = "../../core/codec" } +substrate-primitives = { path = "../../core/primitives" } +substrate-runtime-support = { path = "../../runtime/support" } +substrate-client = { path = "../../core/client" } +substrate-runtime-primitives = { path = "../../runtime/primitives" } + +[dev-dependencies] +substrate-keyring = { path = "../../core/keyring" } diff --git a/node/consensus/README.adoc b/node/consensus/README.adoc new file mode 100644 index 0000000000000..a3ac5f631c38c --- /dev/null +++ b/node/consensus/README.adoc @@ -0,0 +1,5 @@ + += Polkadot Consensus + +placeholder +//TODO Write content :) diff --git a/node/consensus/src/error.rs b/node/consensus/src/error.rs new file mode 100644 index 0000000000000..01823a8e50384 --- /dev/null +++ b/node/consensus/src/error.rs @@ -0,0 +1,51 @@ +// Copyright 2018 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 . + +//! Errors that can occur during the consensus process. + +use primitives::AuthorityId; + +error_chain! { + links { + Api(::node_api::Error, ::node_api::ErrorKind); + Bft(::bft::Error, ::bft::ErrorKind); + } + + errors { + NotValidator(id: AuthorityId) { + description("Local account ID not a validator at this block."), + display("Local account ID ({:?}) not a validator at this block.", id), + } + PrematureDestruction { + description("Proposer destroyed before finishing proposing or evaluating"), + display("Proposer destroyed before finishing proposing or evaluating"), + } + Timer(e: ::tokio::timer::Error) { + description("Failed to register or resolve async timer."), + display("Timer failed: {}", e), + } + Executor(e: ::futures::future::ExecuteErrorKind) { + description("Unable to dispatch agreement future"), + display("Unable to dispatch agreement future: {:?}", e), + } + } +} + +impl From<::bft::InputStreamConcluded> for Error { + fn from(err: ::bft::InputStreamConcluded) -> Self { + ::bft::Error::from(err).into() + } +} diff --git a/node/consensus/src/evaluation.rs b/node/consensus/src/evaluation.rs new file mode 100644 index 0000000000000..9d4d5b0911905 --- /dev/null +++ b/node/consensus/src/evaluation.rs @@ -0,0 +1,96 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Block evaluation and evaluation errors. + +use super::MAX_TRANSACTIONS_SIZE; + +use codec::{Decode, Encode}; +use node_runtime::{Block as GenericBlock, CheckedBlock}; +use node_primitives::{Block, Hash, BlockNumber, Timestamp}; + +error_chain! { + links { + Api(::node_api::Error, ::node_api::ErrorKind); + } + + errors { + BadProposalFormat { + description("Proposal provided not a block."), + display("Proposal provided not a block."), + } + TimestampInFuture { + description("Proposal had timestamp too far in the future."), + display("Proposal had timestamp too far in the future."), + } + WrongParentHash(expected: Hash, got: Hash) { + description("Proposal had wrong parent hash."), + display("Proposal had wrong parent hash. Expected {:?}, got {:?}", expected, got), + } + WrongNumber(expected: BlockNumber, got: BlockNumber) { + description("Proposal had wrong number."), + display("Proposal had wrong number. Expected {:?}, got {:?}", expected, got), + } + ProposalTooLarge(size: usize) { + description("Proposal exceeded the maximum size."), + display( + "Proposal exceeded the maximum size of {} by {} bytes.", + MAX_TRANSACTIONS_SIZE, size.saturating_sub(MAX_TRANSACTIONS_SIZE) + ), + } + } +} + +/// Attempt to evaluate a substrate block as a node block, returning error +/// upon any initial validity checks failing. +pub fn evaluate_initial( + proposal: &Block, + now: Timestamp, + parent_hash: &Hash, + parent_number: BlockNumber, +) -> Result { + const MAX_TIMESTAMP_DRIFT: Timestamp = 60; + + let encoded = Encode::encode(proposal); + let proposal = GenericBlock::decode(&mut &encoded[..]) + .and_then(|b| CheckedBlock::new(b).ok()) + .ok_or_else(|| ErrorKind::BadProposalFormat)?; + + let transactions_size = proposal.extrinsics.iter().fold(0, |a, tx| { + a + Encode::encode(tx).len() + }); + + if transactions_size > MAX_TRANSACTIONS_SIZE { + bail!(ErrorKind::ProposalTooLarge(transactions_size)) + } + + if proposal.header.parent_hash != *parent_hash { + bail!(ErrorKind::WrongParentHash(*parent_hash, proposal.header.parent_hash)); + } + + if proposal.header.number != parent_number + 1 { + bail!(ErrorKind::WrongNumber(parent_number + 1, proposal.header.number)); + } + + let block_timestamp = proposal.timestamp(); + + // lenient maximum -- small drifts will just be delayed using a timer. + if block_timestamp > now + MAX_TIMESTAMP_DRIFT { + bail!(ErrorKind::TimestampInFuture) + } + + Ok(proposal) +} diff --git a/node/consensus/src/lib.rs b/node/consensus/src/lib.rs new file mode 100644 index 0000000000000..2135beebd0ce2 --- /dev/null +++ b/node/consensus/src/lib.rs @@ -0,0 +1,445 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! This service uses BFT consensus provided by the substrate. + +extern crate parking_lot; +extern crate node_api; +extern crate node_transaction_pool as transaction_pool; +extern crate node_runtime; +extern crate node_primitives; + +extern crate substrate_bft as bft; +extern crate substrate_codec as codec; +extern crate substrate_primitives as primitives; +extern crate substrate_runtime_support as runtime_support; +extern crate substrate_runtime_primitives as runtime_primitives; +extern crate substrate_client as client; + +extern crate exit_future; +extern crate tokio; +extern crate rhododendron; + +#[macro_use] +extern crate error_chain; +extern crate futures; + +#[macro_use] +extern crate log; + +#[cfg(test)] +extern crate substrate_keyring; + +use std::sync::Arc; +use std::time::{self, Duration, Instant}; + +use codec::{Decode, Encode}; +use node_api::Api; +use node_primitives::{AccountId, Hash, Block, BlockId, BlockNumber, Header, Timestamp, SessionKey}; +use primitives::{AuthorityId, ed25519}; +use transaction_pool::TransactionPool; +use tokio::runtime::TaskExecutor; +use tokio::timer::Delay; + +use futures::prelude::*; +use futures::future; +use parking_lot::RwLock; + +pub use self::error::{ErrorKind, Error}; +pub use self::offline_tracker::OfflineTracker; +pub use service::Service; + +mod evaluation; +mod error; +mod offline_tracker; +mod service; + +/// Shared offline validator tracker. +pub type SharedOfflineTracker = Arc>; + +// block size limit. +const MAX_TRANSACTIONS_SIZE: usize = 4 * 1024 * 1024; + +/// A long-lived network which can create BFT message routing processes on demand. +pub trait Network { + /// The input stream of BFT messages. Should never logically conclude. + type Input: Stream,Error=Error>; + /// The output sink of BFT messages. Messages sent here should eventually pass to all + /// current authorities. + type Output: Sink,SinkError=Error>; + + /// Instantiate input and output streams. + fn communication_for( + &self, + validators: &[SessionKey], + local_id: SessionKey, + parent_hash: Hash, + task_executor: TaskExecutor + ) -> (Self::Input, Self::Output); +} + +/// Proposer factory. +pub struct ProposerFactory + where + P: Api + Send + Sync + 'static +{ + /// The client instance. + pub client: Arc

, + /// The transaction pool. + pub transaction_pool: Arc>, + /// The backing network handle. + pub network: N, + /// handle to remote task executor + pub handle: TaskExecutor, + /// Offline-tracker. + pub offline: SharedOfflineTracker, +} + +impl bft::Environment for ProposerFactory + where + N: Network, + P: Api + Send + Sync + 'static, +{ + type Proposer = Proposer

; + type Input = N::Input; + type Output = N::Output; + type Error = Error; + + fn init( + &self, + parent_header: &Header, + authorities: &[AuthorityId], + sign_with: Arc, + ) -> Result<(Self::Proposer, Self::Input, Self::Output), Error> { + use runtime_primitives::traits::{Hash as HashT, BlakeTwo256}; + + // force delay in evaluation this long. + const FORCE_DELAY: Timestamp = 5; + + let parent_hash = parent_header.hash().into(); + + let id = BlockId::hash(parent_hash); + let random_seed = self.client.random_seed(&id)?; + let random_seed = BlakeTwo256::hash(&*random_seed); + + let validators = self.client.validators(&id)?; + self.offline.write().note_new_block(&validators[..]); + + info!("Starting consensus session on top of parent {:?}", parent_hash); + + let local_id = sign_with.public().0.into(); + let (input, output) = self.network.communication_for( + authorities, + local_id, + parent_hash.clone(), + self.handle.clone(), + ); + let now = Instant::now(); + let proposer = Proposer { + client: self.client.clone(), + start: now, + local_key: sign_with, + parent_hash, + parent_id: id, + parent_number: parent_header.number, + random_seed, + transaction_pool: self.transaction_pool.clone(), + offline: self.offline.clone(), + validators, + minimum_timestamp: current_timestamp() + FORCE_DELAY, + }; + + Ok((proposer, input, output)) + } +} + +/// The proposer logic. +pub struct Proposer { + client: Arc, + start: Instant, + local_key: Arc, + parent_hash: Hash, + parent_id: BlockId, + parent_number: BlockNumber, + random_seed: Hash, + transaction_pool: Arc>, + offline: SharedOfflineTracker, + validators: Vec, + minimum_timestamp: u64, +} + +impl Proposer { + fn primary_index(&self, round_number: usize, len: usize) -> usize { + use primitives::uint::U256; + + let big_len = U256::from(len); + let offset = U256::from_big_endian(&self.random_seed.0) % big_len; + let offset = offset.low_u64() as usize + round_number; + offset % len + } +} + +impl bft::Proposer for Proposer + where + C: Api + Send + Sync, +{ + type Create = Result; + type Error = Error; + type Evaluate = Box>; + + fn propose(&self) -> Result { + use node_api::BlockBuilder; + use runtime_primitives::traits::{Hash as HashT, BlakeTwo256}; + use node_primitives::InherentData; + + const MAX_VOTE_OFFLINE_SECONDS: Duration = Duration::from_secs(60); + + // TODO: handle case when current timestamp behind that in state. + let timestamp = ::std::cmp::max(self.minimum_timestamp, current_timestamp()); + + let elapsed_since_start = self.start.elapsed(); + let offline_indices = if elapsed_since_start > MAX_VOTE_OFFLINE_SECONDS { + Vec::new() + } else { + self.offline.read().reports(&self.validators[..]) + }; + + if !offline_indices.is_empty() { + info!( + "Submitting offline validators {:?} for slash-vote", + offline_indices.iter().map(|&i| self.validators[i as usize]).collect::>(), + ) + } + + let inherent_data = InherentData { + timestamp, + offline_indices, + }; + + let mut block_builder = self.client.build_block(&self.parent_id, inherent_data)?; + + { + let mut unqueue_invalid = Vec::new(); + let result = self.transaction_pool.cull_and_get_pending(&BlockId::hash(self.parent_hash), |pending_iterator| { + let mut pending_size = 0; + for pending in pending_iterator { + if pending_size + pending.verified.encoded_size() >= MAX_TRANSACTIONS_SIZE { break } + + match block_builder.push_extrinsic(pending.original.clone()) { + Ok(()) => { + pending_size += pending.verified.encoded_size(); + } + Err(e) => { + trace!(target: "transaction-pool", "Invalid transaction: {}", e); + unqueue_invalid.push(pending.verified.hash().clone()); + } + } + } + }); + if let Err(e) = result { + warn!("Unable to get the pending set: {:?}", e); + } + + self.transaction_pool.remove(&unqueue_invalid, false); + } + + let block = block_builder.bake()?; + + info!("Proposing block [number: {}; hash: {}; parent_hash: {}; extrinsics: [{}]]", + block.header.number, + Hash::from(block.header.hash()), + block.header.parent_hash, + block.extrinsics.iter() + .map(|xt| format!("{}", BlakeTwo256::hash_of(xt))) + .collect::>() + .join(", ") + ); + + let substrate_block = Decode::decode(&mut block.encode().as_slice()) + .expect("blocks are defined to serialize to substrate blocks correctly; qed"); + + assert!(evaluation::evaluate_initial( + &substrate_block, + timestamp, + &self.parent_hash, + self.parent_number, + ).is_ok()); + + Ok(substrate_block) + } + + fn evaluate(&self, unchecked_proposal: &Block) -> Self::Evaluate { + debug!(target: "bft", "evaluating block on top of parent ({}, {:?})", self.parent_number, self.parent_hash); + + let current_timestamp = current_timestamp(); + + // do initial serialization and structural integrity checks. + let maybe_proposal = evaluation::evaluate_initial( + unchecked_proposal, + current_timestamp, + &self.parent_hash, + self.parent_number, + ); + + let proposal = match maybe_proposal { + Ok(p) => p, + Err(e) => { + // TODO: these errors are easily re-checked in runtime. + debug!(target: "bft", "Invalid proposal: {:?}", e); + return Box::new(future::ok(false)); + } + }; + + let vote_delays = { + let now = Instant::now(); + + // the duration until the given timestamp is current + let proposed_timestamp = ::std::cmp::max(self.minimum_timestamp, proposal.timestamp()); + let timestamp_delay = if proposed_timestamp > current_timestamp { + let delay_s = proposed_timestamp - current_timestamp; + debug!(target: "bft", "Delaying evaluation of proposal for {} seconds", delay_s); + Some(now + Duration::from_secs(delay_s)) + } else { + None + }; + + match timestamp_delay { + Some(duration) => future::Either::A( + Delay::new(duration).map_err(|e| Error::from(ErrorKind::Timer(e))) + ), + None => future::Either::B(future::ok(())), + } + }; + + // refuse to vote if this block says a validator is offline that we + // think isn't. + let offline = proposal.noted_offline(); + if !self.offline.read().check_consistency(&self.validators[..], offline) { + return Box::new(futures::empty()); + } + + // evaluate whether the block is actually valid. + // TODO: is it better to delay this until the delays are finished? + let evaluated = self.client + .evaluate_block(&self.parent_id, unchecked_proposal.clone()) + .map_err(Into::into); + + let future = future::result(evaluated).and_then(move |good| { + let end_result = future::ok(good); + if good { + // delay a "good" vote. + future::Either::A(vote_delays.and_then(|_| end_result)) + } else { + // don't delay a "bad" evaluation. + future::Either::B(end_result) + } + }); + + Box::new(future) as Box<_> + } + + fn round_proposer(&self, round_number: usize, authorities: &[AuthorityId]) -> AuthorityId { + let offset = self.primary_index(round_number, authorities.len()); + let proposer = authorities[offset].clone(); + trace!(target: "bft", "proposer for round {} is {}", round_number, proposer); + + proposer + } + + fn import_misbehavior(&self, misbehavior: Vec<(AuthorityId, bft::Misbehavior)>) { + use rhododendron::Misbehavior as GenericMisbehavior; + use runtime_primitives::bft::{MisbehaviorKind, MisbehaviorReport}; + use node_primitives::UncheckedExtrinsic as GenericExtrinsic; + use node_runtime::{Call, UncheckedExtrinsic, ConsensusCall}; + + let local_id = self.local_key.public().0.into(); + let mut next_index = { + let cur_index = self.transaction_pool.cull_and_get_pending(&BlockId::hash(self.parent_hash), |pending| pending + .filter(|tx| tx.verified.sender == local_id) + .last() + .map(|tx| Ok(tx.verified.index())) + .unwrap_or_else(|| self.client.index(&self.parent_id, local_id)) + ); + + match cur_index { + Ok(Ok(cur_index)) => cur_index + 1, + Ok(Err(e)) => { + warn!(target: "consensus", "Error computing next transaction index: {}", e); + return; + } + Err(e) => { + warn!(target: "consensus", "Error computing next transaction index: {}", e); + return; + } + } + }; + + for (target, misbehavior) in misbehavior { + let report = MisbehaviorReport { + parent_hash: self.parent_hash, + parent_number: self.parent_number, + target, + misbehavior: match misbehavior { + GenericMisbehavior::ProposeOutOfTurn(_, _, _) => continue, + GenericMisbehavior::DoublePropose(_, _, _) => continue, + GenericMisbehavior::DoublePrepare(round, (h1, s1), (h2, s2)) + => MisbehaviorKind::BftDoublePrepare(round as u32, (h1, s1.signature), (h2, s2.signature)), + GenericMisbehavior::DoubleCommit(round, (h1, s1), (h2, s2)) + => MisbehaviorKind::BftDoubleCommit(round as u32, (h1, s1.signature), (h2, s2.signature)), + } + }; + let payload = (next_index, Call::Consensus(ConsensusCall::report_misbehavior(report))); + let signature = self.local_key.sign(&payload.encode()).into(); + next_index += 1; + + let local_id = self.local_key.public().0.into(); + let extrinsic = UncheckedExtrinsic { + signature: Some((node_runtime::RawAddress::Id(local_id), signature)), + index: payload.0, + function: payload.1, + }; + let uxt: GenericExtrinsic = Decode::decode(&mut extrinsic.encode().as_slice()).expect("Encoded extrinsic is valid"); + self.transaction_pool.submit_one(&BlockId::hash(self.parent_hash), uxt) + .expect("locally signed extrinsic is valid; qed"); + } + } + + fn on_round_end(&self, round_number: usize, was_proposed: bool) { + let primary_validator = self.validators[ + self.primary_index(round_number, self.validators.len()) + ]; + + + // alter the message based on whether we think the empty proposer was forced to skip the round. + // this is determined by checking if our local validator would have been forced to skip the round. + if !was_proposed { + let public = ed25519::Public::from_raw(primary_validator.0); + info!( + "Potential Offline Validator: {} failed to propose during assigned slot: {}", + public, + round_number, + ); + } + + self.offline.write().note_round_end(primary_validator, was_proposed); + } +} + +fn current_timestamp() -> Timestamp { + time::SystemTime::now().duration_since(time::UNIX_EPOCH) + .expect("now always later than unix epoch; qed") + .as_secs() +} diff --git a/node/consensus/src/offline_tracker.rs b/node/consensus/src/offline_tracker.rs new file mode 100644 index 0000000000000..18845dd68b213 --- /dev/null +++ b/node/consensus/src/offline_tracker.rs @@ -0,0 +1,137 @@ +// Copyright 2018 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 . + +//! Tracks offline validators. + +use node_primitives::AccountId; + +use std::collections::HashMap; +use std::time::{Instant, Duration}; + +// time before we report a validator. +const REPORT_TIME: Duration = Duration::from_secs(60 * 5); + +struct Observed { + last_round_end: Instant, + offline_since: Instant, +} + +impl Observed { + fn new() -> Observed { + let now = Instant::now(); + Observed { + last_round_end: now, + offline_since: now, + } + } + + fn note_round_end(&mut self, was_online: bool) { + let now = Instant::now(); + + self.last_round_end = now; + if was_online { + self.offline_since = now; + } + } + + fn is_active(&self) -> bool { + // can happen if clocks are not monotonic + if self.offline_since > self.last_round_end { return true } + self.last_round_end.duration_since(self.offline_since) < REPORT_TIME + } +} + +/// Tracks offline validators and can issue a report for those offline. +pub struct OfflineTracker { + observed: HashMap, +} + +impl OfflineTracker { + /// Create a new tracker. + pub fn new() -> Self { + OfflineTracker { observed: HashMap::new() } + } + + /// Note new consensus is starting with the given set of validators. + pub fn note_new_block(&mut self, validators: &[AccountId]) { + use std::collections::HashSet; + + let set: HashSet<_> = validators.iter().cloned().collect(); + self.observed.retain(|k, _| set.contains(k)); + } + + /// Note that a round has ended. + pub fn note_round_end(&mut self, validator: AccountId, was_online: bool) { + self.observed.entry(validator) + .or_insert_with(Observed::new) + .note_round_end(was_online); + } + + /// Generate a vector of indices for offline account IDs. + pub fn reports(&self, validators: &[AccountId]) -> Vec { + validators.iter() + .enumerate() + .filter_map(|(i, v)| if self.is_online(v) { + None + } else { + Some(i as u32) + }) + .collect() + } + + /// Whether reports on a validator set are consistent with our view of things. + pub fn check_consistency(&self, validators: &[AccountId], reports: &[u32]) -> bool { + reports.iter().cloned().all(|r| { + let v = match validators.get(r as usize) { + Some(v) => v, + None => return false, + }; + + // we must think all validators reported externally are offline. + let thinks_online = self.is_online(v); + !thinks_online + }) + } + + fn is_online(&self, v: &AccountId) -> bool { + self.observed.get(v).map(Observed::is_active).unwrap_or(true) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn validator_offline() { + let mut tracker = OfflineTracker::new(); + let v = [0; 32].into(); + let v2 = [1; 32].into(); + let v3 = [2; 32].into(); + tracker.note_round_end(v, true); + tracker.note_round_end(v2, true); + tracker.note_round_end(v3, true); + + let slash_time = REPORT_TIME + Duration::from_secs(5); + tracker.observed.get_mut(&v).unwrap().offline_since -= slash_time; + tracker.observed.get_mut(&v2).unwrap().offline_since -= slash_time; + + assert_eq!(tracker.reports(&[v, v2, v3]), vec![0, 1]); + + tracker.note_new_block(&[v, v3]); + assert_eq!(tracker.reports(&[v, v2, v3]), vec![0]); + } +} diff --git a/node/consensus/src/service.rs b/node/consensus/src/service.rs new file mode 100644 index 0000000000000..43b5980ddf1bc --- /dev/null +++ b/node/consensus/src/service.rs @@ -0,0 +1,172 @@ +// Copyright 2018 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 . + +//! Consensus service. + +/// Consensus service. A long running service that manages BFT agreement +/// the network. +use std::thread; +use std::time::{Duration, Instant}; +use std::sync::Arc; + +use bft::{self, BftService}; +use client::{BlockchainEvents, ChainHead, BlockBody}; +use primitives::ed25519; +use futures::prelude::*; +use node_api::Api; +use node_primitives::{Block, Header}; +use transaction_pool::TransactionPool; + +use tokio::executor::current_thread::TaskExecutor as LocalThreadHandle; +use tokio::runtime::TaskExecutor as ThreadPoolHandle; +use tokio::runtime::current_thread::Runtime as LocalRuntime; +use tokio::timer::Interval; + +use super::{Network, ProposerFactory}; +use error; + +const TIMER_DELAY_MS: u64 = 5000; +const TIMER_INTERVAL_MS: u64 = 500; + +// spin up an instance of BFT agreement on the current thread's executor. +// panics if there is no current thread executor. +fn start_bft( + header: Header, + bft_service: Arc>, +) where + F: bft::Environment + 'static, + C: bft::BlockImport + bft::Authorities + 'static, + F::Error: ::std::fmt::Debug, + >::Error: ::std::fmt::Display + Into, + >::Error: ::std::fmt::Display +{ + let mut handle = LocalThreadHandle::current(); + match bft_service.build_upon(&header) { + Ok(Some(bft_work)) => if let Err(e) = handle.spawn_local(Box::new(bft_work)) { + warn!(target: "bft", "Couldn't initialize BFT agreement: {:?}", e); + } + Ok(None) => trace!(target: "bft", "Could not start agreement on top of {}", header.hash()), + Err(e) => warn!(target: "bft", "BFT agreement error: {}", e), + } +} + +/// Consensus service. Starts working when created. +pub struct Service { + thread: Option>, + exit_signal: Option<::exit_future::Signal>, +} + +impl Service { + /// Create and start a new instance. + pub fn new( + client: Arc, + api: Arc, + network: N, + transaction_pool: Arc>, + thread_pool: ThreadPoolHandle, + key: ed25519::Pair, + ) -> Service + where + A: Api + Send + Sync + 'static, + C: BlockchainEvents + ChainHead + BlockBody, + C: bft::BlockImport + bft::Authorities + Send + Sync + 'static, + N: Network + Send + 'static, + { + use parking_lot::RwLock; + use super::OfflineTracker; + + let (signal, exit) = ::exit_future::signal(); + let thread = thread::spawn(move || { + let mut runtime = LocalRuntime::new().expect("Could not create local runtime"); + let key = Arc::new(key); + + let factory = ProposerFactory { + client: api.clone(), + transaction_pool: transaction_pool.clone(), + network, + handle: thread_pool.clone(), + offline: Arc::new(RwLock::new(OfflineTracker::new())), + }; + let bft_service = Arc::new(BftService::new(client.clone(), key, factory)); + + let notifications = { + let client = client.clone(); + let bft_service = bft_service.clone(); + + client.import_notification_stream().for_each(move |notification| { + if notification.is_new_best { + start_bft(notification.header, bft_service.clone()); + } + Ok(()) + }) + }; + + let interval = Interval::new( + Instant::now() + Duration::from_millis(TIMER_DELAY_MS), + Duration::from_millis(TIMER_INTERVAL_MS), + ); + + let mut prev_best = match client.best_block_header() { + Ok(header) => header.hash(), + Err(e) => { + warn!("Cant's start consensus service. Error reading best block header: {:?}", e); + return; + } + }; + + let timed = { + let c = client.clone(); + let s = bft_service.clone(); + + interval.map_err(|e| debug!(target: "bft", "Timer error: {:?}", e)).for_each(move |_| { + if let Ok(best_block) = c.best_block_header() { + let hash = best_block.hash(); + + if hash == prev_best { + debug!(target: "bft", "Starting consensus round after a timeout"); + start_bft(best_block, s.clone()); + } + prev_best = hash; + } + Ok(()) + }) + }; + + runtime.spawn(notifications); + runtime.spawn(timed); + + if let Err(e) = runtime.block_on(exit) { + debug!("BFT event loop error {:?}", e); + } + }); + Service { + thread: Some(thread), + exit_signal: Some(signal), + } + } +} + +impl Drop for Service { + fn drop(&mut self) { + if let Some(signal) = self.exit_signal.take() { + signal.fire(); + } + + if let Some(thread) = self.thread.take() { + thread.join().expect("The service thread has panicked"); + } + } +} diff --git a/node/executor/Cargo.toml b/node/executor/Cargo.toml new file mode 100644 index 0000000000000..2974a60a943a9 --- /dev/null +++ b/node/executor/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "node-executor" +version = "0.1.0" +authors = ["Parity Technologies "] +description = "Substrate node implementation in Rust." + +[dependencies] +hex-literal = "0.1" +triehash = "0.2" +substrate-codec = { path = "../../core/codec" } +substrate-runtime-io = { path = "../../core/runtime-io" } +substrate-runtime-support = { path = "../../runtime/support" } +substrate-state-machine = { path = "../../core/state-machine" } +substrate-executor = { path = "../../core/executor" } +substrate-primitives = { path = "../../core/primitives" } +node-primitives = { path = "../primitives" } +node-runtime = { path = "../runtime" } + +[dev-dependencies] +substrate-keyring = { path = "../../core/keyring" } +substrate-runtime-primitives = { path = "../../runtime/primitives" } +substrate-runtime-balances = { path = "../../runtime/balances" } +substrate-runtime-session = { path = "../../runtime/session" } +substrate-runtime-staking = { path = "../../runtime/staking" } +substrate-runtime-system = { path = "../../runtime/system" } +substrate-runtime-consensus = { path = "../../runtime/consensus" } +substrate-runtime-timestamp = { path = "../../runtime/timestamp" } +substrate-runtime-treasury = { path = "../../runtime/treasury" } diff --git a/node/executor/src/lib.rs b/node/executor/src/lib.rs new file mode 100644 index 0000000000000..9c92e42d0d828 --- /dev/null +++ b/node/executor/src/lib.rs @@ -0,0 +1,523 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! A `CodeExecutor` specialisation which uses natively compiled runtime when the wasm to be +//! executed is equivalent to the natively compiled code. + +extern crate node_runtime; +#[macro_use] extern crate substrate_executor; +extern crate substrate_codec as codec; +extern crate substrate_state_machine as state_machine; +extern crate substrate_runtime_io as runtime_io; +extern crate substrate_primitives as primitives; +extern crate node_primitives; +extern crate triehash; + +#[cfg(test)] extern crate substrate_keyring as keyring; +#[cfg(test)] extern crate substrate_runtime_primitives as runtime_primitives; +#[cfg(test)] extern crate substrate_runtime_support as runtime_support; +#[cfg(test)] extern crate substrate_runtime_balances as balances; +#[cfg(test)] extern crate substrate_runtime_session as session; +#[cfg(test)] extern crate substrate_runtime_staking as staking; +#[cfg(test)] extern crate substrate_runtime_system as system; +#[cfg(test)] extern crate substrate_runtime_consensus as consensus; +#[cfg(test)] extern crate substrate_runtime_timestamp as timestamp; +#[cfg(test)] extern crate substrate_runtime_treasury as treasury; +#[cfg(test)] #[macro_use] extern crate hex_literal; + +pub use substrate_executor::NativeExecutor; +native_executor_instance!(pub Executor, node_runtime::api::dispatch, node_runtime::VERSION, include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm")); + +#[cfg(test)] +mod tests { + use runtime_io; + use super::Executor; + use substrate_executor::{WasmExecutor, NativeExecutionDispatch}; + use codec::{Encode, Decode, Joiner}; + use keyring::Keyring; + use runtime_support::{Hashable, StorageValue, StorageMap}; + use state_machine::{CodeExecutor, TestExternalities}; + use primitives::{twox_128, Blake2Hasher, ed25519::{Public, Pair}}; + use node_primitives::{Hash, BlockNumber, AccountId}; + use runtime_primitives::traits::Header as HeaderT; + use runtime_primitives::{ApplyOutcome, ApplyError, ApplyResult}; + use {balances, staking, session, system, consensus, timestamp, treasury}; + use system::{EventRecord, Phase}; + use node_runtime::{Header, Block, UncheckedExtrinsic, CheckedExtrinsic, Call, Runtime, Balances, + BuildStorage, GenesisConfig, BalancesConfig, SessionConfig, StakingConfig, System, Event}; + + const BLOATY_CODE: &[u8] = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.wasm"); + const COMPACT_CODE: &[u8] = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm"); + + // TODO: move into own crate. + macro_rules! map { + ($( $name:expr => $value:expr ),*) => ( + vec![ $( ( $name, $value ) ),* ].into_iter().collect() + ) + } + + fn alice() -> AccountId { + AccountId::from(Keyring::Alice.to_raw_public()) + } + + fn bob() -> AccountId { + AccountId::from(Keyring::Bob.to_raw_public()) + } + + fn sign(xt: CheckedExtrinsic) -> UncheckedExtrinsic { + match xt.signed { + Some(signed) => { + let payload = (xt.index, xt.function); + let pair = Pair::from(Keyring::from_public(Public::from_raw(signed.clone().into())).unwrap()); + let signature = pair.sign(&payload.encode()).into(); + UncheckedExtrinsic { + signature: Some((balances::address::Address::Id(signed), signature)), + index: payload.0, + function: payload.1, + } + } + None => UncheckedExtrinsic { + signature: None, + index: xt.index, + function: xt.function, + }, + } + } + + fn xt() -> UncheckedExtrinsic { + sign(CheckedExtrinsic { + signed: Some(alice()), + index: 0, + function: Call::Balances(balances::Call::transfer::(bob().into(), 69)), + }) + } + + fn from_block_number(n: u64) -> Header { + Header::new(n, Default::default(), Default::default(), [69; 32].into(), Default::default()) + } + + fn executor() -> ::substrate_executor::NativeExecutor { + ::substrate_executor::NativeExecutor::new() + } + + #[test] + fn panic_execution_with_foreign_code_gives_error() { + let mut t: TestExternalities = map![ + twox_128(&>::key_for(alice())).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0], + twox_128(>::key()).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0], + twox_128(>::key()).to_vec() => vec![70u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32] + ]; + + let r = executor().call(&mut t, 8, BLOATY_CODE, "initialise_block", &vec![].and(&from_block_number(1u64)), true).0; + assert!(r.is_ok()); + let v = executor().call(&mut t, 8, BLOATY_CODE, "apply_extrinsic", &vec![].and(&xt()), true).0.unwrap(); + let r = ApplyResult::decode(&mut &v[..]).unwrap(); + assert_eq!(r, Err(ApplyError::CantPay)); + } + + #[test] + fn bad_extrinsic_with_native_equivalent_code_gives_error() { + let mut t: TestExternalities = map![ + twox_128(&>::key_for(alice())).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0], + twox_128(>::key()).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0], + twox_128(>::key()).to_vec() => vec![70u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32] + ]; + + let r = executor().call(&mut t, 8, COMPACT_CODE, "initialise_block", &vec![].and(&from_block_number(1u64)), true).0; + assert!(r.is_ok()); + let v = executor().call(&mut t, 8, COMPACT_CODE, "apply_extrinsic", &vec![].and(&xt()), true).0.unwrap(); + let r = ApplyResult::decode(&mut &v[..]).unwrap(); + assert_eq!(r, Err(ApplyError::CantPay)); + } + + #[test] + fn successful_execution_with_native_equivalent_code_gives_ok() { + let mut t: TestExternalities = map![ + twox_128(&>::key_for(alice())).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0], + twox_128(>::key()).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32] + ]; + + let r = executor().call(&mut t, 8, COMPACT_CODE, "initialise_block", &vec![].and(&from_block_number(1u64)), true).0; + assert!(r.is_ok()); + let r = executor().call(&mut t, 8, COMPACT_CODE, "apply_extrinsic", &vec![].and(&xt()), true).0; + assert!(r.is_ok()); + + runtime_io::with_externalities(&mut t, || { + assert_eq!(Balances::total_balance(&alice()), 42); + assert_eq!(Balances::total_balance(&bob()), 69); + }); + } + + #[test] + fn successful_execution_with_foreign_code_gives_ok() { + let mut t: TestExternalities = map![ + twox_128(&>::key_for(alice())).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0], + twox_128(>::key()).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32] + ]; + + let r = executor().call(&mut t, 8, BLOATY_CODE, "initialise_block", &vec![].and(&from_block_number(1u64)), true).0; + assert!(r.is_ok()); + let r = executor().call(&mut t, 8, BLOATY_CODE, "apply_extrinsic", &vec![].and(&xt()), true).0; + assert!(r.is_ok()); + + runtime_io::with_externalities(&mut t, || { + assert_eq!(Balances::total_balance(&alice()), 42); + assert_eq!(Balances::total_balance(&bob()), 69); + }); + } + + fn new_test_ext() -> TestExternalities { + use keyring::Keyring::*; + let three = [3u8; 32].into(); + GenesisConfig { + consensus: Some(Default::default()), + system: Some(Default::default()), + balances: Some(BalancesConfig { + balances: vec![(alice(), 111)], + transaction_base_fee: 1, + transaction_byte_fee: 0, + existential_deposit: 0, + transfer_fee: 0, + creation_fee: 0, + reclaim_rebate: 0, + }), + session: Some(SessionConfig { + session_length: 2, + validators: vec![One.to_raw_public().into(), Two.to_raw_public().into(), three], + }), + staking: Some(StakingConfig { + sessions_per_era: 2, + current_era: 0, + intentions: vec![alice(), bob(), Charlie.to_raw_public().into()], + validator_count: 3, + minimum_validator_count: 0, + bonding_duration: 0, + offline_slash: 0, + session_reward: 0, + offline_slash_grace: 0, + }), + democracy: Some(Default::default()), + council: Some(Default::default()), + timestamp: Some(Default::default()), + treasury: Some(Default::default()), + }.build_storage().unwrap().into() + } + + fn construct_block(number: BlockNumber, parent_hash: Hash, state_root: Hash, extrinsics: Vec) -> (Vec, Hash) { + use triehash::ordered_trie_root; + + let extrinsics = extrinsics.into_iter().map(sign).collect::>(); + let extrinsics_root = ordered_trie_root::(extrinsics.iter().map(Encode::encode)).0.into(); + + let header = Header { + parent_hash, + number, + state_root, + extrinsics_root, + digest: Default::default(), + }; + let hash = header.blake2_256(); + + (Block { header, extrinsics }.encode(), hash.into()) + } + + fn block1() -> (Vec, Hash) { + construct_block( + 1, + [69u8; 32].into(), + hex!("c2fcc528c92b3c958b0e0914f26e05f151903ed43c87f29b20f9c8f0450d7484").into(), + vec![ + CheckedExtrinsic { + signed: None, + index: 0, + function: Call::Timestamp(timestamp::Call::set(42)), + }, + CheckedExtrinsic { + signed: Some(alice()), + index: 0, + function: Call::Balances(balances::Call::transfer(bob().into(), 69)), + }, + ] + ) + } + + fn block2() -> (Vec, Hash) { + construct_block( + 2, + block1().1, + hex!("62e5879f10338fa6136161c60ae0ffc35936f7b8c3fdb38095ddd0e044309762").into(), + vec![ + CheckedExtrinsic { + signed: None, + index: 0, + function: Call::Timestamp(timestamp::Call::set(52)), + }, + CheckedExtrinsic { + signed: Some(bob()), + index: 0, + function: Call::Balances(balances::Call::transfer(alice().into(), 5)), + }, + CheckedExtrinsic { + signed: Some(alice()), + index: 1, + function: Call::Balances(balances::Call::transfer(bob().into(), 15)), + } + ] + ) + } + + fn block1big() -> (Vec, Hash) { + construct_block( + 1, + [69u8; 32].into(), + hex!("789b19bc7beaa83ae70412f65ad0ac02435fd79e0226ba3394865a052e56fbd8").into(), + vec![ + CheckedExtrinsic { + signed: None, + index: 0, + function: Call::Timestamp(timestamp::Call::set(42)), + }, + CheckedExtrinsic { + signed: Some(alice()), + index: 0, + function: Call::Consensus(consensus::Call::remark(vec![0; 120000])), + } + ] + ) + } + + #[test] + fn full_native_block_import_works() { + let mut t = new_test_ext(); + + executor().call(&mut t, 8, COMPACT_CODE, "execute_block", &block1().0, true).0.unwrap(); + + runtime_io::with_externalities(&mut t, || { + assert_eq!(Balances::total_balance(&alice()), 41); + assert_eq!(Balances::total_balance(&bob()), 69); + assert_eq!(System::events(), vec![ + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: Event::system(system::Event::ExtrinsicSuccess) + }, + EventRecord { + phase: Phase::ApplyExtrinsic(1), + event: Event::balances(balances::RawEvent::NewAccount(bob(), 1, balances::NewAccountOutcome::NoHint)) + }, + EventRecord { + phase: Phase::ApplyExtrinsic(1), + event: Event::balances(balances::RawEvent::Transfer( + hex!["d172a74cda4c865912c32ba0a80a57ae69abae410e5ccb59dee84e2f4432db4f"].into(), + hex!["d7568e5f0a7eda67a82691ff379ac4bba4f9c9b859fe779b5d46363b61ad2db9"].into(), + 69, + 0 + )) + }, + EventRecord { + phase: Phase::ApplyExtrinsic(1), + event: Event::system(system::Event::ExtrinsicSuccess) + }, + EventRecord { + phase: Phase::Finalization, + event: Event::treasury(treasury::RawEvent::Spending(0)) + }, + EventRecord { + phase: Phase::Finalization, + event: Event::treasury(treasury::RawEvent::Burnt(0)) + }, + EventRecord { + phase: Phase::Finalization, + event: Event::treasury(treasury::RawEvent::Rollover(0)) + } + ]); + }); + + executor().call(&mut t, 8, COMPACT_CODE, "execute_block", &block2().0, true).0.unwrap(); + + runtime_io::with_externalities(&mut t, || { + assert_eq!(Balances::total_balance(&alice()), 30); + assert_eq!(Balances::total_balance(&bob()), 78); + assert_eq!(System::events(), vec![ + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: Event::system(system::Event::ExtrinsicSuccess) + }, + EventRecord { + phase: Phase::ApplyExtrinsic(1), + event: Event::balances( + balances::RawEvent::Transfer( + hex!["d7568e5f0a7eda67a82691ff379ac4bba4f9c9b859fe779b5d46363b61ad2db9"].into(), + hex!["d172a74cda4c865912c32ba0a80a57ae69abae410e5ccb59dee84e2f4432db4f"].into(), + 5, + 0 + ) + ) + }, + EventRecord { + phase: Phase::ApplyExtrinsic(1), + event: Event::system(system::Event::ExtrinsicSuccess) + }, + EventRecord { + phase: Phase::ApplyExtrinsic(2), + event: Event::balances( + balances::RawEvent::Transfer( + hex!["d172a74cda4c865912c32ba0a80a57ae69abae410e5ccb59dee84e2f4432db4f"].into(), + hex!["d7568e5f0a7eda67a82691ff379ac4bba4f9c9b859fe779b5d46363b61ad2db9"].into(), + 15, + 0 + ) + ) + }, + EventRecord { + phase: Phase::ApplyExtrinsic(2), + event: Event::system(system::Event::ExtrinsicSuccess) + }, + EventRecord { + phase: Phase::Finalization, + event: Event::session(session::RawEvent::NewSession(1)) + }, + EventRecord { + phase: Phase::Finalization, + event: Event::staking(staking::RawEvent::Reward(0)) + }, + EventRecord { + phase: Phase::Finalization, + event: Event::treasury(treasury::RawEvent::Spending(0)) + }, + EventRecord { + phase: Phase::Finalization, + event: Event::treasury(treasury::RawEvent::Burnt(0)) + }, + EventRecord { + phase: Phase::Finalization, + event: Event::treasury(treasury::RawEvent::Rollover(0)) + } + ]); + }); + } + + #[test] + fn full_wasm_block_import_works() { + let mut t = new_test_ext(); + + WasmExecutor::new().call(&mut t, 8, COMPACT_CODE, "execute_block", &block1().0).unwrap(); + + runtime_io::with_externalities(&mut t, || { + assert_eq!(Balances::total_balance(&alice()), 41); + assert_eq!(Balances::total_balance(&bob()), 69); + }); + + WasmExecutor::new().call(&mut t, 8, COMPACT_CODE, "execute_block", &block2().0).unwrap(); + + runtime_io::with_externalities(&mut t, || { + assert_eq!(Balances::total_balance(&alice()), 30); + assert_eq!(Balances::total_balance(&bob()), 78); + }); + } + + #[test] + fn wasm_big_block_import_fails() { + let mut t = new_test_ext(); + + let r = WasmExecutor::new().call(&mut t, 8, COMPACT_CODE, "execute_block", &block1big().0); + assert!(!r.is_ok()); + } + + #[test] + fn native_big_block_import_succeeds() { + let mut t = new_test_ext(); + + let r = Executor::new().call(&mut t, 8, COMPACT_CODE, "execute_block", &block1big().0, true).0; + assert!(r.is_ok()); + } + + #[test] + fn native_big_block_import_fails_on_fallback() { + let mut t = new_test_ext(); + + let r = Executor::new().call(&mut t, 8, COMPACT_CODE, "execute_block", &block1big().0, false).0; + assert!(!r.is_ok()); + } + + #[test] + fn panic_execution_gives_error() { + let mut t: TestExternalities = map![ + twox_128(&>::key_for(alice())).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0], + twox_128(>::key()).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0], + twox_128(>::key()).to_vec() => vec![70u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32] + ]; + + let foreign_code = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.wasm"); + let r = WasmExecutor::new().call(&mut t, 8, &foreign_code[..], "initialise_block", &vec![].and(&from_block_number(1u64))); + assert!(r.is_ok()); + let r = WasmExecutor::new().call(&mut t, 8, &foreign_code[..], "apply_extrinsic", &vec![].and(&xt())).unwrap(); + let r = ApplyResult::decode(&mut &r[..]).unwrap(); + assert_eq!(r, Err(ApplyError::CantPay)); + } + + #[test] + fn successful_execution_gives_ok() { + let mut t: TestExternalities = map![ + twox_128(&>::key_for(alice())).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0], + twox_128(>::key()).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32] + ]; + + let foreign_code = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm"); + let r = WasmExecutor::new().call(&mut t, 8, &foreign_code[..], "initialise_block", &vec![].and(&from_block_number(1u64))); + assert!(r.is_ok()); + let r = WasmExecutor::new().call(&mut t, 8, &foreign_code[..], "apply_extrinsic", &vec![].and(&xt())).unwrap(); + let r = ApplyResult::decode(&mut &r[..]).unwrap(); + 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); + }); + } +} diff --git a/node/network/Cargo.toml b/node/network/Cargo.toml new file mode 100644 index 0000000000000..b44864ef9a90d --- /dev/null +++ b/node/network/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "node-network" +version = "0.1.0" +authors = ["Parity Technologies "] +description = "Substrate node networking protocol" + +[dependencies] +node-api = { path = "../api" } +node-consensus = { path = "../consensus" } +node-primitives = { path = "../primitives" } +substrate-primitives = { path = "../../core/primitives" } +substrate-bft = { path = "../../core/bft" } +substrate-network = { path = "../../core/network" } +futures = "0.1" +tokio = "0.1.7" +log = "0.4" +rhododendron = "0.3" diff --git a/node/network/src/consensus.rs b/node/network/src/consensus.rs new file mode 100644 index 0000000000000..6a6ab77ca67fe --- /dev/null +++ b/node/network/src/consensus.rs @@ -0,0 +1,297 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! The "consensus" networking code built on top of the base network service. +//! This fulfills the `node_consensus::Network` trait, providing a hook to be called +//! each time consensus begins on a new chain head. + +use bft; +use primitives::ed25519; +use substrate_network::{self as net, generic_message as msg}; +use substrate_network::consensus_gossip::ConsensusMessage; +use node_api::Api; +use node_consensus::Network; +use node_primitives::{Block, Hash, SessionKey}; +use rhododendron; + +use futures::prelude::*; +use futures::sync::mpsc; + +use std::sync::Arc; + +use tokio::runtime::TaskExecutor; + +use super::NetworkService; + +/// Sink for output BFT messages. +pub struct BftSink { + network: Arc, + parent_hash: Hash, + _marker: ::std::marker::PhantomData, +} + +impl Sink for BftSink { + type SinkItem = bft::Communication; + // TODO: replace this with the ! type when that's stabilized + type SinkError = E; + + fn start_send(&mut self, message: bft::Communication) + -> ::futures::StartSend, E> + { + let network_message = net::LocalizedBftMessage { + message: match message { + rhododendron::Communication::Consensus(c) => msg::BftMessage::Consensus(match c { + rhododendron::LocalizedMessage::Propose(proposal) => msg::SignedConsensusMessage::Propose(msg::SignedConsensusProposal { + round_number: proposal.round_number as u32, + proposal: proposal.proposal, + digest: proposal.digest, + sender: proposal.sender, + digest_signature: proposal.digest_signature.signature, + full_signature: proposal.full_signature.signature, + }), + rhododendron::LocalizedMessage::Vote(vote) => msg::SignedConsensusMessage::Vote(msg::SignedConsensusVote { + sender: vote.sender, + signature: vote.signature.signature, + vote: match vote.vote { + rhododendron::Vote::Prepare(r, h) => msg::ConsensusVote::Prepare(r as u32, h), + rhododendron::Vote::Commit(r, h) => msg::ConsensusVote::Commit(r as u32, h), + rhododendron::Vote::AdvanceRound(r) => msg::ConsensusVote::AdvanceRound(r as u32), + } + }), + }), + rhododendron::Communication::Auxiliary(justification) => { + let unchecked: bft::UncheckedJustification<_> = justification.uncheck().into(); + msg::BftMessage::Auxiliary(unchecked.into()) + } + }, + parent_hash: self.parent_hash, + }; + self.network.with_spec( + move |spec, ctx| spec.consensus_gossip.multicast_bft_message(ctx, network_message) + ); + Ok(::futures::AsyncSink::Ready) + } + + fn poll_complete(&mut self) -> ::futures::Poll<(), E> { + Ok(Async::Ready(())) + } +} + +// check signature and authority validity of message. +fn process_bft_message( + msg: msg::LocalizedBftMessage, + local_id: &SessionKey, + authorities: &[SessionKey] + ) -> Result>, bft::Error> +{ + Ok(Some(match msg.message { + msg::BftMessage::Consensus(c) => rhododendron::Communication::Consensus(match c { + msg::SignedConsensusMessage::Propose(proposal) => rhododendron::LocalizedMessage::Propose({ + if &proposal.sender == local_id { return Ok(None) } + let proposal = rhododendron::LocalizedProposal { + round_number: proposal.round_number as usize, + proposal: proposal.proposal, + digest: proposal.digest, + sender: proposal.sender, + digest_signature: ed25519::LocalizedSignature { + signature: proposal.digest_signature, + signer: ed25519::Public(proposal.sender.into()), + }, + full_signature: ed25519::LocalizedSignature { + signature: proposal.full_signature, + signer: ed25519::Public(proposal.sender.into()), + } + }; + bft::check_proposal(authorities, &msg.parent_hash, &proposal)?; + + trace!(target: "bft", "importing proposal message for round {} from {}", proposal.round_number, Hash::from(proposal.sender.0)); + proposal + }), + msg::SignedConsensusMessage::Vote(vote) => rhododendron::LocalizedMessage::Vote({ + if &vote.sender == local_id { return Ok(None) } + let vote = rhododendron::LocalizedVote { + sender: vote.sender, + signature: ed25519::LocalizedSignature { + signature: vote.signature, + signer: ed25519::Public(vote.sender.0), + }, + vote: match vote.vote { + msg::ConsensusVote::Prepare(r, h) => rhododendron::Vote::Prepare(r as usize, h), + msg::ConsensusVote::Commit(r, h) => rhododendron::Vote::Commit(r as usize, h), + msg::ConsensusVote::AdvanceRound(r) => rhododendron::Vote::AdvanceRound(r as usize), + } + }; + bft::check_vote::(authorities, &msg.parent_hash, &vote)?; + + trace!(target: "bft", "importing vote {:?} from {}", vote.vote, Hash::from(vote.sender.0)); + vote + }), + }), + msg::BftMessage::Auxiliary(a) => { + let justification = bft::UncheckedJustification::from(a); + // TODO: get proper error + let justification: Result<_, bft::Error> = bft::check_prepare_justification::(authorities, msg.parent_hash, justification) + .map_err(|_| bft::ErrorKind::InvalidJustification.into()); + rhododendron::Communication::Auxiliary(justification?) + }, + })) +} + +// task that processes all gossipped consensus messages, +// checking signatures +struct MessageProcessTask { + inner_stream: mpsc::UnboundedReceiver>, + bft_messages: mpsc::UnboundedSender>, + validators: Vec, + local_id: SessionKey, +} + +impl MessageProcessTask { + fn process_message(&self, msg: ConsensusMessage) -> Option> { + match msg { + ConsensusMessage::Bft(msg) => { + match process_bft_message(msg, &self.local_id, &self.validators[..]) { + Ok(Some(msg)) => { + if let Err(_) = self.bft_messages.unbounded_send(msg) { + // if the BFT receiving stream has ended then + // we should just bail. + trace!(target: "bft", "BFT message stream appears to have closed"); + return Some(Async::Ready(())); + } + } + Ok(None) => {} // ignored local message + Err(e) => { + debug!("Message validation failed: {:?}", e); + } + } + } + ConsensusMessage::ChainSpecific(_, _) => { + panic!("ChainSpecific messages are not allowed by the top level message handler"); + } + } + + None + } +} + +impl Future for MessageProcessTask { + type Item = (); + type Error = (); + + fn poll(&mut self) -> Poll<(), ()> { + loop { + match self.inner_stream.poll() { + Ok(Async::Ready(Some(val))) => if let Some(async) = self.process_message(val) { + return Ok(async); + }, + Ok(Async::Ready(None)) => return Ok(Async::Ready(())), + Ok(Async::NotReady) => return Ok(Async::NotReady), + Err(e) => { + debug!(target: "node-network", "Error getting consensus message: {:?}", e); + return Err(e); + }, + } + } + } +} + +/// Input stream from the consensus network. +pub struct InputAdapter { + input: mpsc::UnboundedReceiver>, +} + +impl Stream for InputAdapter { + type Item = bft::Communication; + type Error = ::node_consensus::Error; + + fn poll(&mut self) -> Poll, Self::Error> { + match self.input.poll() { + Err(_) | Ok(Async::Ready(None)) => Err(bft::InputStreamConcluded.into()), + Ok(x) => Ok(x) + } + } +} + +/// Wrapper around the network service +pub struct ConsensusNetwork

{ + network: Arc, + api: Arc

, +} + +impl

ConsensusNetwork

{ + /// Create a new consensus networking object. + pub fn new(network: Arc, api: Arc

) -> Self { + ConsensusNetwork { network, api } + } +} + +impl

Clone for ConsensusNetwork

{ + fn clone(&self) -> Self { + ConsensusNetwork { + network: self.network.clone(), + api: self.api.clone(), + } + } +} + +/// A long-lived network which can create parachain statement and BFT message routing processes on demand. +impl Network for ConsensusNetwork

{ + /// The input stream of BFT messages. Should never logically conclude. + type Input = InputAdapter; + /// The output sink of BFT messages. Messages sent here should eventually pass to all + /// current validators. + type Output = BftSink<::node_consensus::Error>; + + /// Get input and output streams of BFT messages. + fn communication_for( + &self, validators: &[SessionKey], + local_id: SessionKey, + parent_hash: Hash, + task_executor: TaskExecutor + ) -> (Self::Input, Self::Output) + { + let sink = BftSink { + network: self.network.clone(), + parent_hash, + _marker: Default::default(), + }; + + let (bft_send, bft_recv) = mpsc::unbounded(); + + // spin up a task in the background that processes all incoming statements + // TODO: propagate statements on a timer? + let process_task = self.network.with_spec(|spec, _ctx| { + spec.new_consensus(parent_hash); + MessageProcessTask { + inner_stream: spec.consensus_gossip.messages_for(parent_hash), + bft_messages: bft_send, + validators: validators.to_vec(), + local_id, + } + }); + + match process_task { + Some(task) => task_executor.spawn(task), + None => warn!(target: "node-network", "Cannot process incoming messages: network appears to be down"), + } + + (InputAdapter { input: bft_recv }, sink) + } +} + +/// Error when the network appears to be down. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct NetworkDown; diff --git a/node/network/src/lib.rs b/node/network/src/lib.rs new file mode 100644 index 0000000000000..5b02f4d8ff5f0 --- /dev/null +++ b/node/network/src/lib.rs @@ -0,0 +1,117 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Substrate-specific network implementation. +//! +//! This manages gossip of consensus messages for BFT. + +#![warn(unused_extern_crates)] + +extern crate substrate_primitives as primitives; +extern crate substrate_bft as bft; +extern crate substrate_network; + +extern crate node_api; +extern crate node_consensus; +extern crate node_primitives; + +extern crate futures; +extern crate tokio; +extern crate rhododendron; + +#[macro_use] +extern crate log; + +pub mod consensus; + +use node_primitives::{Block, Hash, Header}; +use substrate_network::{NodeIndex, Context, Severity}; +use substrate_network::consensus_gossip::ConsensusGossip; +use substrate_network::{message, generic_message}; +use substrate_network::specialization::Specialization; +use substrate_network::StatusMessage as GenericFullStatus; + +/// Demo protocol id. +pub const PROTOCOL_ID: ::substrate_network::ProtocolId = *b"dot"; + +type FullStatus = GenericFullStatus; + +/// Specialization of the network service for the node protocol. +pub type NetworkService = ::substrate_network::Service; + + +/// Demo protocol attachment for substrate. +pub struct Protocol { + consensus_gossip: ConsensusGossip, + live_consensus: Option, +} + +impl Protocol { + /// Instantiate a node protocol handler. + pub fn new() -> Self { + Protocol { + consensus_gossip: ConsensusGossip::new(), + live_consensus: None, + } + } + + /// Note new consensus session. + fn new_consensus(&mut self, parent_hash: Hash) { + let old_consensus = self.live_consensus.take(); + self.live_consensus = Some(parent_hash); + self.consensus_gossip.collect_garbage(old_consensus.as_ref()); + } +} + +impl Specialization for Protocol { + fn status(&self) -> Vec { + Vec::new() + } + + fn on_connect(&mut self, ctx: &mut Context, who: NodeIndex, status: FullStatus) { + self.consensus_gossip.new_peer(ctx, who, status.roles); + } + + fn on_disconnect(&mut self, ctx: &mut Context, who: NodeIndex) { + self.consensus_gossip.peer_disconnected(ctx, who); + } + + fn on_message(&mut self, ctx: &mut Context, who: NodeIndex, message: message::Message) { + match message { + generic_message::Message::BftMessage(msg) => { + trace!(target: "node-network", "BFT message from {}: {:?}", who, msg); + // TODO: check signature here? what if relevant block is unknown? + self.consensus_gossip.on_bft_message(ctx, who, msg) + } + generic_message::Message::ChainSpecific(_) => { + trace!(target: "node-network", "Bad message from {}", who); + ctx.report_peer(who, Severity::Bad("Invalid node protocol message format")); + } + _ => {} + } + } + + fn on_abort(&mut self) { + self.consensus_gossip.abort(); + } + + fn maintain_peers(&mut self, _ctx: &mut Context) { + } + + fn on_block_imported(&mut self, _ctx: &mut Context, _hash: Hash, _header: &Header) { + } +} + diff --git a/node/primitives/Cargo.toml b/node/primitives/Cargo.toml new file mode 100644 index 0000000000000..f6a74b058fa74 --- /dev/null +++ b/node/primitives/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "node-primitives" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +serde = { version = "1.0", default_features = false } +serde_derive = { version = "1.0", optional = true } +substrate-codec = { path = "../../core/codec", default_features = false } +substrate-codec-derive = { path = "../../core/codec/derive", default_features = false } +substrate-primitives = { path = "../../core/primitives", default_features = false } +substrate-runtime-std = { path = "../../core/runtime-std", default_features = false } +substrate-runtime-primitives = { path = "../../runtime/primitives", default_features = false } + +[dev-dependencies] +substrate-serializer = { path = "../../core/serializer" } +pretty_assertions = "0.4" + +[features] +default = ["std"] +std = [ + "substrate-codec-derive/std", + "substrate-codec/std", + "substrate-primitives/std", + "substrate-runtime-std/std", + "substrate-runtime-primitives/std", + "serde_derive", + "serde/std", +] diff --git a/node/primitives/src/lib.rs b/node/primitives/src/lib.rs new file mode 100644 index 0000000000000..433412eaf3f77 --- /dev/null +++ b/node/primitives/src/lib.rs @@ -0,0 +1,94 @@ +// Copyright 2018 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 . + +//! Low-level types used throughout the Substrate code. + +#![warn(missing_docs)] + +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(not(feature = "std"), feature(alloc))] + +#[cfg(feature = "std")] +extern crate serde; + +#[cfg(feature = "std")] +#[macro_use] +extern crate serde_derive; + +#[macro_use] +extern crate substrate_codec_derive; + +extern crate substrate_runtime_std as rstd; +extern crate substrate_runtime_primitives as runtime_primitives; +extern crate substrate_primitives as primitives; +extern crate substrate_codec as codec; + +use rstd::prelude::*; +use runtime_primitives::generic; +#[cfg(feature = "std")] +use primitives::bytes; +use runtime_primitives::traits::BlakeTwo256; + +/// An index to a block. +pub type BlockNumber = u64; + +/// Alias to Ed25519 pubkey that identifies an account on the chain. This will almost +/// certainly continue to be the same as the substrate's `AuthorityId`. +pub type AccountId = ::primitives::H256; + +/// The type for looking up accounts. We don't expect more than 4 billion of them, but you +/// never know... +pub type AccountIndex = u32; + +/// Balance of an account. +pub type Balance = u64; + +/// The Ed25519 pub key of an session that belongs to an authority of the chain. This is +/// exactly equivalent to what the substrate calls an "authority". +pub type SessionKey = primitives::AuthorityId; + +/// Index of a transaction in the chain. +pub type Index = u64; + +/// A hash of some data used by the chain. +pub type Hash = primitives::H256; + +/// Alias to 512-bit hash when used in the context of a signature on the chain. +pub type Signature = runtime_primitives::Ed25519Signature; + +/// A timestamp: seconds since the unix epoch. +pub type Timestamp = u64; + +/// Header type. +pub type Header = generic::Header>; +/// Block type. +pub type Block = generic::Block; +/// Block ID. +pub type BlockId = generic::BlockId; + +/// Opaque, encoded, unchecked extrinsic. +#[derive(PartialEq, Eq, Clone, Default, Encode, Decode)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] +pub struct UncheckedExtrinsic(#[cfg_attr(feature = "std", serde(with="bytes"))] pub Vec); + +/// Inherent data to include in a block. +#[derive(Encode, Decode)] +pub struct InherentData { + /// Current timestamp. + pub timestamp: Timestamp, + /// Indices of offline validators. + pub offline_indices: Vec, +} diff --git a/node/runtime/Cargo.toml b/node/runtime/Cargo.toml new file mode 100644 index 0000000000000..31db56e360ff6 --- /dev/null +++ b/node/runtime/Cargo.toml @@ -0,0 +1,61 @@ +[package] +name = "node-runtime" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +rustc-hex = "1.0" +hex-literal = "0.1.0" +log = { version = "0.3", optional = true } +serde = { version = "1.0", default_features = false } +serde_derive = { version = "1.0", optional = true } +safe-mix = { version = "1.0", default_features = false} +substrate-codec = { path = "../../core/codec" } +substrate-codec-derive = { path = "../../core/codec/derive" } +substrate-runtime-std = { path = "../../core/runtime-std" } +substrate-runtime-io = { path = "../../core/runtime-io" } +substrate-primitives = { path = "../../core/primitives" } +substrate-keyring = { path = "../../core/keyring" } +substrate-runtime-support = { path = "../../runtime/support" } +substrate-runtime-balances = { path = "../../runtime/balances" } +substrate-runtime-consensus = { path = "../../runtime/consensus" } +substrate-runtime-contract = { path = "../../runtime/contract" } +substrate-runtime-council = { path = "../../runtime/council" } +substrate-runtime-democracy = { path = "../../runtime/democracy" } +substrate-runtime-executive = { path = "../../runtime/executive" } +substrate-runtime-primitives = { path = "../../runtime/primitives" } +substrate-runtime-session = { path = "../../runtime/session" } +substrate-runtime-staking = { path = "../../runtime/staking" } +substrate-runtime-system = { path = "../../runtime/system" } +substrate-runtime-timestamp = { path = "../../runtime/timestamp" } +substrate-runtime-treasury = { path = "../../runtime/treasury" } +substrate-runtime-version = { path = "../../runtime/version" } +node-primitives = { path = "../primitives" } + +[features] +default = ["std"] +std = [ + "substrate-codec/std", + "substrate-primitives/std", + "substrate-runtime-std/std", + "substrate-runtime-io/std", + "substrate-runtime-support/std", + "substrate-runtime-balances/std", + "substrate-runtime-consensus/std", + "substrate-runtime-contract/std", + "substrate-runtime-council/std", + "substrate-runtime-democracy/std", + "substrate-runtime-executive/std", + "substrate-runtime-primitives/std", + "substrate-runtime-session/std", + "substrate-runtime-staking/std", + "substrate-runtime-system/std", + "substrate-runtime-timestamp/std", + "substrate-runtime-treasury/std", + "substrate-runtime-version/std", + "node-primitives/std", + "serde_derive", + "serde/std", + "log", + "safe-mix/std" +] diff --git a/node/runtime/src/checked_block.rs b/node/runtime/src/checked_block.rs new file mode 100644 index 0000000000000..bba748dd15fd4 --- /dev/null +++ b/node/runtime/src/checked_block.rs @@ -0,0 +1,94 @@ +// Copyright 2018 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 . + +//! Typesafe block interaction. + +use super::{Call, Block, TIMESTAMP_SET_POSITION, NOTE_OFFLINE_POSITION}; +use timestamp::Call as TimestampCall; +use consensus::Call as ConsensusCall; + +/// Provides a type-safe wrapper around a structurally valid block. +pub struct CheckedBlock { + inner: Block, + file_line: Option<(&'static str, u32)>, +} + +impl CheckedBlock { + /// Create a new checked block. Fails if the block is not structurally valid. + pub fn new(block: Block) -> Result { + let has_timestamp = block.extrinsics.get(TIMESTAMP_SET_POSITION as usize).map_or(false, |xt| { + !xt.is_signed() && match xt.function { + Call::Timestamp(TimestampCall::set(_)) => true, + _ => false, + } + }); + + if !has_timestamp { return Err(block) } + + Ok(CheckedBlock { + inner: block, + file_line: None, + }) + } + + // Creates a new checked block, asserting that it is valid. + #[doc(hidden)] + pub fn new_unchecked(block: Block, file: &'static str, line: u32) -> Self { + CheckedBlock { + inner: block, + file_line: Some((file, line)), + } + } + + /// Extract the timestamp from the block. + pub fn timestamp(&self) -> ::node_primitives::Timestamp { + let x = self.inner.extrinsics.get(TIMESTAMP_SET_POSITION as usize).and_then(|xt| match xt.function { + Call::Timestamp(TimestampCall::set(x)) => Some(x), + _ => None + }); + + match x { + Some(x) => x, + None => panic!("Invalid block asserted at {:?}", self.file_line), + } + } + + /// Extract the noted missed proposal validator indices (if any) from the block. + pub fn noted_offline(&self) -> &[u32] { + self.inner.extrinsics.get(NOTE_OFFLINE_POSITION as usize).and_then(|xt| match xt.function { + Call::Consensus(ConsensusCall::note_offline(ref x)) => Some(&x[..]), + _ => None, + }).unwrap_or(&[]) + } + + /// Convert into inner block. + pub fn into_inner(self) -> Block { self.inner } +} + +impl ::std::ops::Deref for CheckedBlock { + type Target = Block; + + fn deref(&self) -> &Block { &self.inner } +} + +/// Assert that a block is structurally valid. May lead to panic in the future +/// in case it isn't. +#[macro_export] +macro_rules! assert_node_block { + ($block: expr) => { + $crate::CheckedBlock::new_unchecked($block, file!(), line!()) + } +} diff --git a/node/runtime/src/lib.rs b/node/runtime/src/lib.rs new file mode 100644 index 0000000000000..948507d5b2e07 --- /dev/null +++ b/node/runtime/src/lib.rs @@ -0,0 +1,386 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! The Substrate runtime. This can be compiled with ``#[no_std]`, ready for Wasm. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[macro_use] +extern crate substrate_runtime_io as runtime_io; + +#[macro_use] +extern crate substrate_runtime_support; + +#[macro_use] +extern crate substrate_runtime_primitives as runtime_primitives; + +#[cfg(feature = "std")] +#[macro_use] +extern crate serde_derive; + +#[cfg(feature = "std")] +extern crate serde; + +extern crate substrate_codec as codec; +extern crate substrate_primitives; + +#[macro_use] +extern crate substrate_codec_derive; + +#[cfg_attr(not(feature = "std"), macro_use)] +extern crate substrate_runtime_std as rstd; +extern crate substrate_runtime_balances as balances; +extern crate substrate_runtime_consensus as consensus; +extern crate substrate_runtime_contract as contract; +extern crate substrate_runtime_council as council; +extern crate substrate_runtime_democracy as democracy; +extern crate substrate_runtime_executive as executive; +extern crate substrate_runtime_session as session; +extern crate substrate_runtime_staking as staking; +extern crate substrate_runtime_system as system; +extern crate substrate_runtime_timestamp as timestamp; +extern crate substrate_runtime_treasury as treasury; +#[macro_use] +extern crate substrate_runtime_version as version; +extern crate node_primitives; + +#[cfg(feature = "std")] +mod checked_block; + +use rstd::prelude::*; +use substrate_primitives::u32_trait::{_2, _4}; +use codec::{Encode, Decode, Input}; +use node_primitives::{AccountId, AccountIndex, Balance, BlockNumber, Hash, Index, SessionKey, Signature, InherentData}; +use runtime_primitives::generic; +use runtime_primitives::traits::{Convert, BlakeTwo256, DigestItem}; +use version::RuntimeVersion; +use council::{motions as council_motions, voting as council_voting}; + +#[cfg(any(feature = "std", test))] +pub use runtime_primitives::BuildStorage; +pub use consensus::Call as ConsensusCall; +pub use timestamp::Call as TimestampCall; +pub use runtime_primitives::Permill; +#[cfg(any(feature = "std", test))] +pub use checked_block::CheckedBlock; + +const TIMESTAMP_SET_POSITION: u32 = 0; +const NOTE_OFFLINE_POSITION: u32 = 1; + +// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. +#[derive(Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] +/// Runtime type used to collate and parameterize the various modules. +pub struct Runtime; + +/// Runtime version. +pub const VERSION: RuntimeVersion = RuntimeVersion { + spec_name: ver_str!("node"), + impl_name: ver_str!("substrate-node"), + authoring_version: 1, + spec_version: 1, + impl_version: 0, +}; + +impl system::Trait for Runtime { + type Origin = Origin; + type Index = Index; + type BlockNumber = BlockNumber; + type Hash = Hash; + type Hashing = BlakeTwo256; + type Digest = generic::Digest; + type AccountId = AccountId; + type Header = generic::Header; + type Event = Event; +} + +/// System module for this concrete runtime. +pub type System = system::Module; + +impl balances::Trait for Runtime { + type Balance = Balance; + type AccountIndex = AccountIndex; + type OnFreeBalanceZero = (Staking, Contract); + type EnsureAccountLiquid = Staking; + type Event = Event; +} + +/// Staking module for this concrete runtime. +pub type Balances = balances::Module; + +impl consensus::Trait for Runtime { + const NOTE_OFFLINE_POSITION: u32 = NOTE_OFFLINE_POSITION; + type Log = Log; + type SessionKey = SessionKey; + type OnOfflineValidator = Staking; +} + +/// Consensus module for this concrete runtime. +pub type Consensus = consensus::Module; + +impl timestamp::Trait for Runtime { + const TIMESTAMP_SET_POSITION: u32 = TIMESTAMP_SET_POSITION; + type Moment = u64; +} + +/// Timestamp module for this concrete runtime. +pub type Timestamp = timestamp::Module; + +/// Session key conversion. +pub struct SessionKeyConversion; +impl Convert for SessionKeyConversion { + fn convert(a: AccountId) -> SessionKey { + a.0.into() + } +} + +impl session::Trait for Runtime { + type ConvertAccountIdToSessionKey = SessionKeyConversion; + type OnSessionChange = Staking; + type Event = Event; +} + +/// Session module for this concrete runtime. +pub type Session = session::Module; + +impl staking::Trait for Runtime { + type OnRewardMinted = Treasury; + type Event = Event; +} + +/// Staking module for this concrete runtime. +pub type Staking = staking::Module; + +impl democracy::Trait for Runtime { + type Proposal = Call; + type Event = Event; +} + +/// Democracy module for this concrete runtime. +pub type Democracy = democracy::Module; + +impl council::Trait for Runtime { + type Event = Event; +} + +/// Council module for this concrete runtime. +pub type Council = council::Module; + +impl council::voting::Trait for Runtime { + type Event = Event; +} + +/// Council voting module for this concrete runtime. +pub type CouncilVoting = council::voting::Module; + +impl council::motions::Trait for Runtime { + type Origin = Origin; + type Proposal = Call; + type Event = Event; +} + +/// Council motions module for this concrete runtime. +pub type CouncilMotions = council_motions::Module; + +impl treasury::Trait for Runtime { + type ApproveOrigin = council_motions::EnsureMembers<_4>; + type RejectOrigin = council_motions::EnsureMembers<_2>; + type Event = Event; +} + +/// Treasury module for this concrete runtime. +pub type Treasury = treasury::Module; + +/// Address calculated from the code (of the constructor), input data to the constructor +/// and account id which requested the account creation. +/// +/// Formula: `blake2_256(blake2_256(code) + blake2_256(data) + origin)` +pub struct DetermineContractAddress; +impl contract::ContractAddressFor for DetermineContractAddress { + fn contract_address_for(code: &[u8], data: &[u8], origin: &AccountId) -> AccountId { + use runtime_primitives::traits::Hash; + + let code_hash = BlakeTwo256::hash(code); + let data_hash = BlakeTwo256::hash(data); + let mut buf = [0u8, 32 + 32 + 32]; + &mut buf[0..32].copy_from_slice(&code_hash); + &mut buf[32..64].copy_from_slice(&data_hash); + &mut buf[64..96].copy_from_slice(origin); + AccountId::from(BlakeTwo256::hash(&buf[..])) + } +} + +impl contract::Trait for Runtime { + type Gas = u64; + type DetermineContractAddress = DetermineContractAddress; +} + +/// Contract module for this concrete runtime. +pub type Contract = contract::Module; + +impl_outer_event! { + pub enum Event for Runtime { + //consensus, + balances, + //timetstamp, + session, + staking, + democracy, + council, + council_voting, + council_motions, + treasury + } +} + +impl_outer_log! { + pub enum Log(InternalLog: DigestItem) for Runtime { + consensus(AuthoritiesChange) + } +} + +impl_outer_origin! { + pub enum Origin for Runtime { + council_motions + } +} + +impl_outer_dispatch! { + pub enum Call where origin: Origin { + Consensus, + Balances, + Timestamp, + Session, + Staking, + Democracy, + Council, + CouncilVoting, + CouncilMotions, + Treasury, + Contract, + } +} + +impl_outer_config! { + pub struct GenesisConfig for Runtime { + SystemConfig => system, + ConsensusConfig => consensus, + BalancesConfig => balances, + TimestampConfig => timestamp, + SessionConfig => session, + StakingConfig => staking, + DemocracyConfig => democracy, + CouncilConfig => council, + TreasuryConfig => treasury, + } +} + +type AllModules = ( + Consensus, + Balances, + Timestamp, + Session, + Staking, + Democracy, + Council, + CouncilVoting, + CouncilMotions, + Treasury, + Contract, +); + +impl_json_metadata!( + for Runtime with modules + system::Module with Storage, + consensus::Module with Storage, + balances::Module with Storage, + timestamp::Module with Storage, + session::Module with Storage, + staking::Module with Storage, + democracy::Module with Storage, + council::Module with Storage, + council_voting::Module with Storage, + council_motions::Module with Storage, + treasury::Module with Storage, + contract::Module with Storage, +); + +impl DigestItem for Log { + type AuthorityId = SessionKey; + + fn as_authorities_change(&self) -> Option<&[Self::AuthorityId]> { + match self.0 { + InternalLog::consensus(ref item) => item.as_authorities_change(), + } + } +} + +/// The address format for describing accounts. +pub use balances::address::Address as RawAddress; +/// The address format for describing accounts. +pub type Address = balances::Address; +/// Block header type as expected by this runtime. +pub type Header = generic::Header; +/// Block type as expected by this runtime. +pub type Block = generic::Block; +/// BlockId type as expected by this runtime. +pub type BlockId = generic::BlockId; +/// Unchecked extrinsic type as expected by this runtime. +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; +/// Extrinsic type that has already been checked. +pub type CheckedExtrinsic = generic::CheckedExtrinsic; +/// Executive: handles dispatch to the various modules. +pub type Executive = executive::Executive; + +pub mod api { + impl_stubs!( + version => |()| super::VERSION, + json_metadata => |()| super::Runtime::json_metadata(), + authorities => |()| super::Consensus::authorities(), + initialise_block => |header| super::Executive::initialise_block(&header), + apply_extrinsic => |extrinsic| super::Executive::apply_extrinsic(extrinsic), + execute_block => |block| super::Executive::execute_block(block), + finalise_block => |()| super::Executive::finalise_block(), + inherent_extrinsics => |(inherent, spec_version)| super::inherent_extrinsics(inherent, spec_version), + validator_count => |()| super::Session::validator_count(), + validators => |()| super::Session::validators(), + timestamp => |()| super::Timestamp::get(), + random_seed => |()| super::System::random_seed(), + account_nonce => |account| super::System::account_nonce(&account), + lookup_address => |address| super::Balances::lookup_address(address) + ); +} + +/// Produces the list of inherent extrinsics. +fn inherent_extrinsics(data: InherentData, _spec_version: u32) -> Vec { + let make_inherent = |function| UncheckedExtrinsic { + signature: Default::default(), + function, + index: 0, + }; + + let mut inherent = vec![ + make_inherent(Call::Timestamp(TimestampCall::set(data.timestamp))), + ]; + + if !data.offline_indices.is_empty() { + inherent.push(make_inherent( + Call::Consensus(ConsensusCall::note_offline(data.offline_indices)) + )); + } + + inherent +} diff --git a/node/runtime/wasm/Cargo.lock b/node/runtime/wasm/Cargo.lock new file mode 100644 index 0000000000000..78ff2c0cedd66 --- /dev/null +++ b/node/runtime/wasm/Cargo.lock @@ -0,0 +1,568 @@ +[[package]] +name = "arrayvec" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "byteorder" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cfg-if" +version = "0.1.3" +source = "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 = "fixed-hash" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "hashdb" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "hex-literal" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "hex-literal-impl 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hex-literal-impl" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "integer-sqrt" +version = "0.1.0" +source = "git+https://github.com/paritytech/integer-sqrt-rs.git#886e9cb983c46498003878afe965d55caa762025" + +[[package]] +name = "log" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "node-primitives" +version = "0.1.0" +dependencies = [ + "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-codec-derive 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-primitives 0.1.0", + "substrate-runtime-std 0.1.0", +] + +[[package]] +name = "node-runtime" +version = "0.1.0" +dependencies = [ + "integer-sqrt 0.1.0 (git+https://github.com/paritytech/integer-sqrt-rs.git)", + "node-primitives 0.1.0", + "safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-codec-derive 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-balances 0.1.0", + "substrate-runtime-consensus 0.1.0", + "substrate-runtime-contract 0.1.0", + "substrate-runtime-council 0.1.0", + "substrate-runtime-democracy 0.1.0", + "substrate-runtime-executive 0.1.0", + "substrate-runtime-io 0.1.0", + "substrate-runtime-primitives 0.1.0", + "substrate-runtime-session 0.1.0", + "substrate-runtime-staking 0.1.0", + "substrate-runtime-std 0.1.0", + "substrate-runtime-support 0.1.0", + "substrate-runtime-system 0.1.0", + "substrate-runtime-timestamp 0.1.0", + "substrate-runtime-treasury 0.1.0", + "substrate-runtime-version 0.1.0", +] + +[[package]] +name = "nodrop" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "num-traits" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "parity-wasm" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "plain_hasher" +version = "0.2.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)", +] + +[[package]] +name = "proc-macro-hack" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro-hack-impl 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro-hack-impl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "proc-macro2" +version = "0.4.4" +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 = "pwasm-alloc" +version = "0.1.0" +dependencies = [ + "pwasm-libc 0.1.0", + "rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pwasm-libc" +version = "0.1.0" + +[[package]] +name = "pwasm-utils" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-wasm 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc-hex" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc_version" +version = "0.2.2" +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 = "safe-mix" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.2.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 = "serde" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "substrate-codec" +version = "0.1.0" +dependencies = [ + "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "substrate-codec-derive" +version = "0.1.0" +dependencies = [ + "proc-macro2 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "substrate-primitives" +version = "0.1.0" +dependencies = [ + "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "fixed-hash 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hashdb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "plain_hasher 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hex 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-codec-derive 0.1.0", + "substrate-runtime-std 0.1.0", + "uint 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "substrate-runtime-balances" +version = "0.1.0" +dependencies = [ + "hex-literal 0.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.64 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-codec-derive 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-io 0.1.0", + "substrate-runtime-primitives 0.1.0", + "substrate-runtime-std 0.1.0", + "substrate-runtime-support 0.1.0", + "substrate-runtime-system 0.1.0", +] + +[[package]] +name = "substrate-runtime-consensus" +version = "0.1.0" +dependencies = [ + "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-codec-derive 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-io 0.1.0", + "substrate-runtime-primitives 0.1.0", + "substrate-runtime-std 0.1.0", + "substrate-runtime-support 0.1.0", + "substrate-runtime-system 0.1.0", +] + +[[package]] +name = "substrate-runtime-contract" +version = "0.1.0" +dependencies = [ + "parity-wasm 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pwasm-utils 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-balances 0.1.0", + "substrate-runtime-io 0.1.0", + "substrate-runtime-primitives 0.1.0", + "substrate-runtime-sandbox 0.1.0", + "substrate-runtime-std 0.1.0", + "substrate-runtime-support 0.1.0", + "substrate-runtime-system 0.1.0", +] + +[[package]] +name = "substrate-runtime-council" +version = "0.1.0" +dependencies = [ + "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "integer-sqrt 0.1.0 (git+https://github.com/paritytech/integer-sqrt-rs.git)", + "safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-codec-derive 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-balances 0.1.0", + "substrate-runtime-consensus 0.1.0", + "substrate-runtime-democracy 0.1.0", + "substrate-runtime-io 0.1.0", + "substrate-runtime-primitives 0.1.0", + "substrate-runtime-std 0.1.0", + "substrate-runtime-support 0.1.0", + "substrate-runtime-system 0.1.0", +] + +[[package]] +name = "substrate-runtime-democracy" +version = "0.1.0" +dependencies = [ + "hex-literal 0.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.64 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-codec-derive 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-balances 0.1.0", + "substrate-runtime-consensus 0.1.0", + "substrate-runtime-io 0.1.0", + "substrate-runtime-primitives 0.1.0", + "substrate-runtime-std 0.1.0", + "substrate-runtime-support 0.1.0", + "substrate-runtime-system 0.1.0", +] + +[[package]] +name = "substrate-runtime-executive" +version = "0.1.0" +dependencies = [ + "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-runtime-io 0.1.0", + "substrate-runtime-primitives 0.1.0", + "substrate-runtime-std 0.1.0", + "substrate-runtime-support 0.1.0", + "substrate-runtime-system 0.1.0", +] + +[[package]] +name = "substrate-runtime-io" +version = "0.1.0" +dependencies = [ + "hashdb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-std 0.1.0", +] + +[[package]] +name = "substrate-runtime-primitives" +version = "0.1.0" +dependencies = [ + "integer-sqrt 0.1.0 (git+https://github.com/paritytech/integer-sqrt-rs.git)", + "num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-codec-derive 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-io 0.1.0", + "substrate-runtime-std 0.1.0", + "substrate-runtime-support 0.1.0", +] + +[[package]] +name = "substrate-runtime-sandbox" +version = "0.1.0" +dependencies = [ + "rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-io 0.1.0", + "substrate-runtime-std 0.1.0", +] + +[[package]] +name = "substrate-runtime-session" +version = "0.1.0" +dependencies = [ + "hex-literal 0.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.64 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-codec-derive 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-consensus 0.1.0", + "substrate-runtime-io 0.1.0", + "substrate-runtime-primitives 0.1.0", + "substrate-runtime-std 0.1.0", + "substrate-runtime-support 0.1.0", + "substrate-runtime-system 0.1.0", + "substrate-runtime-timestamp 0.1.0", +] + +[[package]] +name = "substrate-runtime-staking" +version = "0.1.0" +dependencies = [ + "hex-literal 0.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.64 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-codec-derive 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-balances 0.1.0", + "substrate-runtime-consensus 0.1.0", + "substrate-runtime-io 0.1.0", + "substrate-runtime-primitives 0.1.0", + "substrate-runtime-sandbox 0.1.0", + "substrate-runtime-session 0.1.0", + "substrate-runtime-std 0.1.0", + "substrate-runtime-support 0.1.0", + "substrate-runtime-system 0.1.0", + "substrate-runtime-timestamp 0.1.0", +] + +[[package]] +name = "substrate-runtime-std" +version = "0.1.0" +dependencies = [ + "pwasm-alloc 0.1.0", + "pwasm-libc 0.1.0", + "rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "substrate-runtime-support" +version = "0.1.0" +dependencies = [ + "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-io 0.1.0", + "substrate-runtime-std 0.1.0", +] + +[[package]] +name = "substrate-runtime-system" +version = "0.1.0" +dependencies = [ + "hex-literal 0.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.64 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-codec-derive 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-io 0.1.0", + "substrate-runtime-primitives 0.1.0", + "substrate-runtime-std 0.1.0", + "substrate-runtime-support 0.1.0", +] + +[[package]] +name = "substrate-runtime-timestamp" +version = "0.1.0" +dependencies = [ + "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-consensus 0.1.0", + "substrate-runtime-io 0.1.0", + "substrate-runtime-primitives 0.1.0", + "substrate-runtime-std 0.1.0", + "substrate-runtime-support 0.1.0", + "substrate-runtime-system 0.1.0", +] + +[[package]] +name = "substrate-runtime-treasury" +version = "0.1.0" +dependencies = [ + "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-codec-derive 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-balances 0.1.0", + "substrate-runtime-io 0.1.0", + "substrate-runtime-primitives 0.1.0", + "substrate-runtime-std 0.1.0", + "substrate-runtime-support 0.1.0", + "substrate-runtime-system 0.1.0", +] + +[[package]] +name = "substrate-runtime-version" +version = "0.1.0" +dependencies = [ + "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-codec-derive 0.1.0", + "substrate-runtime-std 0.1.0", + "substrate-runtime-support 0.1.0", +] + +[[package]] +name = "syn" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.3 (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 = "uint" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hex 2.0.0 (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.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" +"checksum byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "74c0b906e9446b0a2e4f760cdb3fa4b2c48cdc6db8766a845c54b6ff063fd2e9" +"checksum cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "405216fd8fe65f718daa7102ea808a946b6ce40c742998fbfd3463645552de18" +"checksum crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" +"checksum fixed-hash 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0d5ec8112f00ea8a483e04748a85522184418fd1cf02890b626d8fc28683f7de" +"checksum hashdb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f1c71fc577cde89b3345d5f2880fecaf462a32e96c619f431279bdaf1ba5ddb1" +"checksum hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4da5f0e01bd8a71a224a4eedecaacfcabda388dbb7a80faf04d3514287572d95" +"checksum hex-literal-impl 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1d340b6514f232f6db1bd16db65302a5278a04fef9ce867cb932e7e5fa21130a" +"checksum integer-sqrt 0.1.0 (git+https://github.com/paritytech/integer-sqrt-rs.git)" = "" +"checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2" +"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" +"checksum num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "775393e285254d2f5004596d69bb8bc1149754570dcc08cf30cabeba67955e28" +"checksum parity-wasm 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1c91199d14bd5b78ecade323d4a891d094799749c1b9e82d9c590c2e2849a40" +"checksum plain_hasher 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "95fa6386b1d34aaf0adb9b7dd2885dbe7c34190e6263785e5a7ec2b19044a90f" +"checksum proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ba8d4f9257b85eb6cdf13f055cea3190520aab1409ca2ab43493ea4820c25f0" +"checksum proc-macro-hack-impl 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d5cb6f960ad471404618e9817c0e5d10b1ae74cfdf01fab89ea0641fe7fb2892" +"checksum proc-macro2 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1fa93823f53cfd0f5ac117b189aed6cfdfb2cfc0a9d82e956dd7927595ed7d46" +"checksum pwasm-utils 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "efd695333cfae6e9dbe2703a6d040e252b57a6fc3b9a65c712615ac042b2e0c5" +"checksum quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e44651a0dc4cdd99f71c83b561e221f714912d11af1a4dff0631f923d53af035" +"checksum rustc-hex 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d2b03280c2813907a030785570c577fb27d3deec8da4c18566751ade94de0ace" +"checksum rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a54aa04a10c68c1c4eacb4337fd883b435997ede17a9385784b990777686b09a" +"checksum safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7f7bf422d23a88c16d5090d455f182bc99c60af4df6a345c63428acf5129e347" +"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.64 (registry+https://github.com/rust-lang/crates.io-index)" = "fba5be06346c5200249c8c8ca4ccba4a09e8747c71c16e420bd359a0db4d8f91" +"checksum syn 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6dfd71b2be5a58ee30a6f8ea355ba8290d397131c00dfa55c3d34e6e13db5101" +"checksum uint 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "754ba11732b9161b94c41798e5197e5e75388d012f760c42adb5000353e98646" +"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" diff --git a/node/runtime/wasm/Cargo.toml b/node/runtime/wasm/Cargo.toml new file mode 100644 index 0000000000000..db62ba1d175c8 --- /dev/null +++ b/node/runtime/wasm/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "node-runtime" +version = "0.1.0" +authors = ["Parity Technologies "] + +[lib] +crate-type = ["cdylib"] + +[dependencies] +integer-sqrt = { git = "https://github.com/paritytech/integer-sqrt-rs.git", branch = "master" } +safe-mix = { version = "1.0", default_features = false} +node-primitives = { path = "../../primitives", default-features = false } +substrate-codec-derive = { path = "../../../core/codec/derive" } +substrate-codec = { path = "../../../core/codec", default-features = false } +substrate-primitives = { path = "../../../core/primitives", default-features = false } +substrate-runtime-std = { path = "../../../core/runtime-std", default-features = false } +substrate-runtime-io = { path = "../../../core/runtime-io", default-features = false } +substrate-runtime-support = { path = "../../../runtime/support", default-features = false } +substrate-runtime-balances = { path = "../../../runtime/balances", default-features = false } +substrate-runtime-consensus = { path = "../../../runtime/consensus", default-features = false } +substrate-runtime-contract = { path = "../../../runtime/contract", default-features = false } +substrate-runtime-council = { path = "../../../runtime/council", default-features = false } +substrate-runtime-democracy = { path = "../../../runtime/democracy", default-features = false } +substrate-runtime-executive = { path = "../../../runtime/executive", default-features = false } +substrate-runtime-primitives = { path = "../../../runtime/primitives", default-features = false } +substrate-runtime-session = { path = "../../../runtime/session", default-features = false } +substrate-runtime-staking = { path = "../../../runtime/staking", default-features = false } +substrate-runtime-system = { path = "../../../runtime/system", default-features = false } +substrate-runtime-timestamp = { path = "../../../runtime/timestamp", default-features = false } +substrate-runtime-treasury = { path = "../../../runtime/treasury", default-features = false } +substrate-runtime-version = { path = "../../../runtime/version", default-features = false } + +[profile.release] +panic = "abort" +lto = true + +[workspace] +members = [] diff --git a/node/runtime/wasm/build.sh b/node/runtime/wasm/build.sh new file mode 100755 index 0000000000000..9fe3f0ca10d68 --- /dev/null +++ b/node/runtime/wasm/build.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -e + +cargo +nightly 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/node/runtime/wasm/src b/node/runtime/wasm/src new file mode 120000 index 0000000000000..5cd551cf2693e --- /dev/null +++ b/node/runtime/wasm/src @@ -0,0 +1 @@ +../src \ No newline at end of file diff --git a/node/service/Cargo.toml b/node/service/Cargo.toml new file mode 100644 index 0000000000000..5890c2ab7456a --- /dev/null +++ b/node/service/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "node-service" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +parking_lot = "0.4" +error-chain = "0.12" +lazy_static = "1.0" +log = "0.3" +slog = "^2" +tokio = "0.1.7" +hex-literal = "0.1" +node-api = { path = "../api" } +node-primitives = { path = "../primitives" } +node-runtime = { path = "../runtime" } +node-executor = { path = "../executor" } +node-consensus = { path = "../consensus" } +node-network = { path = "../network" } +node-transaction-pool = { path = "../transaction-pool" } +substrate-runtime-io = { path = "../../core/runtime-io" } +substrate-primitives = { path = "../../core/primitives" } +substrate-network = { path = "../../core/network" } +substrate-client = { path = "../../core/client" } +substrate-service = { path = "../../core/service" } +substrate-telemetry = { path = "../../core/telemetry" } diff --git a/node/service/src/chain_spec.rs b/node/service/src/chain_spec.rs new file mode 100644 index 0000000000000..b26de88f75953 --- /dev/null +++ b/node/service/src/chain_spec.rs @@ -0,0 +1,209 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Substrate chain configurations. + +use primitives::{AuthorityId, ed25519}; +use node_runtime::{GenesisConfig, ConsensusConfig, CouncilConfig, DemocracyConfig, + SessionConfig, StakingConfig, TimestampConfig, BalancesConfig, TreasuryConfig, Permill}; +use service::ChainSpec; + +const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/"; + +pub fn testnet_config() -> Result, String> { + //ChainSpec::from_embedded(include_bytes!("../res/node.json")) + Ok(staging_testnet_config()) +} + +fn staging_testnet_config_genesis() -> GenesisConfig { + let initial_authorities = vec![ + hex!["82c39b31a2b79a90f8e66e7a77fdb85a4ed5517f2ae39f6a80565e8ecae85cf5"].into(), + hex!["4de37a07567ebcbf8c64568428a835269a566723687058e017b6d69db00a77e7"].into(), + hex!["063d7787ebca768b7445dfebe7d62cbb1625ff4dba288ea34488da266dd6dca5"].into(), + hex!["8101764f45778d4980dadaceee6e8af2517d3ab91ac9bec9cd1714fa5994081c"].into(), + ]; + let endowed_accounts = vec![ + hex!["f295940fa750df68a686fcf4abd4111c8a9c5a5a5a83c4c8639c451a94a7adfd"].into(), + ]; + GenesisConfig { + consensus: Some(ConsensusConfig { + code: include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm").to_vec(), // TODO change + authorities: initial_authorities.clone(), + }), + system: None, + balances: Some(BalancesConfig { + transaction_base_fee: 100, + transaction_byte_fee: 1, + existential_deposit: 500, + transfer_fee: 0, + creation_fee: 0, + reclaim_rebate: 0, + balances: endowed_accounts.iter().map(|&k|(k, 1u64 << 60)).collect(), + }), + session: Some(SessionConfig { + validators: initial_authorities.iter().cloned().map(Into::into).collect(), + session_length: 60, // that's 5 minutes per session. + }), + staking: Some(StakingConfig { + current_era: 0, + intentions: initial_authorities.iter().cloned().map(Into::into).collect(), + offline_slash: 10000, + session_reward: 100, + validator_count: 12, + sessions_per_era: 12, // 1 hour per era + bonding_duration: 24 * 60 * 12, // 1 day per bond. + offline_slash_grace: 4, + minimum_validator_count: 4, + }), + democracy: Some(DemocracyConfig { + launch_period: 12 * 60 * 24, // 1 day per public referendum + voting_period: 12 * 60 * 24 * 3, // 3 days to discuss & vote on an active referendum + minimum_deposit: 5000, // 12000 as the minimum deposit for a referendum + }), + council: Some(CouncilConfig { + active_council: vec![], + candidacy_bond: 5000, // 5000 to become a council candidate + voter_bond: 1000, // 1000 down to vote for a candidate + present_slash_per_voter: 1, // slash by 1 per voter for an invalid presentation. + carry_count: 6, // carry over the 6 runners-up to the next council election + presentation_duration: 12 * 60 * 24, // one day for presenting winners. + approval_voting_period: 12 * 60 * 24 * 2, // two days period between possible council elections. + term_duration: 12 * 60 * 24 * 24, // 24 day term duration for the council. + desired_seats: 0, // start with no council: we'll raise this once the stake has been dispersed a bit. + inactive_grace_period: 1, // one addition vote should go by before an inactive voter can be reaped. + + cooloff_period: 12 * 60 * 24 * 4, // 4 day cooling off period if council member vetoes a proposal. + voting_period: 12 * 60 * 24, // 1 day voting period for council members. + }), + timestamp: Some(TimestampConfig { + period: 5, // 5 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), + }), + } +} + +/// Staging testnet config. +pub fn staging_testnet_config() -> ChainSpec { + let boot_nodes = vec![]; + ChainSpec::from_genesis( + "Staging Testnet", + "staging_testnet", + staging_testnet_config_genesis, + boot_nodes, + Some(STAGING_TELEMETRY_URL.into()), + ) +} + +fn testnet_genesis(initial_authorities: Vec) -> GenesisConfig { + let endowed_accounts = vec![ + ed25519::Pair::from_seed(b"Alice ").public().0.into(), + ed25519::Pair::from_seed(b"Bob ").public().0.into(), + ed25519::Pair::from_seed(b"Charlie ").public().0.into(), + ed25519::Pair::from_seed(b"Dave ").public().0.into(), + ed25519::Pair::from_seed(b"Eve ").public().0.into(), + ed25519::Pair::from_seed(b"Ferdie ").public().0.into(), + ]; + GenesisConfig { + consensus: Some(ConsensusConfig { + code: include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm").to_vec(), + authorities: initial_authorities.clone(), + }), + system: None, + balances: Some(BalancesConfig { + transaction_base_fee: 1, + transaction_byte_fee: 0, + existential_deposit: 500, + transfer_fee: 0, + creation_fee: 0, + reclaim_rebate: 0, + balances: endowed_accounts.iter().map(|&k|(k, (1u64 << 60))).collect(), + }), + session: Some(SessionConfig { + validators: initial_authorities.iter().cloned().map(Into::into).collect(), + session_length: 10, + }), + staking: Some(StakingConfig { + current_era: 0, + intentions: initial_authorities.iter().cloned().map(Into::into).collect(), + minimum_validator_count: 1, + validator_count: 2, + sessions_per_era: 5, + bonding_duration: 2 * 60 * 12, + offline_slash: 0, + session_reward: 0, + offline_slash_grace: 0, + }), + democracy: Some(DemocracyConfig { + launch_period: 9, + voting_period: 18, + minimum_deposit: 10, + }), + council: Some(CouncilConfig { + active_council: endowed_accounts.iter() + .filter(|a| initial_authorities.iter().find(|&b| a.0 == b.0).is_none()) + .map(|a| (a.clone(), 1000000)).collect(), + candidacy_bond: 10, + voter_bond: 2, + present_slash_per_voter: 1, + carry_count: 4, + presentation_duration: 10, + approval_voting_period: 20, + term_duration: 1000000, + desired_seats: (endowed_accounts.len() - initial_authorities.len()) as u32, + inactive_grace_period: 1, + + cooloff_period: 75, + voting_period: 20, + }), + timestamp: Some(TimestampConfig { + period: 5, // 5 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), + }), + } +} + +fn development_config_genesis() -> GenesisConfig { + testnet_genesis(vec![ + ed25519::Pair::from_seed(b"Alice ").public().into(), + ]) +} + +/// Development config (single validator Alice) +pub fn development_config() -> ChainSpec { + ChainSpec::from_genesis("Development", "development", development_config_genesis, vec![], None) +} + +fn local_testnet_genesis() -> GenesisConfig { + testnet_genesis(vec![ + ed25519::Pair::from_seed(b"Alice ").public().into(), + ed25519::Pair::from_seed(b"Bob ").public().into(), + ]) +} + +/// Local testnet config (multivalidator Alice + Bob) +pub fn local_testnet_config() -> ChainSpec { + ChainSpec::from_genesis("Local Testnet", "local_testnet", local_testnet_genesis, vec![], None) +} diff --git a/node/service/src/lib.rs b/node/service/src/lib.rs new file mode 100644 index 0000000000000..469349d1c4ab2 --- /dev/null +++ b/node/service/src/lib.rs @@ -0,0 +1,213 @@ +// Copyright 2018 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 . + +#![warn(unused_extern_crates)] + +//! Substrate service. Specialized wrapper over substrate service. + +extern crate node_api; +extern crate node_primitives; +extern crate node_runtime; +extern crate node_executor; +extern crate node_network; +extern crate node_transaction_pool as transaction_pool; +extern crate node_consensus as consensus; +extern crate substrate_primitives as primitives; +extern crate substrate_network as network; +extern crate substrate_client as client; +extern crate substrate_service as service; +extern crate tokio; + +#[macro_use] +extern crate log; +#[macro_use] +extern crate hex_literal; + +pub mod chain_spec; + +use std::sync::Arc; + +use transaction_pool::TransactionPool; +use node_api::Api; +use node_primitives::{Block, Hash}; +use node_runtime::GenesisConfig; +use client::Client; +use node_network::{Protocol as DemoProtocol, consensus::ConsensusNetwork}; +use tokio::runtime::TaskExecutor; +use service::FactoryFullConfiguration; +use primitives::{Blake2Hasher, RlpCodec}; + +pub use service::{Roles, PruningMode, ExtrinsicPoolOptions, + ErrorKind, Error, ComponentBlock, LightComponents, FullComponents}; +pub use client::ExecutionStrategy; + +/// Specialised `ChainSpec`. +pub type ChainSpec = service::ChainSpec; +/// Client type for specialised `Components`. +pub type ComponentClient = Client<::Backend, ::Executor, Block>; +pub type NetworkService = network::Service::NetworkProtocol, Hash>; + +/// A collection of type to generalise specific components over full / light client. +pub trait Components: service::Components { + /// Demo API. + type Api: 'static + Api + Send + Sync; + /// Client backend. + type Backend: 'static + client::backend::Backend; + /// Client executor. + type Executor: 'static + client::CallExecutor + Send + Sync; +} + +impl Components for service::LightComponents { + type Api = service::LightClient; + type Executor = service::LightExecutor; + type Backend = service::LightBackend; +} + +impl Components for service::FullComponents { + type Api = service::FullClient; + type Executor = service::FullExecutor; + type Backend = service::FullBackend; +} + +/// All configuration for the node. +pub type Configuration = FactoryFullConfiguration; + +/// Demo-specific configuration. +#[derive(Default)] +pub struct CustomConfiguration; + +/// Config for the substrate service. +pub struct Factory; + +impl service::ServiceFactory for Factory { + type Block = Block; + type ExtrinsicHash = Hash; + type NetworkProtocol = DemoProtocol; + type RuntimeDispatch = node_executor::Executor; + type FullExtrinsicPoolApi = transaction_pool::ChainApi>; + type LightExtrinsicPoolApi = transaction_pool::ChainApi>; + type Genesis = GenesisConfig; + type Configuration = CustomConfiguration; + + const NETWORK_PROTOCOL_ID: network::ProtocolId = ::node_network::PROTOCOL_ID; + + fn build_full_extrinsic_pool(config: ExtrinsicPoolOptions, client: Arc>) + -> Result>, Error> + { + Ok(TransactionPool::new(config, transaction_pool::ChainApi::new(client))) + } + + fn build_light_extrinsic_pool(config: ExtrinsicPoolOptions, client: Arc>) + -> Result>, Error> + { + Ok(TransactionPool::new(config, transaction_pool::ChainApi::new(client))) + } + + fn build_network_protocol(_config: &Configuration) + -> Result + { + Ok(DemoProtocol::new()) + } +} + +/// Demo service. +pub struct Service { + inner: service::Service, + client: Arc>, + network: Arc, + api: Arc<::Api>, + _consensus: Option, +} + +impl Service { + pub fn client(&self) -> Arc> { + self.client.clone() + } + + pub fn network(&self) -> Arc { + self.network.clone() + } + + pub fn api(&self) -> Arc<::Api> { + self.api.clone() + } +} + +/// Creates light client and register protocol with the network service +pub fn new_light(config: Configuration, executor: TaskExecutor) + -> Result>, Error> +{ + let service = service::Service::>::new(config, executor.clone())?; + let api = service.client(); + Ok(Service { + client: service.client(), + network: service.network(), + api: api, + inner: service, + _consensus: None, + }) +} + +/// Creates full client and register protocol with the network service +pub fn new_full(config: Configuration, executor: TaskExecutor) + -> Result>, Error> +{ + let is_validator = (config.roles & Roles::AUTHORITY) == Roles::AUTHORITY; + let service = service::Service::>::new(config, executor.clone())?; + // Spin consensus service if configured + let consensus = if is_validator { + // Load the first available key + let key = service.keystore().load(&service.keystore().contents()?[0], "")?; + info!("Using authority key {}", key.public()); + + let client = service.client(); + + let consensus_net = ConsensusNetwork::new(service.network(), client.clone()); + Some(consensus::Service::new( + client.clone(), + client.clone(), + consensus_net, + service.extrinsic_pool(), + executor, + key, + )) + } else { + None + }; + + Ok(Service { + client: service.client(), + network: service.network(), + api: service.client(), + inner: service, + _consensus: consensus, + }) +} + +/// Creates bare client without any networking. +pub fn new_client(config: Configuration) + -> Result>>, Error> +{ + service::new_client::(config) +} + +impl ::std::ops::Deref for Service { + type Target = service::Service; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} diff --git a/node/src/main.rs b/node/src/main.rs new file mode 100644 index 0000000000000..dca5e5ed90967 --- /dev/null +++ b/node/src/main.rs @@ -0,0 +1,69 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Substrate CLI + +#![warn(missing_docs)] + +extern crate node_cli as cli; +extern crate ctrlc; +extern crate futures; + +#[macro_use] +extern crate error_chain; + +use cli::VersionInfo; +use futures::sync::oneshot; +use futures::{future, Future}; + +use std::cell::RefCell; + +mod vergen { + #![allow(unused)] + include!(concat!(env!("OUT_DIR"), "/version.rs")); +} + +// handles ctrl-c +struct Exit; +impl cli::IntoExit for Exit { + type Exit = future::MapErr, fn(oneshot::Canceled) -> ()>; + fn into_exit(self) -> Self::Exit { + // can't use signal directly here because CtrlC takes only `Fn`. + let (exit_send, exit) = oneshot::channel(); + + let exit_send_cell = RefCell::new(Some(exit_send)); + ctrlc::set_handler(move || { + if let Some(exit_send) = exit_send_cell.try_borrow_mut().expect("signal handler not reentrant; qed").take() { + exit_send.send(()).expect("Error sending exit notification"); + } + }).expect("Error setting Ctrl-C handler"); + + exit.map_err(drop) + } +} + +quick_main!(run); + +fn run() -> cli::error::Result<()> { + let version = VersionInfo { + commit: vergen::short_sha(), + version: env!("CARGO_PKG_VERSION"), + executable_name: "substrate", + author: "Parity Team ", + description: "Generic substrate node", + }; + cli::run(::std::env::args(), Exit, version) +} diff --git a/node/transaction-pool/Cargo.toml b/node/transaction-pool/Cargo.toml new file mode 100644 index 0000000000000..0db2796437cf3 --- /dev/null +++ b/node/transaction-pool/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "node-transaction-pool" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +log = "0.3.0" +error-chain = "0.12" +parking_lot = "0.4" +node-api = { path = "../api" } +node-primitives = { path = "../primitives" } +node-runtime = { path = "../runtime" } +substrate-client = { path = "../../core/client" } +substrate-codec = { path = "../../core/codec" } +substrate-keyring = { path = "../../core/keyring" } +substrate-extrinsic-pool = { path = "../../core/extrinsic-pool" } +substrate-primitives = { path = "../../core/primitives" } +substrate-runtime-primitives = { path = "../../runtime/primitives" } diff --git a/node/transaction-pool/src/error.rs b/node/transaction-pool/src/error.rs new file mode 100644 index 0000000000000..7d1712a55ac2c --- /dev/null +++ b/node/transaction-pool/src/error.rs @@ -0,0 +1,73 @@ +// Copyright 2018 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 extrinsic_pool; +use node_api; +use primitives::Hash; +use runtime::{Address, UncheckedExtrinsic}; + +error_chain! { + links { + Pool(extrinsic_pool::Error, extrinsic_pool::ErrorKind); + Api(node_api::Error, node_api::ErrorKind); + } + errors { + /// Unexpected extrinsic format submitted + InvalidExtrinsicFormat { + description("Invalid extrinsic format."), + display("Invalid extrinsic format."), + } + /// Attempted to queue an inherent transaction. + IsInherent(xt: UncheckedExtrinsic) { + description("Inherent transactions cannot be queued."), + display("Inherent transactions cannot be queued."), + } + /// Attempted to queue a transaction with bad signature. + BadSignature(e: &'static str) { + description("Transaction had bad signature."), + display("Transaction had bad signature: {}", e), + } + /// Attempted to queue a transaction that is already in the pool. + AlreadyImported(hash: Hash) { + description("Transaction is already in the pool."), + display("Transaction {:?} is already in the pool.", hash), + } + /// Import error. + Import(err: Box<::std::error::Error + Send>) { + description("Error importing transaction"), + display("Error importing transaction: {}", err.description()), + } + /// Runtime failure. + UnrecognisedAddress(who: Address) { + description("Unrecognised address in extrinsic"), + display("Unrecognised address in extrinsic: {}", who), + } + /// Extrinsic too large + TooLarge(got: usize, max: usize) { + description("Extrinsic too large"), + display("Extrinsic is too large ({} > {})", got, max), + } + } +} + +impl extrinsic_pool::IntoPoolError for Error { + fn into_pool_error(self) -> ::std::result::Result { + match self { + Error(ErrorKind::Pool(e), c) => Ok(extrinsic_pool::Error(e, c)), + e => Err(e), + } + } +} diff --git a/node/transaction-pool/src/lib.rs b/node/transaction-pool/src/lib.rs new file mode 100644 index 0000000000000..97ef5b59f2fdc --- /dev/null +++ b/node/transaction-pool/src/lib.rs @@ -0,0 +1,236 @@ +// Copyright 2018 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 . + +extern crate substrate_client as client; +extern crate substrate_codec as codec; +extern crate substrate_extrinsic_pool as extrinsic_pool; +extern crate substrate_primitives; +extern crate substrate_runtime_primitives; +extern crate node_runtime as runtime; +extern crate node_primitives as primitives; +extern crate node_api; +extern crate parking_lot; + +#[cfg(test)] +extern crate substrate_keyring; + +#[macro_use] +extern crate error_chain; + +#[macro_use] +extern crate log; + +mod error; + +use std::{ + cmp::Ordering, + collections::HashMap, + sync::Arc, +}; + +use codec::{Decode, Encode}; +use extrinsic_pool::{Readiness, scoring::{Change, Choice}, VerifiedFor, ExtrinsicFor}; +use node_api::Api; +use primitives::{AccountId, BlockId, Block, Hash, Index}; +use runtime::{Address, UncheckedExtrinsic, RawAddress}; +use substrate_runtime_primitives::traits::{Bounded, Checkable, Hash as HashT, BlakeTwo256}; + +pub use extrinsic_pool::{Options, Status, LightStatus, VerifiedTransaction as VerifiedTransactionOps}; +pub use error::{Error, ErrorKind, Result}; + +/// Maximal size of a single encoded extrinsic. +const MAX_TRANSACTION_SIZE: usize = 4 * 1024 * 1024; + +/// Type alias for convenience. +pub type CheckedExtrinsic = std::result::Result>>::Checked; + +/// Type alias for the transaction pool. +pub type TransactionPool = extrinsic_pool::Pool>; + +/// A verified transaction which should be includable and non-inherent. +#[derive(Clone, Debug)] +pub struct VerifiedTransaction { + /// Transaction hash. + pub hash: Hash, + /// Transaction sender. + pub sender: AccountId, + /// Transaction index. + pub index: Index, + encoded_size: usize, +} + +impl VerifiedTransaction { + /// Get the 256-bit hash of this transaction. + pub fn hash(&self) -> &Hash { + &self.hash + } + + /// Get the account ID of the sender of this transaction. + pub fn index(&self) -> Index { + self.index + } + + /// Get encoded size of the transaction. + pub fn encoded_size(&self) -> usize { + self.encoded_size + } +} + +impl extrinsic_pool::VerifiedTransaction for VerifiedTransaction { + type Hash = Hash; + type Sender = AccountId; + + fn hash(&self) -> &Self::Hash { + &self.hash + } + + fn sender(&self) -> &Self::Sender { + &self.sender + } + + fn mem_usage(&self) -> usize { + self.encoded_size // TODO + } +} + +/// The transaction pool logic. +pub struct ChainApi { + api: Arc, +} + +impl ChainApi where + A: Api, +{ + /// Create a new instance. + pub fn new(api: Arc) -> Self { + ChainApi { + api, + } + } +} + + +impl extrinsic_pool::ChainApi for ChainApi where + A: Api + Send + Sync, +{ + type Block = Block; + type Hash = Hash; + type Sender = AccountId; + type VEx = VerifiedTransaction; + type Ready = HashMap; + type Error = Error; + type Score = u64; + type Event = (); + + fn verify_transaction(&self, _at: &BlockId, xt: &ExtrinsicFor) -> Result { + let encoded = xt.encode(); + let uxt = UncheckedExtrinsic::decode(&mut encoded.as_slice()).ok_or_else(|| ErrorKind::InvalidExtrinsicFormat)?; + if !uxt.is_signed() { + bail!(ErrorKind::IsInherent(uxt)) + } + + let (encoded_size, hash) = (encoded.len(), BlakeTwo256::hash(&encoded)); + if encoded_size > MAX_TRANSACTION_SIZE { + bail!(ErrorKind::TooLarge(encoded_size, MAX_TRANSACTION_SIZE)); + } + + debug!(target: "transaction-pool", "Transaction submitted: {}", ::substrate_primitives::hexdisplay::HexDisplay::from(&encoded)); + let checked = uxt.clone().check_with(|a| { + match a { + RawAddress::Id(id) => Ok(id), + RawAddress::Index(_) => Err("Index based addresses are not supported".into()),// TODO: Make index addressing optional in substrate + } + })?; + let sender = checked.signed.expect("Only signed extrinsics are allowed at this point"); + + + if encoded_size < 1024 { + debug!(target: "transaction-pool", "Transaction verified: {} => {:?}", hash, uxt); + } else { + debug!(target: "transaction-pool", "Transaction verified: {} ({} bytes is too large to display)", hash, encoded_size); + } + + Ok(VerifiedTransaction { + index: checked.index, + sender, + hash, + encoded_size, + }) + } + + fn ready(&self) -> Self::Ready { + HashMap::default() + } + + fn is_ready(&self, at: &BlockId, known_nonces: &mut Self::Ready, xt: &VerifiedFor) -> Readiness { + let sender = xt.verified.sender().clone(); + trace!(target: "transaction-pool", "Checking readiness of {} (from {})", xt.verified.hash, sender); + + // TODO: find a way to handle index error properly -- will need changes to + // transaction-pool trait. + let api = &self.api; + let next_index = known_nonces.entry(sender) + .or_insert_with(|| api.index(at, sender).ok().unwrap_or_else(Bounded::max_value)); + + trace!(target: "transaction-pool", "Next index for sender is {}; xt index is {}", next_index, xt.verified.index); + + let result = match xt.verified.index.cmp(&next_index) { + // TODO: this won't work perfectly since accounts can now be killed, returning the nonce + // to zero. + // We should detect if the index was reset and mark all transactions as `Stale` for cull to work correctly. + // Otherwise those transactions will keep occupying the queue. + // Perhaps we could mark as stale if `index - state_index` > X? + Ordering::Greater => Readiness::Future, + Ordering::Equal => Readiness::Ready, + // TODO [ToDr] Should mark transactions referencing too old blockhash as `Stale` as well. + Ordering::Less => Readiness::Stale, + }; + + // remember to increment `next_index` + *next_index = next_index.saturating_add(1); + + result + } + + fn compare(old: &VerifiedFor, other: &VerifiedFor) -> Ordering { + old.verified.index().cmp(&other.verified.index()) + } + + fn choose(old: &VerifiedFor, new: &VerifiedFor) -> Choice { + if old.verified.index() == new.verified.index() { + return Choice::ReplaceOld; + } + Choice::InsertNew + } + + fn update_scores( + xts: &[extrinsic_pool::Transaction>], + scores: &mut [Self::Score], + _change: Change<()> + ) { + for i in 0..xts.len() { + // all the same score since there are no fees. + // TODO: prioritize things like misbehavior or fishermen reports + scores[i] = 1; + } + } + + fn should_replace(_old: &VerifiedFor, _new: &VerifiedFor) -> Choice { + // Don't allow new transactions if we are reaching the limit. + Choice::RejectNew + } +} + diff --git a/runtime/README.adoc b/runtime/README.adoc new file mode 100644 index 0000000000000..616c12568a076 --- /dev/null +++ b/runtime/README.adoc @@ -0,0 +1,6 @@ + += Runtime + +Set of libs for the substrate runtime. + +TODO: Add READMEs to packages. diff --git a/runtime/balances/Cargo.toml b/runtime/balances/Cargo.toml new file mode 100644 index 0000000000000..8729db0b5d812 --- /dev/null +++ b/runtime/balances/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "substrate-runtime-balances" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +hex-literal = "0.1.0" +serde = { version = "1.0", default_features = false } +serde_derive = { version = "1.0", optional = true } +safe-mix = { version = "1.0", default_features = false} +substrate-keyring = { path = "../../core/keyring", optional = true } +substrate-codec = { path = "../../core/codec", default_features = false } +substrate-codec-derive = { path = "../../core/codec/derive", default_features = false } +substrate-primitives = { path = "../../core/primitives", default_features = false } +substrate-runtime-std = { path = "../../core/runtime-std", default_features = false } +substrate-runtime-io = { path = "../../core/runtime-io", default_features = false } +substrate-runtime-support = { path = "../support", default_features = false } +substrate-runtime-primitives = { path = "../primitives", default_features = false } +substrate-runtime-system = { path = "../system", default_features = false } + +[features] +default = ["std"] +std = [ + "serde/std", + "serde_derive", + "safe-mix/std", + "substrate-keyring", + "substrate-codec/std", + "substrate-codec-derive/std", + "substrate-primitives/std", + "substrate-runtime-std/std", + "substrate-runtime-io/std", + "substrate-runtime-support/std", + "substrate-runtime-primitives/std", + "substrate-runtime-system/std", +] diff --git a/runtime/balances/src/address.rs b/runtime/balances/src/address.rs new file mode 100644 index 0000000000000..01fb25439507b --- /dev/null +++ b/runtime/balances/src/address.rs @@ -0,0 +1,111 @@ +// Copyright 2017 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 . + +//! Address type that is union of index and id for an account. + +#[cfg(feature = "std")] +use std::fmt; +use super::{Member, Decode, Encode, As, Input, Output}; + +/// A vetted and verified extrinsic from the external world. +#[derive(PartialEq, Eq, Clone)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug, Hash))] +pub enum Address where + AccountId: Member, + AccountIndex: Member, +{ + /// It's an account ID (pubkey). + #[cfg_attr(feature = "std", serde(deserialize_with="AccountId::deserialize"))] + Id(AccountId), + /// It's an account index. + #[cfg_attr(feature = "std", serde(deserialize_with="AccountIndex::deserialize"))] + Index(AccountIndex), +} + +#[cfg(feature = "std")] +impl fmt::Display for Address where + AccountId: Member, + AccountIndex: Member, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "{:?}", self) + } +} + +impl From for Address where + AccountId: Member, + AccountIndex: Member, +{ + fn from(a: AccountId) -> Self { + Address::Id(a) + } +} + +fn need_more_than(a: T, b: T) -> Option { + if a < b { Some(a) } else { None } +} + +impl Decode for Address where + AccountId: Member + Decode, + AccountIndex: Member + Decode + PartialOrd + Ord + As + As + As + Copy, +{ + fn decode(input: &mut I) -> Option { + Some(match input.read_byte()? { + x @ 0x00...0xef => Address::Index(As::sa(x)), + 0xfc => Address::Index(As::sa(need_more_than(0xef, u16::decode(input)?)?)), + 0xfd => Address::Index(As::sa(need_more_than(0xffff, u32::decode(input)?)?)), + 0xfe => Address::Index(need_more_than(As::sa(0xffffffffu32), Decode::decode(input)?)?), + 0xff => Address::Id(Decode::decode(input)?), + _ => return None, + }) + } +} + +impl Encode for Address where + AccountId: Member + Encode, + AccountIndex: Member + Encode + PartialOrd + Ord + As + As + As + Copy, +{ + fn encode_to(&self, dest: &mut T) { + match *self { + Address::Id(ref i) => { + dest.push_byte(255); + dest.push(i); + } + Address::Index(i) if i > As::sa(0xffffffffu32) => { + dest.push_byte(254); + dest.push(&i); + } + Address::Index(i) if i > As::sa(0xffffu32) => { + dest.push_byte(253); + dest.push(&As::::as_(i)); + } + Address::Index(i) if i >= As::sa(0xf0u32) => { + dest.push_byte(252); + dest.push(&As::::as_(i)); + } + Address::Index(i) => dest.push_byte(As::::as_(i)), + } + } +} + +impl Default for Address where + AccountId: Member + Default, + AccountIndex: Member, +{ + fn default() -> Self { + Address::Id(Default::default()) + } +} diff --git a/runtime/balances/src/genesis_config.rs b/runtime/balances/src/genesis_config.rs new file mode 100644 index 0000000000000..1b88353c632e3 --- /dev/null +++ b/runtime/balances/src/genesis_config.rs @@ -0,0 +1,84 @@ +// Copyright 2017 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 . + +//! Build a balances genesis block. + +#![cfg(feature = "std")] + +use std::collections::HashMap; +use rstd::prelude::*; +use codec::Encode; +use runtime_support::{StorageValue, StorageMap}; +use primitives::traits::{Zero, As}; +use substrate_primitives::Blake2Hasher; +use {runtime_io, primitives}; +use super::{Trait, ENUM_SET_SIZE, EnumSet, NextEnumSet, CreationFee, TransferFee, + ReclaimRebate, ExistentialDeposit, TransactionByteFee, TransactionBaseFee, TotalIssuance, + FreeBalance}; + +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +#[serde(deny_unknown_fields)] +pub struct GenesisConfig { + pub balances: Vec<(T::AccountId, T::Balance)>, + pub transaction_base_fee: T::Balance, + pub transaction_byte_fee: T::Balance, + pub transfer_fee: T::Balance, + pub creation_fee: T::Balance, + pub reclaim_rebate: T::Balance, + pub existential_deposit: T::Balance, +} + +impl Default for GenesisConfig { + fn default() -> Self { + GenesisConfig { + balances: vec![], + transaction_base_fee: T::Balance::sa(0), + transaction_byte_fee: T::Balance::sa(0), + transfer_fee: T::Balance::sa(0), + creation_fee: T::Balance::sa(0), + existential_deposit: T::Balance::sa(0), + reclaim_rebate: T::Balance::sa(0), + } + } +} + +impl primitives::BuildStorage for GenesisConfig { + fn build_storage(self) -> ::std::result::Result, Vec>, String> { + let total_issuance: T::Balance = self.balances.iter().fold(Zero::zero(), |acc, &(_, n)| acc + n); + + let mut r: runtime_io::TestExternalities = map![ + Self::hash(>::key()).to_vec() => T::AccountIndex::sa(self.balances.len() / ENUM_SET_SIZE).encode(), + Self::hash(>::key()).to_vec() => self.transaction_base_fee.encode(), + Self::hash(>::key()).to_vec() => self.transaction_byte_fee.encode(), + Self::hash(>::key()).to_vec() => self.transfer_fee.encode(), + Self::hash(>::key()).to_vec() => self.creation_fee.encode(), + Self::hash(>::key()).to_vec() => self.existential_deposit.encode(), + Self::hash(>::key()).to_vec() => self.reclaim_rebate.encode(), + Self::hash(>::key()).to_vec() => total_issuance.encode() + ]; + + let ids: Vec<_> = self.balances.iter().map(|x| x.0.clone()).collect(); + for i in 0..(ids.len() + ENUM_SET_SIZE - 1) / ENUM_SET_SIZE { + r.insert(Self::hash(&>::key_for(T::AccountIndex::sa(i))).to_vec(), + ids[i * ENUM_SET_SIZE..ids.len().min((i + 1) * ENUM_SET_SIZE)].to_owned().encode()); + } + for (who, value) in self.balances.into_iter() { + r.insert(Self::hash(&>::key_for(who)).to_vec(), value.encode()); + } + Ok(r.into()) + } +} diff --git a/runtime/balances/src/lib.rs b/runtime/balances/src/lib.rs new file mode 100644 index 0000000000000..3451b55f6aa47 --- /dev/null +++ b/runtime/balances/src/lib.rs @@ -0,0 +1,660 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Balances: Handles balances. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(feature = "std")] +extern crate serde; + +#[cfg(feature = "std")] +#[macro_use] +extern crate serde_derive; + +#[macro_use] +extern crate substrate_runtime_support as runtime_support; + +#[cfg_attr(feature = "std", macro_use)] +extern crate substrate_runtime_std as rstd; + +#[macro_use] +extern crate substrate_codec_derive; + +extern crate substrate_codec as codec; +extern crate substrate_primitives; +extern crate substrate_runtime_io as runtime_io; +extern crate substrate_runtime_primitives as primitives; +extern crate substrate_runtime_system as system; + +use rstd::prelude::*; +use rstd::{cmp, result}; +use codec::{Encode, Decode, Codec, Input, Output}; +use runtime_support::{StorageValue, StorageMap, Parameter}; +use runtime_support::dispatch::Result; +use primitives::traits::{Zero, One, SimpleArithmetic, OnFinalise, MakePayment, + As, Lookup, Member, CheckedAdd, CheckedSub}; +use address::Address as RawAddress; +use system::{ensure_signed, ensure_root}; + +mod mock; + +pub mod address; +mod tests; +mod genesis_config; + +#[cfg(feature = "std")] +pub use genesis_config::GenesisConfig; + +/// Number of account IDs stored per enum set. +const ENUM_SET_SIZE: usize = 64; + +/// The byte to identify intention to reclaim an existing account index. +const RECLAIM_INDEX_MAGIC: usize = 0x69; + +pub type Address = RawAddress<::AccountId, ::AccountIndex>; + +/// The account with the given id was killed. +pub trait OnFreeBalanceZero { + /// The account was the given id was killed. + fn on_free_balance_zero(who: &AccountId); +} + +impl OnFreeBalanceZero for () { + fn on_free_balance_zero(_who: &AccountId) {} +} +impl< + AccountId, + X: OnFreeBalanceZero, + Y: OnFreeBalanceZero, +> OnFreeBalanceZero for (X, Y) { + fn on_free_balance_zero(who: &AccountId) { + X::on_free_balance_zero(who); + Y::on_free_balance_zero(who); + } +} + +/// Trait for a hook to get called when some balance has been minted, causing dilution. +pub trait OnDilution { + /// Some `portion` of the total balance just "grew" by `minted`. `portion` is the pre-growth + /// amount (it doesn't take account of the recent growth). + fn on_dilution(minted: Balance, portion: Balance); +} + +impl OnDilution for () { + fn on_dilution(_minted: Balance, _portion: Balance) {} +} + +/// Determinator for whether a given account is able to transfer balance. +pub trait EnsureAccountLiquid { + /// Returns `Ok` iff the account is able to transfer funds normally. `Err(...)` + /// with the reason why not otherwise. + fn ensure_account_liquid(who: &AccountId) -> Result; +} + +impl EnsureAccountLiquid for () { + fn ensure_account_liquid(_who: &AccountId) -> Result { Ok(()) } +} + +pub trait Trait: system::Trait { + /// The balance of an account. + type Balance: Parameter + SimpleArithmetic + Codec + Default + Copy + As + As + As; + /// Type used for storing an account's index; implies the maximum number of accounts the system + /// can hold. + type AccountIndex: Parameter + Member + Codec + SimpleArithmetic + As + As + As + As + As + Copy; + /// A function which is invoked when the free-balance has fallen below the existential deposit and + /// has been reduced to zero. + /// + /// Gives a chance to clean up resources associated with the given account. + type OnFreeBalanceZero: OnFreeBalanceZero; + + /// A function that returns true iff a given account can transfer its funds to another account. + type EnsureAccountLiquid: EnsureAccountLiquid; + + /// The overarching event type. + type Event: From> + Into<::Event>; +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + fn transfer(origin, dest: RawAddress, value: T::Balance) -> Result; + fn set_balance(origin, who: RawAddress, free: T::Balance, reserved: T::Balance) -> Result; + } +} + +decl_event!( + pub enum Event with RawEvent + where ::AccountId, ::AccountIndex, ::Balance { + /// A new account was created. + NewAccount(AccountId, AccountIndex, NewAccountOutcome), + /// An account was reaped. + ReapedAccount(AccountId), + /// Transfer succeeded (from, to, value, fees). + Transfer(AccountId, AccountId, Balance, Balance), + } +); + +decl_storage! { + trait Store for Module as Balances { + /// The total amount of stake on the system. + pub TotalIssuance get(total_issuance): required T::Balance; + /// The minimum amount allowed to keep an account open. + pub ExistentialDeposit get(existential_deposit): required T::Balance; + /// The amount credited to a destination's account whose index was reclaimed. + pub ReclaimRebate get(reclaim_rebate): required T::Balance; + /// The fee required to make a transfer. + pub TransferFee get(transfer_fee): required T::Balance; + /// The fee required to create an account. At least as big as ReclaimRebate. + pub CreationFee get(creation_fee): required T::Balance; + + /// The next free enumeration set. + pub NextEnumSet get(next_enum_set): required T::AccountIndex; + /// The enumeration sets. + pub EnumSet get(enum_set): default map [ T::AccountIndex => Vec ]; + + /// The 'free' balance of a given account. + /// + /// This is the only balance that matters in terms of most operations on tokens. It is + /// alone used to determine the balance when in the contract execution environment. When this + /// balance falls below the value of `ExistentialDeposit`, then the 'current account' is + /// deleted: specifically `FreeBalance`. Furthermore, `OnFreeBalanceZero` callback + /// is invoked, giving a chance to external modules to cleanup data associated with + /// the deleted account. + /// + /// `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): default map [ T::AccountId => T::Balance ]; + + /// The amount of the balance of a given account that is exterally reserved; this can still get + /// slashed, but gets slashed last of all. + /// + /// This balance is a 'reserve' balance that other subsystems use in order to set aside tokens + /// that are still 'owned' by the account holder, but which are unspendable. (This is different + /// and wholly unrelated to the `Bondage` system used in the staking module.) + /// + /// When this balance falls below the value of `ExistentialDeposit`, then this 'reserve account' + /// is deleted: specifically, `ReservedBalance`. + /// + /// `system::AccountNonce` is also deleted if `FreeBalance` is also zero (it also gets + /// collapsed to zero if it ever becomes less than `ExistentialDeposit`. + pub ReservedBalance get(reserved_balance): default map [ T::AccountId => T::Balance ]; + + + // Payment stuff. + + /// The fee to be paid for making a transaction; the base. + pub TransactionBaseFee get(transaction_base_fee): required T::Balance; + /// The fee to be paid for making a transaction; the per-byte portion. + pub TransactionByteFee get(transaction_byte_fee): required T::Balance; + } +} + +/// Whatever happened about the hint given when creating the new account. +#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] +#[derive(Encode, Decode, PartialEq, Eq, Clone, Copy)] +pub enum NewAccountOutcome { + NoHint, + GoodHint, + BadHint, +} + +/// Outcome of a balance update. +pub enum UpdateBalanceOutcome { + /// Account balance was simply updated. + Updated, + /// The update has led to killing of the account. + AccountKilled, +} + +impl Module { + + /// Deposit one of this module's events. + fn deposit_event(event: Event) { + >::deposit_event(::Event::from(event).into()); + } + + // PUBLIC IMMUTABLES + + /// The combined balance of `who`. + pub fn total_balance(who: &T::AccountId) -> T::Balance { + Self::free_balance(who) + Self::reserved_balance(who) + } + + /// Some result as `slash(who, value)` (but without the side-effects) assuming there are no + /// balance changes in the meantime and only the reserved balance is not taken into account. + pub fn can_slash(who: &T::AccountId, value: T::Balance) -> bool { + Self::free_balance(who) >= value + } + + /// Same result as `reserve(who, value)` (but without the side-effects) assuming there + /// are no balance changes in the meantime. + pub fn can_reserve(who: &T::AccountId, value: T::Balance) -> bool { + if T::EnsureAccountLiquid::ensure_account_liquid(who).is_ok() { + Self::free_balance(who) >= value + } else { + false + } + } + + /// Lookup an T::AccountIndex to get an Id, if there's one there. + pub fn lookup_index(index: T::AccountIndex) -> Option { + let enum_set_size = Self::enum_set_size(); + let set = Self::enum_set(index / enum_set_size); + let i: usize = (index % enum_set_size).as_(); + set.get(i).map(|x| x.clone()) + } + + /// `true` if the account `index` is ready for reclaim. + pub fn can_reclaim(try_index: T::AccountIndex) -> bool { + let enum_set_size = Self::enum_set_size(); + let try_set = Self::enum_set(try_index / enum_set_size); + let i = (try_index % enum_set_size).as_(); + i < try_set.len() && Self::total_balance(&try_set[i]).is_zero() + } + + /// Lookup an address to get an Id, if there's one there. + pub fn lookup_address(a: address::Address) -> Option { + match a { + address::Address::Id(i) => Some(i), + address::Address::Index(i) => Self::lookup_index(i), + } + } + + // PUBLIC DISPATCH + + /// Transfer some liquid free balance to another staker. + pub fn transfer(origin: T::Origin, dest: Address, value: T::Balance) -> Result { + let transactor = ensure_signed(origin)?; + + let dest = Self::lookup(dest)?; + let from_balance = Self::free_balance(&transactor); + let would_create = from_balance.is_zero(); + let fee = if would_create { Self::creation_fee() } else { Self::transfer_fee() }; + let liability = value + fee; + + let new_from_balance = match from_balance.checked_sub(&liability) { + Some(b) => b, + None => return Err("balance too low to send value"), + }; + if would_create && value < Self::existential_deposit() { + return Err("value too low to create account"); + } + T::EnsureAccountLiquid::ensure_account_liquid(&transactor)?; + + let to_balance = Self::free_balance(&dest); + // NOTE: total stake being stored in the same type means that this could never overflow + // but better to be safe than sorry. + let new_to_balance = match to_balance.checked_add(&value) { + Some(b) => b, + None => return Err("destination balance too high to receive value"), + }; + + if transactor != dest { + Self::set_free_balance(&transactor, new_from_balance); + Self::decrease_total_stake_by(fee); + Self::set_free_balance_creating(&dest, new_to_balance); + Self::deposit_event(RawEvent::Transfer(transactor, dest, value, fee)); + } + + Ok(()) + } + + /// Set the balances of a given account. + fn set_balance(origin: T::Origin, who: Address, free: T::Balance, reserved: T::Balance) -> Result { + ensure_root(origin)?; + let who = Self::lookup(who)?; + Self::set_free_balance(&who, free); + Self::set_reserved_balance(&who, reserved); + Ok(()) + } + + // PUBLIC MUTABLES (DANGEROUS) + + /// Set the free balance of an account to some new value. + /// + /// Will enforce ExistentialDeposit law, anulling the account as needed. + /// In that case it will return `AccountKilled`. + pub fn set_reserved_balance(who: &T::AccountId, balance: T::Balance) -> UpdateBalanceOutcome { + if balance < Self::existential_deposit() { + >::insert(who, balance); + Self::on_reserved_too_low(who); + UpdateBalanceOutcome::AccountKilled + } else { + >::insert(who, balance); + UpdateBalanceOutcome::Updated + } + } + + /// Set the free balance of an account to some new value. Will enforce ExistentialDeposit + /// law anulling the account as needed. + /// + /// Doesn't do any preparatory work for creating a new account, so should only be used when it + /// is known that the account already exists. + /// + /// Returns if the account was successfully updated or update has led to killing of the account. + pub fn set_free_balance(who: &T::AccountId, balance: T::Balance) -> UpdateBalanceOutcome { + // Commented out for no - but consider it instructive. + // assert!(!Self::total_balance(who).is_zero()); + if balance < Self::existential_deposit() { + >::insert(who, balance); + Self::on_free_too_low(who); + UpdateBalanceOutcome::AccountKilled + } else { + >::insert(who, balance); + UpdateBalanceOutcome::Updated + } + } + + /// Set the free balance on an account to some new value. + /// + /// Same as [`set_free_balance`], but will create a new account. + /// + /// Returns if the account was successfully updated or update has led to killing of the account. + /// + /// [`set_free_balance`]: #method.set_free_balance + pub fn set_free_balance_creating(who: &T::AccountId, balance: T::Balance) -> UpdateBalanceOutcome { + let ed = >::existential_deposit(); + // If the balance is too low, then the account is reaped. + // NOTE: There are two balances for every account: `reserved_balance` and + // `free_balance`. This contract subsystem only cares about the latter: whenever + // the term "balance" is used *here* it should be assumed to mean "free balance" + // in the rest of the module. + // Free balance can never be less than ED. If that happens, it gets reduced to zero + // and the account information relevant to this subsystem is deleted (i.e. the + // account is reaped). + // NOTE: This is orthogonal to the `Bondage` value that an account has, a high + // value of which makes even the `free_balance` unspendable. + // TODO: enforce this for the other balance-altering functions. + if balance < ed { + Self::set_free_balance(who, balance); + UpdateBalanceOutcome::AccountKilled + } else { + if !>::exists(who) { + let outcome = Self::new_account(&who, balance); + let credit = match outcome { + NewAccountOutcome::GoodHint => balance + >::reclaim_rebate(), + _ => balance, + }; + Self::set_free_balance(who, credit); + Self::increase_total_stake_by(credit - balance); + } else { + Self::set_free_balance(who, balance); + } + + UpdateBalanceOutcome::Updated + } + } + + /// Adds up to `value` to the free balance of `who`. If `who` doesn't exist, it is created. + /// + /// This is a sensitive function since it circumvents any fees associated with account + /// setup. Ensure it is only called by trusted code. + /// + /// NOTE: This assumes that the total stake remains unchanged after this operation. If + /// you mean to actually mint value into existence, then use `reward` instead. + pub fn increase_free_balance_creating(who: &T::AccountId, value: T::Balance) -> UpdateBalanceOutcome { + Self::set_free_balance_creating(who, Self::free_balance(who) + value) + } + + /// Deducts up to `value` from the combined balance of `who`, preferring to deduct from the + /// free balance. This function cannot fail. + /// + /// As much funds up to `value` will be deducted as possible. If this is less than `value`, + /// then `Some(remaining)` will be returned. Full completion is given by `None`. + pub fn slash(who: &T::AccountId, value: T::Balance) -> Option { + let free_balance = Self::free_balance(who); + let free_slash = cmp::min(free_balance, value); + Self::set_free_balance(who, free_balance - free_slash); + Self::decrease_total_stake_by(free_slash); + if free_slash < value { + Self::slash_reserved(who, value - free_slash) + } else { + None + } + } + + /// Adds up to `value` to the free balance of `who`. + /// + /// If `who` doesn't exist, nothing is done and an Err returned. + pub fn reward(who: &T::AccountId, value: T::Balance) -> Result { + if Self::total_balance(who).is_zero() { + return Err("beneficiary account must pre-exist"); + } + Self::set_free_balance(who, Self::free_balance(who) + value); + Self::increase_total_stake_by(value); + Ok(()) + } + + /// Moves `value` from balance to reserved balance. + /// + /// If the free balance is lower than `value`, then no funds will be moved and an `Err` will + /// be returned to notify of this. This is different behaviour to `unreserve`. + pub fn reserve(who: &T::AccountId, value: T::Balance) -> Result { + let b = Self::free_balance(who); + if b < value { + return Err("not enough free funds") + } + T::EnsureAccountLiquid::ensure_account_liquid(who)?; + Self::set_reserved_balance(who, Self::reserved_balance(who) + value); + Self::set_free_balance(who, b - value); + Ok(()) + } + + /// Moves up to `value` from reserved balance to balance. This function cannot fail. + /// + /// As much funds up to `value` will be deducted as possible. If this is less than `value`, + /// then `Some(remaining)` will be returned. Full completion is given by `None`. + /// NOTE: This is different to `reserve`. + pub fn unreserve(who: &T::AccountId, value: T::Balance) -> Option { + let b = Self::reserved_balance(who); + let actual = cmp::min(b, value); + Self::set_free_balance(who, Self::free_balance(who) + actual); + Self::set_reserved_balance(who, b - actual); + if actual == value { + None + } else { + Some(value - actual) + } + } + + /// Deducts up to `value` from reserved balance of `who`. This function cannot fail. + /// + /// As much funds up to `value` will be deducted as possible. If this is less than `value`, + /// then `Some(remaining)` will be returned. Full completion is given by `None`. + pub fn slash_reserved(who: &T::AccountId, value: T::Balance) -> Option { + let b = Self::reserved_balance(who); + let slash = cmp::min(b, value); + Self::set_reserved_balance(who, b - slash); + Self::decrease_total_stake_by(slash); + if value == slash { + None + } else { + Some(value - slash) + } + } + + /// Moves up to `value` from reserved balance of account `slashed` to free balance of account + /// `beneficiary`. `beneficiary` must exist for this to succeed. If it does not, `Err` will be + /// returned. + /// + /// As much funds up to `value` will be moved as possible. If this is less than `value`, then + /// `Ok(Some(remaining))` will be returned. Full completion is given by `Ok(None)`. + pub fn repatriate_reserved( + slashed: &T::AccountId, + beneficiary: &T::AccountId, + value: T::Balance + ) -> result::Result, &'static str> { + if Self::total_balance(beneficiary).is_zero() { + return Err("beneficiary account must pre-exist"); + } + let b = Self::reserved_balance(slashed); + let slash = cmp::min(b, value); + Self::set_free_balance(beneficiary, Self::free_balance(beneficiary) + slash); + Self::set_reserved_balance(slashed, b - slash); + if value == slash { + Ok(None) + } else { + Ok(Some(value - slash)) + } + } + + fn enum_set_size() -> T::AccountIndex { + T::AccountIndex::sa(ENUM_SET_SIZE) + } + + /// Register a new account (with existential balance). + fn new_account(who: &T::AccountId, balance: T::Balance) -> NewAccountOutcome { + let enum_set_size = Self::enum_set_size(); + let next_set_index = Self::next_enum_set(); + let reclaim_index_magic = T::AccountIndex::sa(RECLAIM_INDEX_MAGIC); + let reclaim_index_modulus = T::AccountIndex::sa(256usize); + let quantization = T::AccountIndex::sa(256usize); + + // A little easter-egg for reclaiming dead indexes.. + let ret = { + // we quantise the number of accounts so it stays constant over a reasonable + // period of time. + let quantized_account_count: T::AccountIndex = (next_set_index * enum_set_size / quantization + One::one()) * quantization; + // then modify the starting balance to be modulo this to allow it to potentially + // identify an account index for reuse. + let maybe_try_index = balance % >::sa(quantized_account_count * reclaim_index_modulus); + let maybe_try_index = As::::as_(maybe_try_index); + + // this identifier must end with magic byte 0x69 to trigger this check (a minor + // optimisation to ensure we don't check most unintended account creations). + if maybe_try_index % reclaim_index_modulus == reclaim_index_magic { + // reuse is probably intended. first, remove magic byte. + let try_index = maybe_try_index / reclaim_index_modulus; + + // then check to see if this balance identifies a dead account index. + let set_index = try_index / enum_set_size; + let mut try_set = Self::enum_set(set_index); + let item_index = (try_index % enum_set_size).as_(); + if item_index < try_set.len() { + if Self::total_balance(&try_set[item_index]).is_zero() { + // yup - this index refers to a dead account. can be reused. + try_set[item_index] = who.clone(); + >::insert(set_index, try_set); + + Self::deposit_event(RawEvent::NewAccount(who.clone(), try_index, NewAccountOutcome::GoodHint)); + + return NewAccountOutcome::GoodHint + } + } + NewAccountOutcome::BadHint + } else { + NewAccountOutcome::NoHint + } + }; + + // insert normally as a back up + let mut set_index = next_set_index; + // defensive only: this loop should never iterate since we keep NextEnumSet up to date later. + let mut set = loop { + let set = Self::enum_set(set_index); + if set.len() < ENUM_SET_SIZE { + break set; + } + set_index += One::one(); + }; + + let index = T::AccountIndex::sa(set_index.as_() * ENUM_SET_SIZE + set.len()); + + // update set. + set.push(who.clone()); + + // keep NextEnumSet up to date + if set.len() == ENUM_SET_SIZE { + >::put(set_index + One::one()); + } + + // write set. + >::insert(set_index, set); + + Self::deposit_event(RawEvent::NewAccount(who.clone(), index, ret)); + + ret + } + + fn reap_account(who: &T::AccountId) { + >::remove(who); + Self::deposit_event(RawEvent::ReapedAccount(who.clone())); + } + + /// Kill an account's free portion. + fn on_free_too_low(who: &T::AccountId) { + Self::decrease_total_stake_by(Self::free_balance(who)); + >::remove(who); + + T::OnFreeBalanceZero::on_free_balance_zero(who); + + if Self::reserved_balance(who).is_zero() { + Self::reap_account(who); + } + } + + /// Kill an account's reserved portion. + fn on_reserved_too_low(who: &T::AccountId) { + Self::decrease_total_stake_by(Self::reserved_balance(who)); + >::remove(who); + + if Self::free_balance(who).is_zero() { + Self::reap_account(who); + } + } + + /// Increase TotalIssuance by Value. + pub fn increase_total_stake_by(value: T::Balance) { + if let Some(v) = >::total_issuance().checked_add(&value) { + >::put(v); + } + } + /// Decrease TotalIssuance by Value. + pub fn decrease_total_stake_by(value: T::Balance) { + if let Some(v) = >::total_issuance().checked_sub(&value) { + >::put(v); + } + } +} + +impl OnFinalise for Module { + fn on_finalise(_n: T::BlockNumber) { + } +} + +impl Lookup for Module { + type Source = address::Address; + type Target = T::AccountId; + fn lookup(a: Self::Source) -> result::Result { + match a { + address::Address::Id(i) => Ok(i), + address::Address::Index(i) => >::lookup_index(i).ok_or("invalid account index"), + } + } +} + +impl MakePayment for Module { + fn make_payment(transactor: &T::AccountId, encoded_len: usize) -> Result { + let b = Self::free_balance(transactor); + let transaction_fee = Self::transaction_base_fee() + Self::transaction_byte_fee() * >::sa(encoded_len as u64); + if b < transaction_fee + Self::existential_deposit() { + return Err("not enough funds for transaction fee"); + } + Self::set_free_balance(transactor, b - transaction_fee); + Self::decrease_total_stake_by(transaction_fee); + Ok(()) + } +} diff --git a/runtime/balances/src/mock.rs b/runtime/balances/src/mock.rs new file mode 100644 index 0000000000000..d85a27715819d --- /dev/null +++ b/runtime/balances/src/mock.rs @@ -0,0 +1,77 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Test utilities + +#![cfg(test)] + +use primitives::BuildStorage; +use primitives::testing::{Digest, Header}; +use substrate_primitives::{H256, Blake2Hasher}; +use runtime_io; +use {GenesisConfig, Module, Trait, system}; + +impl_outer_origin!{ + pub enum Origin for Runtime {} +} + +// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. +#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] +pub struct Runtime; +impl system::Trait for Runtime { + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = ::primitives::traits::BlakeTwo256; + type Digest = Digest; + type AccountId = u64; + type Header = Header; + type Event = (); +} +impl Trait for Runtime { + type Balance = u64; + type AccountIndex = u64; + type OnFreeBalanceZero = (); + type EnsureAccountLiquid = (); + type Event = (); +} + +pub fn new_test_ext(ext_deposit: u64, monied: bool) -> runtime_io::TestExternalities { + let mut t = system::GenesisConfig::::default().build_storage().unwrap(); + let balance_factor = if ext_deposit > 0 { + 256 + } else { + 1 + }; + t.extend(GenesisConfig::{ + balances: if monied { + vec![(1, 10 * balance_factor), (2, 20 * balance_factor), (3, 30 * balance_factor), (4, 40 * balance_factor)] + } else { + vec![(10, balance_factor), (20, balance_factor)] + }, + transaction_base_fee: 0, + transaction_byte_fee: 0, + existential_deposit: ext_deposit, + transfer_fee: 0, + creation_fee: 0, + reclaim_rebate: 0, + }.build_storage().unwrap()); + t.into() +} + +pub type System = system::Module; +pub type Balances = Module; diff --git a/runtime/balances/src/tests.rs b/runtime/balances/src/tests.rs new file mode 100644 index 0000000000000..7279474a1f9e0 --- /dev/null +++ b/runtime/balances/src/tests.rs @@ -0,0 +1,324 @@ +// Copyright 2017 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 runtime_io::with_externalities; +use mock::{Balances, System, Runtime, new_test_ext}; + +#[test] +fn reward_should_work() { + with_externalities(&mut new_test_ext(0, true), || { + assert_eq!(Balances::total_balance(&1), 10); + assert_ok!(Balances::reward(&1, 10)); + assert_eq!(Balances::total_balance(&1), 20); + assert_eq!(>::get(), 110); + }); +} + +#[test] +fn indexing_lookup_should_work() { + with_externalities(&mut new_test_ext(10, true), || { + assert_eq!(Balances::lookup_index(0), Some(1)); + assert_eq!(Balances::lookup_index(1), Some(2)); + assert_eq!(Balances::lookup_index(2), Some(3)); + assert_eq!(Balances::lookup_index(3), Some(4)); + assert_eq!(Balances::lookup_index(4), None); + }); +} + +#[test] +fn default_indexing_on_new_accounts_should_work() { + with_externalities(&mut new_test_ext(10, true), || { + assert_eq!(Balances::lookup_index(4), None); + assert_ok!(Balances::transfer(Some(1).into(), 5.into(), 10)); + assert_eq!(Balances::lookup_index(4), Some(5)); + }); +} + +#[test] +fn dust_account_removal_should_work() { + with_externalities(&mut new_test_ext(256 * 10, true), || { + System::inc_account_nonce(&2); + assert_eq!(System::account_nonce(&2), 1); + assert_eq!(Balances::total_balance(&2), 256 * 20); + + assert_ok!(Balances::transfer(Some(2).into(), 5.into(), 256 * 10 + 1)); // index 1 (account 2) becomes zombie + assert_eq!(Balances::total_balance(&2), 0); + assert_eq!(Balances::total_balance(&5), 256 * 10 + 1); + assert_eq!(System::account_nonce(&2), 0); + }); +} + +#[test] +fn reclaim_indexing_on_new_accounts_should_work() { + with_externalities(&mut new_test_ext(256 * 1, true), || { + assert_eq!(Balances::lookup_index(1), Some(2)); + assert_eq!(Balances::lookup_index(4), None); + assert_eq!(Balances::total_balance(&2), 256 * 20); + + assert_ok!(Balances::transfer(Some(2).into(), 5.into(), 256 * 20)); // account 2 becomes zombie freeing index 1 for reclaim) + assert_eq!(Balances::total_balance(&2), 0); + + assert_ok!(Balances::transfer(Some(5).into(), 6.into(), 256 * 1 + 0x69)); // account 6 takes index 1. + assert_eq!(Balances::total_balance(&6), 256 * 1 + 0x69); + assert_eq!(Balances::lookup_index(1), Some(6)); + }); +} + +#[test] +fn reserved_balance_should_prevent_reclaim_count() { + with_externalities(&mut new_test_ext(256 * 1, true), || { + System::inc_account_nonce(&2); + assert_eq!(Balances::lookup_index(1), Some(2)); + assert_eq!(Balances::lookup_index(4), None); + assert_eq!(Balances::total_balance(&2), 256 * 20); + + assert_ok!(Balances::reserve(&2, 256 * 19 + 1)); // account 2 becomes mostly reserved + assert_eq!(Balances::free_balance(&2), 0); // "free" account deleted." + assert_eq!(Balances::total_balance(&2), 256 * 19 + 1); // reserve still exists. + assert_eq!(System::account_nonce(&2), 1); + + assert_ok!(Balances::transfer(Some(4).into(), 5.into(), 256 * 1 + 0x69)); // account 4 tries to take index 1 for account 5. + assert_eq!(Balances::total_balance(&5), 256 * 1 + 0x69); + assert_eq!(Balances::lookup_index(1), Some(2)); // but fails. + assert_eq!(System::account_nonce(&2), 1); + + assert_eq!(Balances::slash(&2, 256 * 18 + 2), None); // account 2 gets slashed + assert_eq!(Balances::total_balance(&2), 0); // "free" account deleted." + assert_eq!(System::account_nonce(&2), 0); + + assert_ok!(Balances::transfer(Some(4).into(), 6.into(), 256 * 1 + 0x69)); // account 4 tries to take index 1 again for account 6. + assert_eq!(Balances::total_balance(&6), 256 * 1 + 0x69); + assert_eq!(Balances::lookup_index(1), Some(6)); // and succeeds. + }); +} + +#[test] +fn balance_works() { + with_externalities(&mut new_test_ext(0, false), || { + Balances::set_free_balance(&1, 42); + assert_eq!(Balances::free_balance(&1), 42); + assert_eq!(Balances::reserved_balance(&1), 0); + assert_eq!(Balances::total_balance(&1), 42); + assert_eq!(Balances::free_balance(&2), 0); + assert_eq!(Balances::reserved_balance(&2), 0); + assert_eq!(Balances::total_balance(&2), 0); + }); +} + +#[test] +fn balance_transfer_works() { + with_externalities(&mut new_test_ext(0, false), || { + Balances::set_free_balance(&1, 111); + Balances::increase_total_stake_by(111); + assert_ok!(Balances::transfer(Some(1).into(), 2.into(), 69)); + assert_eq!(Balances::total_balance(&1), 42); + assert_eq!(Balances::total_balance(&2), 69); + }); +} + +#[test] +fn reserving_balance_should_work() { + with_externalities(&mut new_test_ext(0, false), || { + Balances::set_free_balance(&1, 111); + + assert_eq!(Balances::total_balance(&1), 111); + assert_eq!(Balances::free_balance(&1), 111); + assert_eq!(Balances::reserved_balance(&1), 0); + + assert_ok!(Balances::reserve(&1, 69)); + + assert_eq!(Balances::total_balance(&1), 111); + assert_eq!(Balances::free_balance(&1), 42); + assert_eq!(Balances::reserved_balance(&1), 69); + }); +} + +#[test] +fn balance_transfer_when_reserved_should_not_work() { + with_externalities(&mut new_test_ext(0, false), || { + Balances::set_free_balance(&1, 111); + assert_ok!(Balances::reserve(&1, 69)); + assert_noop!(Balances::transfer(Some(1).into(), 2.into(), 69), "balance too low to send value"); + }); +} + +#[test] +fn deducting_balance_should_work() { + with_externalities(&mut new_test_ext(0, false), || { + Balances::set_free_balance(&1, 111); + assert_ok!(Balances::reserve(&1, 69)); + assert_eq!(Balances::free_balance(&1), 42); + }); +} + +#[test] +fn refunding_balance_should_work() { + with_externalities(&mut new_test_ext(0, false), || { + Balances::set_free_balance(&1, 42); + Balances::set_reserved_balance(&1, 69); + Balances::unreserve(&1, 69); + assert_eq!(Balances::free_balance(&1), 111); + assert_eq!(Balances::reserved_balance(&1), 0); + }); +} + +#[test] +fn slashing_balance_should_work() { + with_externalities(&mut new_test_ext(0, false), || { + Balances::set_free_balance(&1, 111); + Balances::increase_total_stake_by(111); + assert_ok!(Balances::reserve(&1, 69)); + assert!(Balances::slash(&1, 69).is_none()); + assert_eq!(Balances::free_balance(&1), 0); + assert_eq!(Balances::reserved_balance(&1), 42); + assert_eq!(>::get(), 44); + }); +} + +#[test] +fn slashing_incomplete_balance_should_work() { + with_externalities(&mut new_test_ext(0, false), || { + Balances::set_free_balance(&1, 42); + Balances::increase_total_stake_by(42); + assert_ok!(Balances::reserve(&1, 21)); + assert!(Balances::slash(&1, 69).is_some()); + assert_eq!(Balances::free_balance(&1), 0); + assert_eq!(Balances::reserved_balance(&1), 0); + assert_eq!(>::get(), 2); + }); +} + +#[test] +fn unreserving_balance_should_work() { + with_externalities(&mut new_test_ext(0, false), || { + Balances::set_free_balance(&1, 111); + assert_ok!(Balances::reserve(&1, 111)); + Balances::unreserve(&1, 42); + assert_eq!(Balances::reserved_balance(&1), 69); + assert_eq!(Balances::free_balance(&1), 42); + }); +} + +#[test] +fn slashing_reserved_balance_should_work() { + with_externalities(&mut new_test_ext(0, false), || { + Balances::set_free_balance(&1, 111); + Balances::increase_total_stake_by(111); + assert_ok!(Balances::reserve(&1, 111)); + assert!(Balances::slash_reserved(&1, 42).is_none()); + assert_eq!(Balances::reserved_balance(&1), 69); + assert_eq!(Balances::free_balance(&1), 0); + assert_eq!(>::get(), 71); + }); +} + +#[test] +fn slashing_incomplete_reserved_balance_should_work() { + with_externalities(&mut new_test_ext(0, false), || { + Balances::set_free_balance(&1, 111); + Balances::increase_total_stake_by(111); + assert_ok!(Balances::reserve(&1, 42)); + assert!(Balances::slash_reserved(&1, 69).is_some()); + assert_eq!(Balances::free_balance(&1), 69); + assert_eq!(Balances::reserved_balance(&1), 0); + assert_eq!(>::get(), 71); + }); +} + +#[test] +fn transferring_reserved_balance_should_work() { + with_externalities(&mut new_test_ext(0, false), || { + Balances::set_free_balance(&1, 110); + Balances::set_free_balance(&2, 1); + assert_ok!(Balances::reserve(&1, 110)); + assert_ok!(Balances::repatriate_reserved(&1, &2, 41), None); + assert_eq!(Balances::reserved_balance(&1), 69); + assert_eq!(Balances::free_balance(&1), 0); + assert_eq!(Balances::reserved_balance(&2), 0); + assert_eq!(Balances::free_balance(&2), 42); + }); +} + +#[test] +fn transferring_reserved_balance_to_nonexistent_should_fail() { + with_externalities(&mut new_test_ext(0, false), || { + Balances::set_free_balance(&1, 111); + assert_ok!(Balances::reserve(&1, 111)); + assert_noop!(Balances::repatriate_reserved(&1, &2, 42), "beneficiary account must pre-exist"); + }); +} + +#[test] +fn transferring_incomplete_reserved_balance_should_work() { + with_externalities(&mut new_test_ext(0, false), || { + Balances::set_free_balance(&1, 110); + Balances::set_free_balance(&2, 1); + assert_ok!(Balances::reserve(&1, 41)); + assert!(Balances::repatriate_reserved(&1, &2, 69).unwrap().is_some()); + assert_eq!(Balances::reserved_balance(&1), 0); + assert_eq!(Balances::free_balance(&1), 69); + assert_eq!(Balances::reserved_balance(&2), 0); + assert_eq!(Balances::free_balance(&2), 42); + }); +} + +#[test] +fn transferring_too_high_value_should_not_panic() { + with_externalities(&mut new_test_ext(0, false), || { + >::insert(1, u64::max_value()); + >::insert(2, 1); + + assert_err!( + Balances::transfer(Some(1).into(), 2.into(), u64::max_value()), + "destination balance too high to receive value" + ); + + assert_eq!(Balances::free_balance(&1), u64::max_value()); + assert_eq!(Balances::free_balance(&2), 1); + }); +} + +#[test] +fn account_removal_on_free_too_low() { + with_externalities(&mut new_test_ext(100, false), || { + // Setup two accounts with free balance above the exsistential threshold. + { + Balances::set_free_balance(&1, 110); + Balances::increase_total_stake_by(110); + + Balances::set_free_balance(&2, 110); + Balances::increase_total_stake_by(110); + + assert_eq!(>::get(), 732); + } + + // Transfer funds from account 1 of such amount that after this transfer + // the balance of account 1 will be below the exsistential threshold. + // This should lead to the removal of all balance of this account. + assert_ok!(Balances::transfer(Some(1).into(), 2.into(), 20)); + + // Verify free balance removal of account 1. + assert_eq!(Balances::free_balance(&1), 0); + + // Verify that TotalIssuance tracks balance removal when free balance is too low. + assert_eq!(>::get(), 642); + }); +} diff --git a/runtime/consensus/Cargo.toml b/runtime/consensus/Cargo.toml new file mode 100644 index 0000000000000..6b2e11c6c8e5f --- /dev/null +++ b/runtime/consensus/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "substrate-runtime-consensus" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +hex-literal = "0.1.0" +serde = { version = "1.0", default_features = false } +serde_derive = { version = "1.0", optional = true } +substrate-codec = { path = "../../core/codec", default_features = false } +substrate-codec-derive = { path = "../../core/codec/derive", default_features = false } +substrate-primitives = { path = "../../core/primitives", default_features = false } +substrate-runtime-std = { path = "../../core/runtime-std", default_features = false } +substrate-runtime-io = { path = "../../core/runtime-io", default_features = false } +substrate-runtime-support = { path = "../support", default_features = false } +substrate-runtime-primitives = { path = "../primitives", default_features = false } +substrate-runtime-system = { path = "../system", default_features = false } + +[features] +default = ["std"] +std = [ + "serde/std", + "serde_derive", + "substrate-codec/std", + "substrate-codec-derive/std", + "substrate-primitives/std", + "substrate-runtime-std/std", + "substrate-runtime-io/std", + "substrate-runtime-support/std", + "substrate-runtime-primitives/std", + "substrate-runtime-system/std", +] diff --git a/runtime/consensus/src/lib.rs b/runtime/consensus/src/lib.rs new file mode 100644 index 0000000000000..07ff10d0714b6 --- /dev/null +++ b/runtime/consensus/src/lib.rs @@ -0,0 +1,267 @@ +// Copyright 2017 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 . + +//! Conensus module for runtime; manages the authority set ready for the native code. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[allow(unused_imports)] +#[macro_use] +extern crate substrate_runtime_std as rstd; + +#[macro_use] +extern crate substrate_runtime_support as runtime_support; + +#[cfg(feature = "std")] +extern crate serde; + +#[cfg(feature = "std")] +#[macro_use] +extern crate serde_derive; + +#[macro_use] +extern crate substrate_codec_derive; + +extern crate substrate_runtime_io as runtime_io; +extern crate substrate_runtime_primitives as primitives; +extern crate substrate_codec as codec; +extern crate substrate_runtime_system as system; +extern crate substrate_primitives; + +use rstd::prelude::*; +use runtime_support::{storage, Parameter}; +use runtime_support::dispatch::Result; +use runtime_support::storage::StorageValue; +use runtime_support::storage::unhashed::StorageVec; +use primitives::traits::{MaybeSerializeDebug, OnFinalise, Member, DigestItem}; +use primitives::bft::MisbehaviorReport; +use system::{ensure_signed, ensure_inherent, ensure_root}; + +#[cfg(any(feature = "std", test))] +use substrate_primitives::Blake2Hasher; +#[cfg(any(feature = "std", test))] +use std::collections::HashMap; + +pub const AUTHORITY_AT: &'static [u8] = b":auth:"; +pub const AUTHORITY_COUNT: &'static [u8] = b":auth:len"; + +struct AuthorityStorageVec(rstd::marker::PhantomData); +impl StorageVec for AuthorityStorageVec { + type Item = S; + const PREFIX: &'static [u8] = AUTHORITY_AT; +} + +pub const CODE: &'static [u8] = b":code"; + +pub type KeyValue = (Vec, Vec); + +pub trait OnOfflineValidator { + fn on_offline_validator(validator_index: usize); +} + +impl OnOfflineValidator for () { + fn on_offline_validator(_validator_index: usize) {} +} + +pub type Log = RawLog< + ::SessionKey, +>; + +/// A logs in this module. +#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] +#[derive(Encode, Decode, PartialEq, Eq, Clone)] +pub enum RawLog { + /// Authorities set has been changed. Contains the new set of authorities. + AuthoritiesChange(Vec), +} + +impl DigestItem for RawLog { + type AuthorityId = SessionKey; + + /// Try to cast the log entry as AuthoritiesChange log entry. + fn as_authorities_change(&self) -> Option<&[SessionKey]> { + match *self { + RawLog::AuthoritiesChange(ref item) => Some(&item), + } + } +} + +// Implementation for tests outside of this crate. +impl From> for u64 { + fn from(log: RawLog) -> u64 { + match log { + RawLog::AuthoritiesChange(_) => 1, + } + } +} + +pub trait Trait: system::Trait { + /// The allowed extrinsic position for `note_offline` inherent. + const NOTE_OFFLINE_POSITION: u32; + + /// Type for all log entries of this module. + type Log: From> + Into>; + + type SessionKey: Parameter + Default + MaybeSerializeDebug; + type OnOfflineValidator: OnOfflineValidator; +} + +decl_storage! { + trait Store for Module as Consensus { + // Authorities set actual at the block execution start. IsSome only if + // the set has been changed. + OriginalAuthorities: Vec; + } +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + fn report_misbehavior(origin, report: MisbehaviorReport) -> Result; + fn note_offline(origin, offline_val_indices: Vec) -> Result; + fn remark(origin, remark: Vec) -> Result; + fn set_code(origin, new: Vec) -> Result; + fn set_storage(origin, items: Vec) -> Result; + } +} + +impl Module { + /// Get the current set of authorities. These are the session keys. + pub fn authorities() -> Vec { + AuthorityStorageVec::::items() + } + + /// Set the new code. + fn set_code(origin: T::Origin, new: Vec) -> Result { + ensure_root(origin)?; + storage::unhashed::put_raw(CODE, &new); + Ok(()) + } + + /// Set some items of storage. + fn set_storage(origin: T::Origin, items: Vec) -> Result { + ensure_root(origin)?; + for i in &items { + storage::unhashed::put_raw(&i.0, &i.1); + } + Ok(()) + } + + /// Report some misbehaviour. + fn report_misbehavior(origin: T::Origin, _report: MisbehaviorReport) -> Result { + ensure_signed(origin)?; + // TODO. + Ok(()) + } + + /// Note the previous block's validator missed their opportunity to propose a block. This only comes in + /// if 2/3+1 of the validators agree that no proposal was submitted. It's only relevant + /// for the previous block. + fn note_offline(origin: T::Origin, offline_val_indices: Vec) -> Result { + ensure_inherent(origin)?; + assert!( + >::extrinsic_index() == Some(T::NOTE_OFFLINE_POSITION), + "note_offline extrinsic must be at position {} in the block", + T::NOTE_OFFLINE_POSITION + ); + + for validator_index in offline_val_indices.into_iter() { + T::OnOfflineValidator::on_offline_validator(validator_index as usize); + } + + Ok(()) + } + + /// Make some on-chain remark. + fn remark(origin: T::Origin, _remark: Vec) -> Result { + ensure_signed(origin)?; + Ok(()) + } + + /// Set the current set of authorities' session keys. + /// + /// Called by `next_session` only. + pub fn set_authorities(authorities: &[T::SessionKey]) { + let current_authorities = AuthorityStorageVec::::items(); + if current_authorities != authorities { + Self::save_original_authorities(Some(current_authorities)); + AuthorityStorageVec::::set_items(authorities); + } + } + + /// Set a single authority by index. + pub fn set_authority(index: u32, key: &T::SessionKey) { + let current_authority = AuthorityStorageVec::::item(index); + if current_authority != *key { + Self::save_original_authorities(None); + AuthorityStorageVec::::set_item(index, key); + } + } + + /// Save original authorities set. + fn save_original_authorities(current_authorities: Option>) { + if OriginalAuthorities::::get().is_some() { + // if we have already saved original set before, do not overwrite + return; + } + + >::put(current_authorities.unwrap_or_else(|| + AuthorityStorageVec::::items())); + } +} + +/// Finalization hook for the consensus module. +impl OnFinalise for Module { + fn on_finalise(_n: T::BlockNumber) { + if let Some(_) = >::take() { + // TODO: call Self::deposit_log + } + } +} + +#[cfg(any(feature = "std", test))] +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +#[serde(deny_unknown_fields)] +pub struct GenesisConfig { + pub authorities: Vec, + #[serde(with = "substrate_primitives::bytes")] + pub code: Vec, +} + +#[cfg(any(feature = "std", test))] +impl Default for GenesisConfig { + fn default() -> Self { + GenesisConfig { + authorities: vec![], + code: vec![], + } + } +} + +#[cfg(any(feature = "std", test))] +impl primitives::BuildStorage for GenesisConfig +{ + fn build_storage(self) -> ::std::result::Result, Vec>, String> { + use codec::{Encode, KeyedVec}; + let auth_count = self.authorities.len() as u32; + let mut r: runtime_io::TestExternalities = self.authorities.into_iter().enumerate().map(|(i, v)| + ((i as u32).to_keyed_vec(AUTHORITY_AT), v.encode()) + ).collect(); + r.insert(AUTHORITY_COUNT.to_vec(), auth_count.encode()); + r.insert(CODE.to_vec(), self.code); + Ok(r.into()) + } +} diff --git a/runtime/contract/Cargo.toml b/runtime/contract/Cargo.toml new file mode 100644 index 0000000000000..0eef76fabb6a1 --- /dev/null +++ b/runtime/contract/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "substrate-runtime-contract" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +serde = { version = "1.0", default_features = false } +serde_derive = { version = "1.0", optional = true } +parity-wasm = { version = "0.31", default_features = false } +pwasm-utils = { version = "0.3", default_features = false } +substrate-codec = { path = "../../core/codec", default_features = false } +substrate-primitives = { path = "../../core/primitives", default_features = false } +substrate-runtime-sandbox = { path = "../../core/runtime-sandbox", default_features = false } +substrate-runtime-primitives = { path = "../../runtime/primitives", default_features = false } +substrate-runtime-io = { path = "../../core/runtime-io", default_features = false } +substrate-runtime-std = { path = "../../core/runtime-std", default_features = false } +substrate-runtime-support = { path = "../support", default_features = false } +substrate-runtime-system = { path = "../../runtime/system", default_features = false } +substrate-runtime-balances = { path = "../balances", default_features = false } + +[dev-dependencies] +wabt = "0.4" +assert_matches = "1.1" + +[features] +default = ["std"] +std = [ + "serde_derive", + "serde/std", + "substrate-codec/std", + "substrate-primitives/std", + "substrate-runtime-primitives/std", + "substrate-runtime-io/std", + "substrate-runtime-std/std", + "substrate-runtime-balances/std", + "substrate-runtime-sandbox/std", + "substrate-runtime-support/std", + "substrate-runtime-system/std", + "parity-wasm/std", + "pwasm-utils/std", +] diff --git a/runtime/contract/src/account_db.rs b/runtime/contract/src/account_db.rs new file mode 100644 index 0000000000000..d7ff094003c97 --- /dev/null +++ b/runtime/contract/src/account_db.rs @@ -0,0 +1,180 @@ +// Copyright 2018 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 . + +//! Auxilliaries to help with managing partial changes to accounts state. + +use super::{CodeOf, StorageOf, Trait}; +use double_map::StorageDoubleMap; +use rstd::cell::RefCell; +use rstd::collections::btree_map::{BTreeMap, Entry}; +use rstd::prelude::*; +use runtime_support::StorageMap; +use {balances, system}; + +pub struct ChangeEntry { + balance: Option, + code: Option>, + storage: BTreeMap, Option>>, +} + +// Cannot derive(Default) since it erroneously bounds T by Default. +impl Default for ChangeEntry { + fn default() -> Self { + ChangeEntry { + balance: Default::default(), + code: Default::default(), + storage: Default::default(), + } + } +} + +pub type ChangeSet = BTreeMap<::AccountId, ChangeEntry>; + +pub trait AccountDb { + fn get_storage(&self, account: &T::AccountId, location: &[u8]) -> Option>; + fn get_code(&self, account: &T::AccountId) -> Vec; + fn get_balance(&self, account: &T::AccountId) -> T::Balance; + + fn commit(&mut self, change_set: ChangeSet); +} + +pub struct DirectAccountDb; +impl AccountDb for DirectAccountDb { + fn get_storage(&self, account: &T::AccountId, location: &[u8]) -> Option> { + >::get(account.clone(), location.to_vec()) + } + fn get_code(&self, account: &T::AccountId) -> Vec { + >::get(account) + } + fn get_balance(&self, account: &T::AccountId) -> T::Balance { + balances::Module::::free_balance(account) + } + fn commit(&mut self, s: ChangeSet) { + for (address, changed) in s.into_iter() { + if let Some(balance) = changed.balance { + if let balances::UpdateBalanceOutcome::AccountKilled = + balances::Module::::set_free_balance_creating(&address, balance) + { + // Account killed. This will ultimately lead to calling `OnFreeBalanceZero` callback + // which will make removal of CodeOf and StorageOf for this account. + // In order to avoid writing over the deleted properties we `continue` here. + continue; + } + } + if let Some(code) = changed.code { + >::insert(&address, &code); + } + for (k, v) in changed.storage.into_iter() { + if let Some(value) = v { + >::insert(address.clone(), k, value); + } else { + >::remove(address.clone(), k); + } + } + } + } +} + +pub struct OverlayAccountDb<'a, T: Trait + 'a> { + local: RefCell>, + underlying: &'a AccountDb, +} +impl<'a, T: Trait> OverlayAccountDb<'a, T> { + pub fn new(underlying: &'a AccountDb) -> OverlayAccountDb<'a, T> { + OverlayAccountDb { + local: RefCell::new(ChangeSet::new()), + underlying, + } + } + + pub fn into_change_set(self) -> ChangeSet { + self.local.into_inner() + } + + pub fn set_storage( + &mut self, + account: &T::AccountId, + location: Vec, + value: Option>, + ) { + self.local + .borrow_mut() + .entry(account.clone()) + .or_insert(Default::default()) + .storage + .insert(location, value); + } + pub fn set_code(&mut self, account: &T::AccountId, code: Vec) { + self.local + .borrow_mut() + .entry(account.clone()) + .or_insert(Default::default()) + .code = Some(code); + } + pub fn set_balance(&mut self, account: &T::AccountId, balance: T::Balance) { + self.local + .borrow_mut() + .entry(account.clone()) + .or_insert(Default::default()) + .balance = Some(balance); + } +} + +impl<'a, T: Trait> AccountDb for OverlayAccountDb<'a, T> { + fn get_storage(&self, account: &T::AccountId, location: &[u8]) -> Option> { + self.local + .borrow() + .get(account) + .and_then(|a| a.storage.get(location)) + .cloned() + .unwrap_or_else(|| self.underlying.get_storage(account, location)) + } + fn get_code(&self, account: &T::AccountId) -> Vec { + self.local + .borrow() + .get(account) + .and_then(|a| a.code.clone()) + .unwrap_or_else(|| self.underlying.get_code(account)) + } + fn get_balance(&self, account: &T::AccountId) -> T::Balance { + self.local + .borrow() + .get(account) + .and_then(|a| a.balance) + .unwrap_or_else(|| self.underlying.get_balance(account)) + } + fn commit(&mut self, s: ChangeSet) { + let mut local = self.local.borrow_mut(); + + for (address, changed) in s.into_iter() { + match local.entry(address) { + Entry::Occupied(e) => { + let mut value = e.into_mut(); + if changed.balance.is_some() { + value.balance = changed.balance; + } + if changed.code.is_some() { + value.code = changed.code; + } + value.storage.extend(changed.storage.into_iter()); + } + Entry::Vacant(e) => { + e.insert(changed); + } + } + } + } +} diff --git a/runtime/contract/src/double_map.rs b/runtime/contract/src/double_map.rs new file mode 100644 index 0000000000000..6867d2a5c697c --- /dev/null +++ b/runtime/contract/src/double_map.rs @@ -0,0 +1,90 @@ +// Copyright 2017 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 implementation of double map backed by storage. +//! +//! This implementation is somewhat specialized to the tracking of the storage of accounts. + +use rstd::prelude::*; +use codec::{Codec, Encode}; +use runtime_support::storage::unhashed; +use runtime_io::{blake2_256, twox_128}; + +/// Returns only a first part of the storage key. +/// +/// Hashed by XX. +fn first_part_of_key(k1: M::Key1) -> [u8; 16] { + let mut raw_prefix = Vec::new(); + raw_prefix.extend(M::PREFIX); + raw_prefix.extend(Encode::encode(&k1)); + twox_128(&raw_prefix) +} + +/// Returns a compound key that consist of the two parts: (prefix, `k1`) and `k2`. +/// +/// The first part is hased by XX and then concatenated with a blake2 hash of `k2`. +fn full_key(k1: M::Key1, k2: M::Key2) -> Vec { + let first_part = first_part_of_key::(k1); + let second_part = blake2_256(&Encode::encode(&k2)); + + let mut k = Vec::new(); + k.extend(&first_part); + k.extend(&second_part); + k +} + +/// An implementation of a map with a two keys. +/// +/// It provides an important ability to efficiently remove all entries +/// that have a common first key. +/// +/// # Mapping of keys to a storage path +/// +/// The storage key (i.e. the key under which the `Value` will be stored) is created from two parts. +/// The first part is a XX hash of a concatenation of the `PREFIX` and `Key1`. And the second part +/// is a blake2 hash of a `Key2`. +/// +/// Blake2 is used for `Key2` is because it will be used as a key for contract's storage and +/// thus will be susceptible for a untrusted input. +pub trait StorageDoubleMap { + type Key1: Codec; + type Key2: Codec; + type Value: Codec + Default; + + const PREFIX: &'static [u8]; + + /// Insert an entry into this map. + fn insert(k1: Self::Key1, k2: Self::Key2, val: Self::Value) { + unhashed::put(&full_key::(k1, k2)[..], &val); + } + + /// Remove an entry from this map. + fn remove(k1: Self::Key1, k2: Self::Key2) { + unhashed::kill(&full_key::(k1, k2)[..]); + } + + /// Get an entry from this map. + /// + /// If there is entry stored under the given keys, returns `None`. + fn get(k1: Self::Key1, k2: Self::Key2) -> Option { + unhashed::get(&full_key::(k1, k2)[..]) + } + + /// Removes all entries that shares the `k1` as the first key. + fn remove_prefix(k1: Self::Key1) { + unhashed::kill_prefix(&first_part_of_key::(k1)) + } +} diff --git a/runtime/contract/src/exec.rs b/runtime/contract/src/exec.rs new file mode 100644 index 0000000000000..ba3e0ff9676ec --- /dev/null +++ b/runtime/contract/src/exec.rs @@ -0,0 +1,266 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +use super::{CodeOf, MaxDepth, ContractAddressFor, Module, Trait}; +use account_db::{AccountDb, OverlayAccountDb}; +use gas::GasMeter; +use vm; + +use rstd::prelude::*; +use runtime_primitives::traits::{Zero, CheckedAdd, CheckedSub}; +use runtime_support::{StorageMap, StorageValue}; +use balances::{self, EnsureAccountLiquid}; + +pub struct CreateReceipt { + pub address: T::AccountId, +} + +pub struct CallReceipt { + pub return_data: Vec, +} + +pub struct ExecutionContext<'a, T: Trait + 'a> { + // typically should be dest + pub self_account: T::AccountId, + pub overlay: OverlayAccountDb<'a, T>, + pub depth: usize, +} + +impl<'a, T: Trait> ExecutionContext<'a, T> { + /// Make a call to the specified address. + pub fn call( + &mut self, + caller: T::AccountId, + dest: T::AccountId, + value: T::Balance, + gas_meter: &mut GasMeter, + data: &[u8], + ) -> Result { + if self.depth == >::get() as usize { + return Err("reached maximum depth, cannot make a call"); + } + + let call_base_fee = >::call_base_fee(); + if gas_meter.charge(call_base_fee).is_out_of_gas() { + return Err("not enough gas to pay base call fee"); + } + + let dest_code = >::get(&dest); + + let (exec_result, change_set) = { + let mut overlay = OverlayAccountDb::new(&self.overlay); + + if value > T::Balance::zero() { + transfer( + gas_meter, + false, + &self.self_account, + &dest, + value, + &mut overlay, + )?; + } + + let mut nested = ExecutionContext { + overlay: overlay, + self_account: dest.clone(), + depth: self.depth + 1, + }; + let exec_result = if !dest_code.is_empty() { + vm::execute( + &dest_code, + data, + &mut CallContext { + ctx: &mut nested, + _caller: caller, + }, + gas_meter, + ).map_err(|_| "vm execute returned error while call")? + } else { + // that was a plain transfer + vm::ExecutionResult { + return_data: Vec::new(), + } + }; + + (exec_result, nested.overlay.into_change_set()) + }; + + self.overlay.commit(change_set); + + Ok(CallReceipt { + return_data: exec_result.return_data, + }) + } + + pub fn create( + &mut self, + caller: T::AccountId, + endowment: T::Balance, + gas_meter: &mut GasMeter, + ctor: &[u8], + data: &[u8], + ) -> Result, &'static str> { + if self.depth == >::get() as usize { + return Err("reached maximum depth, cannot create"); + } + + let create_base_fee = >::create_base_fee(); + if gas_meter.charge(create_base_fee).is_out_of_gas() { + return Err("not enough gas to pay base create fee"); + } + + let dest = T::DetermineContractAddress::contract_address_for(ctor, data, &self.self_account); + if >::exists(&dest) { + // TODO: Is it enough? + return Err("contract already exists"); + } + + let change_set = { + let mut overlay = OverlayAccountDb::new(&self.overlay); + + if endowment > T::Balance::zero() { + transfer( + gas_meter, + true, + &self.self_account, + &dest, + endowment, + &mut overlay, + )?; + } + + let mut nested = ExecutionContext { + overlay: overlay, + self_account: dest.clone(), + depth: self.depth + 1, + }; + let exec_result = { + vm::execute( + ctor, + data, + &mut CallContext { + ctx: &mut nested, + _caller: caller, + }, + gas_meter, + ).map_err(|_| "vm execute returned error while create")? + }; + + nested.overlay.set_code(&dest, exec_result.return_data); + nested.overlay.into_change_set() + }; + + self.overlay.commit(change_set); + + Ok(CreateReceipt { + address: dest, + }) + } +} + +fn transfer( + gas_meter: &mut GasMeter, + contract_create: bool, + transactor: &T::AccountId, + dest: &T::AccountId, + value: T::Balance, + overlay: &mut OverlayAccountDb, +) -> Result<(), &'static str> { + let would_create = overlay.get_balance(transactor).is_zero(); + + let fee: T::Balance = if contract_create { + >::contract_fee() + } else { + if would_create { + >::creation_fee() + } else { + >::transfer_fee() + } + }; + + if gas_meter.charge_by_balance(fee).is_out_of_gas() { + return Err("not enough gas to pay transfer fee"); + } + + let from_balance = overlay.get_balance(transactor); + let new_from_balance = match from_balance.checked_sub(&value) { + Some(b) => b, + None => return Err("balance too low to send value"), + }; + if would_create && value < >::existential_deposit() { + return Err("value too low to create account"); + } + ::EnsureAccountLiquid::ensure_account_liquid(transactor)?; + + let to_balance = overlay.get_balance(dest); + let new_to_balance = match to_balance.checked_add(&value) { + Some(b) => b, + None => return Err("destination balance too high to receive value"), + }; + + if transactor != dest { + overlay.set_balance(transactor, new_from_balance); + overlay.set_balance(dest, new_to_balance); + } + + Ok(()) +} + +struct CallContext<'a, 'b: 'a, T: Trait + 'b> { + ctx: &'a mut ExecutionContext<'b, T>, + _caller: T::AccountId, +} + +impl<'a, 'b: 'a, T: Trait + 'b> vm::Ext for CallContext<'a, 'b, T> { + type T = T; + + fn get_storage(&self, key: &[u8]) -> Option> { + self.ctx.overlay.get_storage(&self.ctx.self_account, key) + } + + fn set_storage(&mut self, key: &[u8], value: Option>) { + self.ctx + .overlay + .set_storage(&self.ctx.self_account, key.to_vec(), value) + } + + fn create( + &mut self, + code: &[u8], + endowment: T::Balance, + gas_meter: &mut GasMeter, + data: &[u8], + ) -> Result, ()> { + let caller = self.ctx.self_account.clone(); + self.ctx + .create(caller, endowment, gas_meter, code, &data) + .map_err(|_| ()) + } + + fn call( + &mut self, + to: &T::AccountId, + value: T::Balance, + gas_meter: &mut GasMeter, + data: &[u8], + ) -> Result { + let caller = self.ctx.self_account.clone(); + self.ctx + .call(caller, to.clone(), value, gas_meter, data) + .map_err(|_| ()) + } +} diff --git a/runtime/contract/src/gas.rs b/runtime/contract/src/gas.rs new file mode 100644 index 0000000000000..9d1978f7a527c --- /dev/null +++ b/runtime/contract/src/gas.rs @@ -0,0 +1,178 @@ +// Copyright 2018 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 {Trait, Module, GasSpent}; +use runtime_primitives::traits::{As, CheckedMul, CheckedSub, Zero}; +use runtime_support::StorageValue; +use balances; + +#[must_use] +#[derive(Debug, PartialEq, Eq)] +pub enum GasMeterResult { + Proceed, + OutOfGas, +} + +impl GasMeterResult { + pub fn is_out_of_gas(&self) -> bool { + match *self { + GasMeterResult::OutOfGas => true, + GasMeterResult::Proceed => false, + } + } +} + +pub struct GasMeter { + limit: T::Gas, + /// Amount of gas left from initial gas limit. Can reach zero. + gas_left: T::Gas, + gas_price: T::Balance, +} +impl GasMeter { + #[cfg(test)] + pub fn with_limit(gas_limit: T::Gas, gas_price: T::Balance) -> GasMeter { + GasMeter { + limit: gas_limit, + gas_left: gas_limit, + gas_price, + } + } + + /// Account for used gas. + /// + /// Returns `OutOfGas` if there is not enough gas or addition of the specified + /// amount of gas has lead to overflow. On success returns `Proceed`. + /// + /// NOTE that `amount` is always consumed, i.e. if there is not enough gas + /// then the counter will be set to zero. + pub fn charge(&mut self, amount: T::Gas) -> GasMeterResult { + let new_value = match self.gas_left.checked_sub(&amount) { + None => None, + Some(val) if val.is_zero() => None, + Some(val) => Some(val), + }; + + // We always consume the gas even if there is not enough gas. + self.gas_left = new_value.unwrap_or_else(Zero::zero); + + match new_value { + Some(_) => GasMeterResult::Proceed, + None => GasMeterResult::OutOfGas, + } + } + + /// Account for used gas expressed in balance units. + /// + /// Same as [`charge`], but amount to be charged is converted from units of balance to + /// units of gas. + /// + /// [`charge`]: #method.charge + pub fn charge_by_balance(&mut self, amount: T::Balance) -> GasMeterResult { + let amount_in_gas: T::Balance = amount / self.gas_price; + let amount_in_gas: T::Gas = >::sa(amount_in_gas); + self.charge(amount_in_gas) + } + + /// Allocate some amount of gas and perform some work with + /// a newly created nested gas meter. + /// + /// Invokes `f` with either the gas meter that has `amount` gas left or + /// with `None`, if this gas meter has not enough gas to allocate given `amount`. + /// + /// All unused gas in the nested gas meter is returned to this gas meter. + pub fn with_nested>) -> R>( + &mut self, + amount: T::Gas, + f: F, + ) -> R { + // NOTE that it is ok to allocate all available gas since it still ensured + // by `charge` that it doesn't reach zero. + if self.gas_left < amount { + f(None) + } else { + self.gas_left = self.gas_left - amount; + let mut nested = GasMeter { + limit: amount, + gas_left: amount, + gas_price: self.gas_price, + }; + + let r = f(Some(&mut nested)); + + self.gas_left = self.gas_left + nested.gas_left; + + r + } + } + + /// Returns how much gas left from the initial budget. + pub fn gas_left(&self) -> T::Gas { + self.gas_left + } + + /// Returns how much gas was spent. + fn spent(&self) -> T::Gas { + self.limit - self.gas_left + } +} + +/// Buy the given amount of gas. +/// +/// Cost is calculated by multiplying the gas cost (taken from the storage) by the `gas_limit`. +/// The funds are deducted from `transactor`. +pub fn buy_gas( + transactor: &T::AccountId, + gas_limit: T::Gas, +) -> Result, &'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(); + if gas_limit > gas_available { + return Err("block gas limit is reached"); + } + + // Buy the specified amount of gas. + let gas_price = >::gas_price(); + let b = >::free_balance(transactor); + let cost = >::as_(gas_limit.clone()) + .checked_mul(&gas_price) + .ok_or("overflow multiplying gas limit by price")?; + if b < cost + >::existential_deposit() { + return Err("not enough funds for transaction fee"); + } + >::set_free_balance(transactor, b - cost); + >::decrease_total_stake_by(cost); + Ok(GasMeter { + limit: gas_limit, + gas_left: gas_limit, + gas_price, + }) +} + +/// Refund the unused gas. +pub fn refund_unused_gas(transactor: &T::AccountId, gas_meter: GasMeter) { + // Increase total spent gas. + // This cannot overflow, since `gas_spent` is never greater than `block_gas_limit`, which + // also has T::Gas type. + let gas_spent = >::gas_spent() + gas_meter.spent(); + >::put(gas_spent); + + // Refund gas left by the price it was bought. + let b = >::free_balance(transactor); + let refund = >::as_(gas_meter.gas_left) * gas_meter.gas_price; + >::set_free_balance(transactor, b + refund); + >::increase_total_stake_by(refund); +} diff --git a/runtime/contract/src/genesis_config.rs b/runtime/contract/src/genesis_config.rs new file mode 100644 index 0000000000000..2d9a50573a672 --- /dev/null +++ b/runtime/contract/src/genesis_config.rs @@ -0,0 +1,54 @@ +// Copyright 2018 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 . + +//! Build the contract module part of the genesis block storage. + +#![cfg(feature = "std")] + +use {Trait, ContractFee, CallBaseFee, CreateBaseFee, GasPrice, MaxDepth, BlockGasLimit}; + +use runtime_primitives; +use runtime_io::{self, twox_128}; +use runtime_support::StorageValue; +use codec::Encode; +use std::collections::HashMap; +use substrate_primitives::Blake2Hasher; + +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +#[serde(deny_unknown_fields)] +pub struct GenesisConfig { + pub contract_fee: T::Balance, + pub call_base_fee: T::Gas, + pub create_base_fee: T::Gas, + pub gas_price: T::Balance, + pub max_depth: u32, + pub block_gas_limit: T::Gas, +} + +impl runtime_primitives::BuildStorage for GenesisConfig { + fn build_storage(self) -> ::std::result::Result, Vec>, String> { + let r: runtime_io::TestExternalities = map![ + twox_128(>::key()).to_vec() => self.contract_fee.encode(), + twox_128(>::key()).to_vec() => self.call_base_fee.encode(), + twox_128(>::key()).to_vec() => self.create_base_fee.encode(), + twox_128(>::key()).to_vec() => self.gas_price.encode(), + twox_128(>::key()).to_vec() => self.max_depth.encode(), + twox_128(>::key()).to_vec() => self.block_gas_limit.encode() + ]; + Ok(r.into()) + } +} diff --git a/runtime/contract/src/lib.rs b/runtime/contract/src/lib.rs new file mode 100644 index 0000000000000..8114d2651607e --- /dev/null +++ b/runtime/contract/src/lib.rs @@ -0,0 +1,278 @@ +// Copyright 2018 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 . + +//! Smart-contract module for runtime; Allows deployment and execution of smart-contracts +//! expressed in WebAssembly. +//! +//! This module provides an ability to create smart-contract accounts and send them messages. +//! A smart-contract is an account with associated code and storage. When such an account receives a message, +//! the code associated with that account gets executed. +//! +//! The code is allowed to alter the storage entries of the associated account, +//! create smart-contracts or send messages to existing smart-contracts. +//! +//! For any actions invoked by the smart-contracts fee must be paid. The fee is paid in gas. +//! Gas is bought upfront up to the, specified in transaction, limit. Any unused gas is refunded +//! after the transaction (regardless of the execution outcome). If all gas is used, +//! then changes made for the specific call or create are reverted (including balance transfers). +//! +//! Failures are typically not cascading. That, for example, means that if contract A calls B and B errors +//! somehow, then A can decide if it should proceed or error. +//! +//! # Interaction with the system +//! +//! ## Finalization +//! +//! This module requires performing some finalization steps at the end of the block. If not performed +//! the module will have incorrect behavior. +//! +//! Call [`Module::execute`] at the end of the block. The order in relation to +//! the other module doesn't matter. +//! +//! ## Account killing +//! +//! When `staking` module determines that account is dead (e.g. account's balance fell below +//! exsistential deposit) then it reaps the account. That will lead to deletion of the associated +//! code and storage of the account. +//! +//! [`Module::execute`]: struct.Module.html#impl-OnFinalise + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(feature = "std")] +#[macro_use] +extern crate serde_derive; + +#[cfg(feature = "std")] +extern crate serde; + +extern crate parity_wasm; +extern crate pwasm_utils; + +extern crate substrate_codec as codec; +extern crate substrate_runtime_io as runtime_io; +extern crate substrate_runtime_sandbox as sandbox; + +#[macro_use] +extern crate substrate_runtime_std as rstd; + +extern crate substrate_runtime_balances as balances; +extern crate substrate_runtime_system as system; + +#[macro_use] +extern crate substrate_runtime_support as runtime_support; + +extern crate substrate_runtime_primitives as runtime_primitives; +extern crate substrate_primitives; + +#[cfg(test)] +#[macro_use] +extern crate assert_matches; + +#[cfg(test)] +extern crate wabt; + +mod account_db; +mod double_map; +mod exec; +mod vm; +mod gas; + +mod genesis_config; + +#[cfg(test)] +mod tests; + +#[cfg(feature = "std")] +pub use genesis_config::GenesisConfig; +use exec::ExecutionContext; +use account_db::{AccountDb, OverlayAccountDb}; +use double_map::StorageDoubleMap; + +use rstd::prelude::*; +use codec::Codec; +use runtime_primitives::traits::{As, SimpleArithmetic, OnFinalise}; +use runtime_support::dispatch::Result; +use runtime_support::{Parameter, StorageMap, StorageValue}; +use system::ensure_signed; + +pub trait Trait: balances::Trait { + /// Function type to get the contract address given the creator. + type DetermineContractAddress: ContractAddressFor; + + // As is needed for wasm-utils + type Gas: Parameter + Default + Codec + SimpleArithmetic + Copy + As + As + As; +} + +pub trait ContractAddressFor { + fn contract_address_for(code: &[u8], data: &[u8], origin: &AccountId) -> AccountId; +} + +decl_module! { + /// Contracts module. + pub struct Module for enum Call where origin: T::Origin { + // TODO: Change AccountId to staking::Address + fn call( + origin, + dest: T::AccountId, + value: T::Balance, + gas_limit: T::Gas, + data: Vec + ) -> Result; + + fn create( + origin, + value: T::Balance, + gas_limit: T::Gas, + ctor: Vec, + data: Vec + ) -> Result; + } +} + +decl_storage! { + trait Store for Module as Contract { + /// The fee required to create a contract. At least as big as staking's ReclaimRebate. + ContractFee get(contract_fee): required T::Balance; + /// The fee charged for a call into a contract. + CallBaseFee get(call_base_fee): required T::Gas; + /// The fee charged for a create of a contract. + CreateBaseFee get(create_base_fee): required T::Gas; + /// The price of one unit of gas. + GasPrice get(gas_price): required T::Balance; + /// The maximum nesting level of a call/create stack. + MaxDepth get(max_depth): required u32; + /// The maximum amount of gas that could be expended per block. + BlockGasLimit get(block_gas_limit): required T::Gas; + /// Gas spent so far in this block. + GasSpent get(gas_spent): default T::Gas; + + /// The code associated with an account. + pub CodeOf: default map [ T::AccountId => Vec ]; // TODO Vec values should be optimised to not do a length prefix. + } +} + +// TODO: consider storing upper-bound for contract's gas limit in fixed-length runtime +// code in contract itself and use that. + +/// The storage items associated with an account/key. +/// +/// TODO: keys should also be able to take AsRef to ensure Vecs can be passed as &[u8] +pub(crate) struct StorageOf(::rstd::marker::PhantomData); +impl double_map::StorageDoubleMap for StorageOf { + const PREFIX: &'static [u8] = b"con:sto:"; + type Key1 = T::AccountId; + type Key2 = Vec; + type Value = Vec; +} + +impl Module { + /// Make a call to a specified account, optionally transferring some balance. + fn call( + origin: ::Origin, + dest: T::AccountId, + value: T::Balance, + gas_limit: T::Gas, + data: Vec, + ) -> Result { + let origin = ensure_signed(origin)?; + + // Pay for the gas upfront. + // + // NOTE: it is very important to avoid any state changes before + // paying for the gas. + let mut gas_meter = gas::buy_gas::(&origin, gas_limit)?; + + let mut ctx = ExecutionContext { + self_account: origin.clone(), + depth: 0, + overlay: OverlayAccountDb::::new(&account_db::DirectAccountDb), + }; + let result = ctx.call(origin.clone(), dest, value, &mut gas_meter, &data); + + if let Ok(_) = result { + // Commit all changes that made it thus far into the persistant storage. + account_db::DirectAccountDb.commit(ctx.overlay.into_change_set()); + } + + // Refund cost of the unused gas. + // + // NOTE: this should go after the commit to the storage, since the storage changes + // can alter the balance of the caller. + gas::refund_unused_gas::(&origin, gas_meter); + + result.map(|_| ()) + } + + /// Create a new contract, optionally transfering some balance to the created account. + /// + /// Creation is executed as follows:ExecutionContext + /// + /// - the destination address is computed based on the sender and hash of the code. + /// - account is created at the computed address. + /// - the `ctor_code` is executed in the context of the newly created account. Buffer returned + /// after the execution is saved as the `code` of the account. That code will be invoked + /// upon any message received by this account. + fn create( + origin: ::Origin, + endowment: T::Balance, + gas_limit: T::Gas, + ctor_code: Vec, + data: Vec, + ) -> Result { + let origin = ensure_signed(origin)?; + + // Pay for the gas upfront. + // + // NOTE: it is very important to avoid any state changes before + // paying for the gas. + let mut gas_meter = gas::buy_gas::(&origin, gas_limit)?; + + let mut ctx = ExecutionContext { + self_account: origin.clone(), + depth: 0, + overlay: OverlayAccountDb::::new(&account_db::DirectAccountDb), + }; + let result = ctx.create(origin.clone(), endowment, &mut gas_meter, &ctor_code, &data); + + if let Ok(_) = result { + // Commit all changes that made it thus far into the persistant storage. + account_db::DirectAccountDb.commit(ctx.overlay.into_change_set()); + } + + // Refund cost of the unused gas. + // + // NOTE: this should go after the commit to the storage, since the storage changes + // can alter the balance of the caller. + gas::refund_unused_gas::(&origin, gas_meter); + + result.map(|_| ()) + } +} + +impl balances::OnFreeBalanceZero for Module { + fn on_free_balance_zero(who: &T::AccountId) { + >::remove(who); + >::remove_prefix(who.clone()); + } +} + +/// Finalization hook for the smart-contract module. +impl OnFinalise for Module { + fn on_finalise(_n: T::BlockNumber) { + >::kill(); + } +} diff --git a/runtime/contract/src/tests.rs b/runtime/contract/src/tests.rs new file mode 100644 index 0000000000000..17f5a4d88aac9 --- /dev/null +++ b/runtime/contract/src/tests.rs @@ -0,0 +1,670 @@ +// Copyright 2018 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 double_map::StorageDoubleMap; +use runtime_io::with_externalities; +use runtime_primitives::testing::{Digest, H256, Header}; +use runtime_primitives::traits::{BlakeTwo256}; +use runtime_primitives::BuildStorage; +use runtime_support::StorageMap; +use substrate_primitives::Blake2Hasher; +use wabt; +use { + runtime_io, balances, system, CodeOf, ContractAddressFor, + GenesisConfig, Module, StorageOf, Trait, +}; + +impl_outer_origin! { + pub enum Origin for Test {} +} + +#[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 Digest = Digest; + type AccountId = u64; + type Header = Header; + type Event = (); +} +impl balances::Trait for Test { + type Balance = u64; + type AccountIndex = u64; + type OnFreeBalanceZero = Contract; + type EnsureAccountLiquid = (); + type Event = (); +} +impl Trait for Test { + type Gas = u64; + type DetermineContractAddress = DummyContractAddressFor; +} + +type Balances = balances::Module; +type Contract = Module; + +pub struct DummyContractAddressFor; +impl ContractAddressFor for DummyContractAddressFor { + fn contract_address_for(_code: &[u8], _data: &[u8], origin: &u64) -> u64 { + origin + 1 + } +} + +struct ExtBuilder { + existential_deposit: u64, + gas_price: u64, + block_gas_limit: u64, +} +impl Default for ExtBuilder { + fn default() -> Self { + Self { + existential_deposit: 0, + gas_price: 2, + block_gas_limit: 100_000_000, + } + } +} +impl ExtBuilder { + fn existential_deposit(mut self, existential_deposit: u64) -> Self { + self.existential_deposit = existential_deposit; + self + } + fn gas_price(mut self, gas_price: u64) -> Self { + self.gas_price = gas_price; + self + } + fn block_gas_limit(mut self, block_gas_limit: u64) -> Self { + self.block_gas_limit = block_gas_limit; + self + } + fn build(self) -> runtime_io::TestExternalities { + let mut t = system::GenesisConfig::::default() + .build_storage() + .unwrap(); + t.extend( + balances::GenesisConfig:: { + balances: vec![], + transaction_base_fee: 0, + transaction_byte_fee: 0, + existential_deposit: self.existential_deposit, + transfer_fee: 0, + creation_fee: 0, + reclaim_rebate: 0, + }.build_storage() + .unwrap(), + ); + t.extend( + GenesisConfig:: { + 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, + }.build_storage() + .unwrap(), + ); + t.into() + } +} + +const CODE_TRANSFER: &str = r#" +(module + ;; ext_call( + ;; callee_ptr: u32, + ;; callee_len: u32, + ;; gas: u64, + ;; value_ptr: u32, + ;; value_len: u32, + ;; input_data_ptr: u32, + ;; input_data_len: u32 + ;; ) -> u32 + (import "env" "ext_call" (func $ext_call (param i32 i32 i64 i32 i32 i32 i32) (result i32))) + (import "env" "memory" (memory 1 1)) + (func (export "call") + (drop + (call $ext_call + (i32.const 4) ;; Pointer to "callee" address. + (i32.const 8) ;; Length of "callee" address. + (i64.const 0) ;; How much gas to devote for the execution. 0 = all. + (i32.const 12) ;; Pointer to the buffer with value to transfer + (i32.const 8) ;; Length of the buffer with value to transfer. + (i32.const 0) ;; Pointer to input data buffer address + (i32.const 0) ;; Length of input data buffer + ) + ) + ) + ;; Destination AccountId to transfer the funds. + ;; Represented by u64 (8 bytes long) in little endian. + (data (i32.const 4) "\09\00\00\00\00\00\00\00") + ;; Amount of value to transfer. + ;; Represented by u64 (8 bytes long) in little endian. + (data (i32.const 12) "\06\00\00\00\00\00\00\00") +) +"#; + +#[test] +fn contract_transfer() { + const CONTRACT_SHOULD_TRANSFER_VALUE: u64 = 6; + const CONTRACT_SHOULD_TRANSFER_TO: u64 = 9; + + let code_transfer = wabt::wat2wasm(CODE_TRANSFER).unwrap(); + + with_externalities(&mut ExtBuilder::default().build(), || { + >::insert(1, code_transfer.to_vec()); + + Balances::set_free_balance(&0, 100_000_000); + Balances::increase_total_stake_by(100_000_000); + Balances::set_free_balance(&1, 11); + Balances::increase_total_stake_by(11); + + assert_ok!(Contract::call(Origin::signed(0), 1, 3, 100_000, Vec::new())); + + assert_eq!( + Balances::free_balance(&0), + // 3 - value sent with the transaction + // 2 * 10 - gas used by the contract (10) multiplied by gas price (2) + // 2 * 135 - base gas fee for call (by transaction) + // 2 * 135 - base gas fee for call (by the contract) + 100_000_000 - 3 - (2 * 10) - (2 * 135) - (2 * 135), + ); + assert_eq!( + Balances::free_balance(&1), + 11 + 3 - CONTRACT_SHOULD_TRANSFER_VALUE, + ); + assert_eq!( + Balances::free_balance(&CONTRACT_SHOULD_TRANSFER_TO), + CONTRACT_SHOULD_TRANSFER_VALUE, + ); + }); +} + +#[test] +fn contract_transfer_oog() { + const CONTRACT_SHOULD_TRANSFER_TO: u64 = 9; + + let code_transfer = wabt::wat2wasm(CODE_TRANSFER).unwrap(); + + with_externalities(&mut ExtBuilder::default().build(), || { + >::insert(1, code_transfer.to_vec()); + + Balances::set_free_balance(&0, 100_000_000); + Balances::increase_total_stake_by(100_000_000); + Balances::set_free_balance(&1, 11); + Balances::increase_total_stake_by(11); + + assert_ok!(Contract::call(Origin::signed(0), 1, 3, 135 + 135 + 7, Vec::new())); + + assert_eq!( + Balances::free_balance(&0), + // 3 - value sent with the transaction + // 2 * 7 - gas used by the contract (7) multiplied by gas price (2) + // 2 * 135 - base gas fee for call (by transaction) + // 2 * 135 - base gas fee for call (by contract) + 100_000_000 - 3 - (2 * 7) - (2 * 135) - (2 * 135), + ); + + // Transaction level transfer should succeed. + assert_eq!(Balances::free_balance(&1), 14); + // But `ext_call` should not. + assert_eq!(Balances::free_balance(&CONTRACT_SHOULD_TRANSFER_TO), 0); + }); +} + +#[test] +fn contract_transfer_max_depth() { + const CONTRACT_SHOULD_TRANSFER_TO: u64 = 9; + + let code_transfer = wabt::wat2wasm(CODE_TRANSFER).unwrap(); + + with_externalities(&mut ExtBuilder::default().build(), || { + >::insert(CONTRACT_SHOULD_TRANSFER_TO, code_transfer.to_vec()); + + Balances::set_free_balance(&0, 100_000_000); + Balances::increase_total_stake_by(100_000_000); + Balances::set_free_balance(&CONTRACT_SHOULD_TRANSFER_TO, 11); + Balances::increase_total_stake_by(11); + + assert_ok!(Contract::call(Origin::signed(0), CONTRACT_SHOULD_TRANSFER_TO, 3, 100_000, Vec::new())); + + assert_eq!( + Balances::free_balance(&0), + // 3 - value sent with the transaction + // 2 * 10 * 100 - gas used by the contract (10) multiplied by gas price (2) + // multiplied by max depth (100). + // 2 * 135 * 100 - base gas fee for call (by transaction) multiplied by max depth (100). + 100_000_000 - 3 - (2 * 10 * 100) - (2 * 135 * 100), + ); + assert_eq!(Balances::free_balance(&CONTRACT_SHOULD_TRANSFER_TO), 14); + }); +} + +/// Convert a byte slice to a string with hex values. +/// +/// Each value is preceeded with a `\` character. +fn escaped_bytestring(bytes: &[u8]) -> String { + use std::fmt::Write; + let mut result = String::new(); + for b in bytes { + write!(result, "\\{:02x}", b).unwrap(); + } + result +} + +/// Create a constructor for the specified code. +/// +/// When constructor is executed, it will call `ext_return` with code that +/// specified in `child_bytecode`. +fn code_ctor(child_bytecode: &[u8]) -> String { + format!( + r#" +(module + ;; ext_return(data_ptr: u32, data_len: u32) -> ! + (import "env" "ext_return" (func $ext_return (param i32 i32))) + (import "env" "memory" (memory 1 1)) + (func (export "call") + (call $ext_return + (i32.const 4) + (i32.const {code_len}) + ) + ;; ext_return is diverging, i.e. doesn't return. + unreachable + ) + (data (i32.const 4) "{escaped_bytecode}") +) +"#, + escaped_bytecode = escaped_bytestring(child_bytecode), + code_len = child_bytecode.len(), + ) +} + +/// Returns code that uses `ext_create` runtime call. +/// +/// Takes bytecode of the contract that needs to be deployed. +fn code_create(constructor: &[u8]) -> String { + format!( + r#" +(module + ;; ext_create( + ;; code_ptr: u32, + ;; code_len: u32, + ;; gas: u64, + ;; value_ptr: u32, + ;; value_len: u32, + ;; input_data_ptr: u32, + ;; input_data_len: u32, + ;; ) -> u32 + (import "env" "ext_create" (func $ext_create (param i32 i32 i64 i32 i32 i32 i32) (result i32))) + (import "env" "memory" (memory 1 1)) + (func (export "call") + (drop + (call $ext_create + (i32.const 12) ;; Pointer to `code` + (i32.const {code_len}) ;; Length of `code` + (i64.const 0) ;; How much gas to devote for the execution. 0 = all. + (i32.const 4) ;; Pointer to the buffer with value to transfer + (i32.const 8) ;; Length of the buffer with value to transfer + (i32.const 0) ;; Pointer to input data buffer address + (i32.const 0) ;; Length of input data buffer + ) + ) + ) + ;; Amount of value to transfer. + ;; Represented by u64 (8 bytes long) in little endian. + (data (i32.const 4) "\03\00\00\00\00\00\00\00") + ;; Embedded wasm code. + (data (i32.const 12) "{escaped_constructor}") +) +"#, + escaped_constructor = escaped_bytestring(constructor), + code_len = constructor.len(), + ) +} + +#[test] +fn contract_create() { + let code_transfer = wabt::wat2wasm(CODE_TRANSFER).unwrap(); + let code_ctor_transfer = wabt::wat2wasm(&code_ctor(&code_transfer)).unwrap(); + let code_create = wabt::wat2wasm(&code_create(&code_ctor_transfer)).unwrap(); + + with_externalities(&mut ExtBuilder::default().build(), || { + Balances::set_free_balance(&0, 100_000_000); + Balances::increase_total_stake_by(100_000_000); + Balances::set_free_balance(&1, 0); + Balances::set_free_balance(&9, 30); + Balances::increase_total_stake_by(30); + + >::insert(1, code_create.to_vec()); + + // When invoked, the contract at address `1` must create a contract with 'transfer' code. + assert_ok!(Contract::call(Origin::signed(0), 1, 11, 100_000, Vec::new())); + + let derived_address = ::DetermineContractAddress::contract_address_for( + &code_ctor_transfer, + &[], + &1, + ); + + // 11 - value sent with the transaction + // 2 * 139 - gas spent by the deployer contract (139) multiplied by gas price (2) + // 2 * 135 - base gas fee for call (top level) + // 2 * 175 - base gas fee for create (by contract) + // ((21 / 2) * 2) - price per account creation + let expected_gas_after_create = + 100_000_000 - 11 - (2 * 139) - (2 * 135) - (2 * 175) - ((21 / 2) * 2); + assert_eq!(Balances::free_balance(&0), expected_gas_after_create); + assert_eq!(Balances::free_balance(&1), 8); + assert_eq!(Balances::free_balance(&derived_address), 3); + + // Initiate transfer to the newly created contract. + assert_ok!(Contract::call(Origin::signed(0), derived_address, 22, 100_000, Vec::new())); + + assert_eq!( + Balances::free_balance(&0), + // 22 - value sent with the transaction + // (2 * 10) - gas used by the contract + // (2 * 135) - base gas fee for call (top level) + // (2 * 135) - base gas fee for call (by transfer contract) + expected_gas_after_create - 22 - (2 * 10) - (2 * 135) - (2 * 135), + ); + assert_eq!(Balances::free_balance(&derived_address), 22 - 3); + assert_eq!(Balances::free_balance(&9), 36); + }); +} + +#[test] +fn top_level_create() { + let code_transfer = wabt::wat2wasm(CODE_TRANSFER).unwrap(); + let code_ctor_transfer = wabt::wat2wasm(&code_ctor(&code_transfer)).unwrap(); + + with_externalities(&mut ExtBuilder::default().gas_price(3).build(), || { + let derived_address = ::DetermineContractAddress::contract_address_for( + &code_ctor_transfer, + &[], + &0, + ); + + Balances::set_free_balance(&0, 100_000_000); + Balances::increase_total_stake_by(100_000_000); + Balances::set_free_balance(&derived_address, 30); + Balances::increase_total_stake_by(30); + + assert_ok!(Contract::create( + Origin::signed(0), + 11, + 100_000, + code_ctor_transfer.clone(), + Vec::new(), + )); + + // 11 - value sent with the transaction + // (3 * 129) - gas spent by the ctor + // (3 * 175) - base gas fee for create (175) (top level) multipled by gas price (3) + // ((21 / 3) * 3) - price for contract creation + assert_eq!( + Balances::free_balance(&0), + 100_000_000 - 11 - (3 * 129) - (3 * 175) - ((21 / 3) * 3) + ); + assert_eq!(Balances::free_balance(&derived_address), 30 + 11); + + assert_eq!(>::get(&derived_address), code_transfer); + }); +} + +const CODE_NOP: &'static str = r#" +(module + (func (export "call") + nop + ) +) +"#; + +#[test] +fn refunds_unused_gas() { + let code_nop = wabt::wat2wasm(CODE_NOP).unwrap(); + + with_externalities(&mut ExtBuilder::default().build(), || { + >::insert(1, code_nop.to_vec()); + + Balances::set_free_balance(&0, 100_000_000); + Balances::increase_total_stake_by(100_000_000); + + assert_ok!(Contract::call(Origin::signed(0), 1, 0, 100_000, Vec::new())); + + assert_eq!(Balances::free_balance(&0), 100_000_000 - 4 - (2 * 135)); + }); +} + +#[test] +fn call_with_zero_value() { + with_externalities(&mut ExtBuilder::default().build(), || { + >::insert(1, vec![]); + + Balances::set_free_balance(&0, 100_000_000); + Balances::increase_total_stake_by(100_000_000); + + assert_ok!(Contract::call(Origin::signed(0), 1, 0, 100_000, Vec::new())); + + assert_eq!(Balances::free_balance(&0), 100_000_000 - (2 * 135)); + }); +} + +#[test] +fn create_with_zero_endowment() { + let code_nop = wabt::wat2wasm(CODE_NOP).unwrap(); + + with_externalities(&mut ExtBuilder::default().build(), || { + Balances::set_free_balance(&0, 100_000_000); + Balances::increase_total_stake_by(100_000_000); + + assert_ok!(Contract::create(Origin::signed(0), 0, 100_000, code_nop, Vec::new())); + + assert_eq!( + Balances::free_balance(&0), + // 4 - for the gas spent by the constructor + // 2 * 175 - base gas fee for create (175) multiplied by gas price (2) (top level) + 100_000_000 - 4 - (2 * 175), + ); + }); +} + +#[test] +fn account_removal_removes_storage() { + with_externalities( + &mut ExtBuilder::default().existential_deposit(100).build(), + || { + // Setup two accounts with free balance above than exsistential threshold. + { + Balances::set_free_balance(&1, 110); + Balances::increase_total_stake_by(110); + >::insert(1, b"foo".to_vec(), b"1".to_vec()); + >::insert(1, b"bar".to_vec(), b"2".to_vec()); + + Balances::set_free_balance(&2, 110); + Balances::increase_total_stake_by(110); + >::insert(2, b"hello".to_vec(), b"3".to_vec()); + >::insert(2, b"world".to_vec(), b"4".to_vec()); + } + + // Transfer funds from account 1 of such amount that after this transfer + // the balance of account 1 is will be below than exsistential threshold. + // + // This should lead to the removal of all storage associated with this account. + assert_ok!(Balances::transfer(Origin::signed(1), 2.into(), 20)); + + // Verify that all entries from account 1 is removed, while + // entries from account 2 is in place. + { + assert_eq!(>::get(1, b"foo".to_vec()), None); + assert_eq!(>::get(1, b"bar".to_vec()), None); + + assert_eq!( + >::get(2, b"hello".to_vec()), + Some(b"3".to_vec()) + ); + assert_eq!( + >::get(2, b"world".to_vec()), + Some(b"4".to_vec()) + ); + } + }, + ); +} + +const CODE_UNREACHABLE: &'static str = r#" +(module + (func (export "call") + nop + unreachable + ) +) +"#; + +#[test] +fn top_level_call_refunds_even_if_fails() { + let code_unreachable = wabt::wat2wasm(CODE_UNREACHABLE).unwrap(); + with_externalities(&mut ExtBuilder::default().gas_price(4).build(), || { + >::insert(1, code_unreachable.to_vec()); + + Balances::set_free_balance(&0, 100_000_000); + Balances::increase_total_stake_by(100_000_000); + + assert_err!( + Contract::call(Origin::signed(0), 1, 0, 100_000, Vec::new()), + "vm execute returned error while call" + ); + + assert_eq!(Balances::free_balance(&0), 100_000_000 - (4 * 3) - (4 * 135)); + }); +} + +const CODE_LOOP: &'static str = r#" +(module + (func (export "call") + (loop + (br 0) + ) + ) +) +"#; + +#[test] +fn block_gas_limit() { + let code_loop = wabt::wat2wasm(CODE_LOOP).unwrap(); + with_externalities( + &mut ExtBuilder::default().block_gas_limit(100_000).build(), + || { + >::insert(1, code_loop.to_vec()); + + Balances::set_free_balance(&0, 100_000_000); + Balances::increase_total_stake_by(100_000_000); + + // Spend 50_000 units of gas (OOG). + assert_err!( + Contract::call(Origin::signed(0), 1, 0, 50_000, Vec::new()), + "vm execute returned error while call" + ); + + // Ensure we can't spend more gas than available in block gas limit. + assert_err!( + Contract::call(Origin::signed(0), 1, 0, 50_001, Vec::new()), + "block gas limit is reached" + ); + + // However, we can spend another 50_000 + assert_err!( + Contract::call(Origin::signed(0), 1, 0, 50_000, Vec::new()), + "vm execute returned error while call" + ); + }, + ); +} + +const CODE_INPUT_DATA: &'static str = r#" +(module + (import "env" "ext_input_size" (func $ext_input_size (result i32))) + (import "env" "ext_input_copy" (func $ext_input_copy (param i32 i32 i32))) + (import "env" "memory" (memory 1 1)) + + (func (export "call") + (block $fail + ;; fail if ext_input_size != 4 + (br_if $fail + (i32.ne + (i32.const 4) + (call $ext_input_size) + ) + ) + + (call $ext_input_copy + (i32.const 0) + (i32.const 0) + (i32.const 4) + ) + + + (br_if $fail + (i32.ne + (i32.load8_u (i32.const 0)) + (i32.const 0) + ) + ) + (br_if $fail + (i32.ne + (i32.load8_u (i32.const 1)) + (i32.const 1) + ) + ) + (br_if $fail + (i32.ne + (i32.load8_u (i32.const 2)) + (i32.const 2) + ) + ) + (br_if $fail + (i32.ne + (i32.load8_u (i32.const 3)) + (i32.const 3) + ) + ) + + (return) + ) + unreachable + ) +) +"#; + +#[test] +fn input_data() { + let code_input_data = wabt::wat2wasm(CODE_INPUT_DATA).unwrap(); + with_externalities( + &mut ExtBuilder::default().build(), + || { + >::insert(1, code_input_data.to_vec()); + + Balances::set_free_balance(&0, 100_000_000); + Balances::increase_total_stake_by(100_000_000); + + assert_ok!(Contract::call(Origin::signed(0), 1, 0, 50_000, vec![0, 1, 2, 3])); + + // all asserts are made within contract code itself. + }, + ); +} diff --git a/runtime/contract/src/vm/env_def/macros.rs b/runtime/contract/src/vm/env_def/macros.rs new file mode 100644 index 0000000000000..40651749eb55b --- /dev/null +++ b/runtime/contract/src/vm/env_def/macros.rs @@ -0,0 +1,285 @@ +// Copyright 2018 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 . + +//! Definition of macros that hides boilerplate of defining external environment +//! for a wasm module. +//! +//! Typically you should use `define_env` macro. + +#[macro_export] +macro_rules! convert_args { + () => (vec![]); + ( $( $t:ty ),* ) => ( vec![ $( { use $crate::vm::env_def::ConvertibleToWasm; <$t>::VALUE_TYPE }, )* ] ); +} + +#[macro_export] +macro_rules! gen_signature { + ( ( $( $params: ty ),* ) ) => ( + { + FunctionType::new(convert_args!($($params),*), None) + } + ); + + ( ( $( $params: ty ),* ) -> $returns: ty ) => ( + { + FunctionType::new(convert_args!($($params),*), Some({ + use $crate::vm::env_def::ConvertibleToWasm; <$returns>::VALUE_TYPE + })) + } + ); +} + +/// Unmarshall arguments and then execute `body` expression and return its result. +macro_rules! unmarshall_then_body { + ( $body:tt, $ctx:ident, $args_iter:ident, $( $names:ident : $params:ty ),* ) => ({ + $( + let $names : <$params as $crate::vm::env_def::ConvertibleToWasm>::NativeType = + $args_iter.next() + .and_then(|v| <$params as $crate::vm::env_def::ConvertibleToWasm> + ::from_typed_value(v.clone())) + .expect( + "precondition: all imports should be checked against the signatures of corresponding + functions defined by `define_env!` macro by the user of the macro; + signatures of these functions defined by `$params`; + calls always made with arguments types of which are defined by the corresponding imports; + thus types of arguments should be equal to type list in `$params` and + length of argument list and $params should be equal; + thus this can never be `None`; + qed; + " + ); + )* + $body + }) +} + +/// Since we can't specify the type of closure directly at binding site: +/// +/// ```rust,ignore +/// let f: FnOnce() -> Result<::NativeType, _> = || { /* ... */ }; +/// ``` +/// +/// we use this function to constrain the type of the closure. +#[inline(always)] +pub fn constrain_closure(f: F) -> F +where + F: FnOnce() -> Result, +{ + f +} + +#[macro_export] +macro_rules! unmarshall_then_body_then_marshall { + ( $args_iter:ident, $ctx:ident, ( $( $names:ident : $params:ty ),* ) -> $returns:ty => $body:tt ) => ({ + let body = $crate::vm::env_def::macros::constrain_closure::< + <$returns as $crate::vm::env_def::ConvertibleToWasm>::NativeType, _ + >(|| { + unmarshall_then_body!($body, $ctx, $args_iter, $( $names : $params ),*) + }); + let r = body()?; + return Ok($crate::sandbox::ReturnValue::Value({ use $crate::vm::env_def::ConvertibleToWasm; r.to_typed_value() })) + }); + ( $args_iter:ident, $ctx:ident, ( $( $names:ident : $params:ty ),* ) => $body:tt ) => ({ + let body = $crate::vm::env_def::macros::constrain_closure::<(), _>(|| { + unmarshall_then_body!($body, $ctx, $args_iter, $( $names : $params ),*) + }); + body()?; + return Ok($crate::sandbox::ReturnValue::Unit) + }) +} + +#[macro_export] +macro_rules! define_func { + ( < E: $ext_ty:tt > $name:ident ( $ctx: ident $(, $names:ident : $params:ty)*) $(-> $returns:ty)* => $body:tt ) => { + fn $name< E: $ext_ty >( + $ctx: &mut $crate::vm::Runtime, + args: &[$crate::sandbox::TypedValue], + ) -> Result { + #[allow(unused)] + let mut args = args.iter(); + + unmarshall_then_body_then_marshall!( + args, + $ctx, + ( $( $names : $params ),* ) $( -> $returns )* => $body + ) + } + }; +} + +/// Define a function set that can be imported by executing wasm code. +/// +/// **NB**: Be advised that all functions defined by this macro +/// will panic if called with unexpected arguments. +/// +/// It's up to the user of this macro to check signatures of wasm code to be executed +/// and reject the code if any imported function has a mismached signature. +macro_rules! define_env { + ( $init_name:ident , < E: $ext_ty:tt > , + $( $name:ident ( $ctx:ident $( , $names:ident : $params:ty )* ) + $( -> $returns:ty )* => $body:tt , )* + ) => { + pub(crate) fn $init_name() -> HostFunctionSet { + let mut env = HostFunctionSet::new(); + + $( + env.funcs.insert( + stringify!( $name ).into(), + HostFunction::new( + gen_signature!( ( $( $params ),* ) $( -> $returns )* ), + { + define_func!( + < E: $ext_ty > $name ( $ctx $(, $names : $params )* ) $( -> $returns )* => $body + ); + $name:: + }, + ), + ); + )* + + env + } + }; +} + +#[cfg(test)] +mod tests { + use parity_wasm::elements::FunctionType; + use parity_wasm::elements::ValueType; + use runtime_primitives::traits::{As, Zero}; + use sandbox::{self, ReturnValue, TypedValue}; + use vm::env_def::{HostFunction, HostFunctionSet}; + use vm::tests::MockExt; + use vm::{Ext, Runtime}; + use Trait; + + #[test] + fn macro_unmarshall_then_body_then_marshall_value_or_trap() { + fn test_value( + _ctx: &mut u32, + args: &[sandbox::TypedValue], + ) -> Result { + let mut args = args.iter(); + unmarshall_then_body_then_marshall!( + args, + _ctx, + (a: u32, b: u32) -> u32 => { + if b == 0 { + Err(sandbox::HostError) + } else { + Ok(a / b) + } + } + ) + } + + let ctx = &mut 0; + assert_eq!( + test_value(ctx, &[TypedValue::I32(15), TypedValue::I32(3)]).unwrap(), + ReturnValue::Value(TypedValue::I32(5)), + ); + assert!(test_value(ctx, &[TypedValue::I32(15), TypedValue::I32(0)]).is_err()); + } + + #[test] + fn macro_unmarshall_then_body_then_marshall_unit() { + fn test_unit( + ctx: &mut u32, + args: &[sandbox::TypedValue], + ) -> Result { + let mut args = args.iter(); + unmarshall_then_body_then_marshall!( + args, + ctx, + (a: u32, b: u32) => { + *ctx = a + b; + Ok(()) + } + ) + } + + let ctx = &mut 0; + let result = test_unit(ctx, &[TypedValue::I32(2), TypedValue::I32(3)]).unwrap(); + assert_eq!(result, ReturnValue::Unit); + assert_eq!(*ctx, 5); + } + + #[test] + fn macro_define_func() { + define_func!( ext_gas (_ctx, amount: u32) => { + let amount = <<::T as Trait>::Gas as As>::sa(amount); + if !amount.is_zero() { + Ok(()) + } else { + Err(sandbox::HostError) + } + }); + let _f: fn(&mut Runtime, &[sandbox::TypedValue]) + -> Result = ext_gas::; + } + + #[test] + fn macro_gen_signature() { + assert_eq!( + gen_signature!((i32)), + FunctionType::new(vec![ValueType::I32], None), + ); + + assert_eq!( + gen_signature!( (i32, u32) -> u32 ), + FunctionType::new(vec![ValueType::I32, ValueType::I32], Some(ValueType::I32)), + ); + } + + #[test] + fn macro_unmarshall_then_body() { + let args = vec![TypedValue::I32(5), TypedValue::I32(3)]; + let mut args = args.iter(); + + let ctx: &mut u32 = &mut 0; + + let r = unmarshall_then_body!( + { + *ctx = a + b; + a * b + }, + ctx, + args, + a: u32, + b: u32 + ); + + assert_eq!(*ctx, 8); + assert_eq!(r, 15); + } + + #[test] + fn macro_define_env() { + define_env!(init_env, , + ext_gas( _ctx, amount: u32 ) => { + let amount = <<::T as Trait>::Gas as As>::sa(amount); + if !amount.is_zero() { + Ok(()) + } else { + Err(sandbox::HostError) + } + }, + ); + + let env = init_env::(); + assert!(env.funcs.get("ext_gas").is_some()); + } +} diff --git a/runtime/contract/src/vm/env_def/mod.rs b/runtime/contract/src/vm/env_def/mod.rs new file mode 100644 index 0000000000000..dbdda705d3326 --- /dev/null +++ b/runtime/contract/src/vm/env_def/mod.rs @@ -0,0 +1,315 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +use super::{BalanceOf, CallReceipt, CreateReceipt, Ext, GasMeterResult, Runtime}; +use codec::Decode; +use parity_wasm::elements::{FunctionType, ValueType}; +use rstd::prelude::*; +use rstd::string::String; +use rstd::collections::btree_map::BTreeMap; +use runtime_primitives::traits::As; +use sandbox::{self, TypedValue}; +use system; +use Trait; + +#[macro_use] +mod macros; + +pub trait ConvertibleToWasm: Sized { + const VALUE_TYPE: ValueType; + type NativeType; + fn to_typed_value(self) -> TypedValue; + fn from_typed_value(TypedValue) -> Option; +} +impl ConvertibleToWasm for i32 { + type NativeType = i32; + const VALUE_TYPE: ValueType = ValueType::I32; + fn to_typed_value(self) -> TypedValue { + TypedValue::I32(self) + } + fn from_typed_value(v: TypedValue) -> Option { + v.as_i32() + } +} +impl ConvertibleToWasm for u32 { + type NativeType = u32; + const VALUE_TYPE: ValueType = ValueType::I32; + fn to_typed_value(self) -> TypedValue { + TypedValue::I32(self as i32) + } + fn from_typed_value(v: TypedValue) -> Option { + match v { + TypedValue::I32(v) => Some(v as u32), + _ => None, + } + } +} +impl ConvertibleToWasm for u64 { + type NativeType = u64; + const VALUE_TYPE: ValueType = ValueType::I64; + fn to_typed_value(self) -> TypedValue { + TypedValue::I64(self as i64) + } + fn from_typed_value(v: TypedValue) -> Option { + match v { + TypedValue::I64(v) => Some(v as u64), + _ => None, + } + } +} + +/// Represents a set of function that defined in this particular environment and +/// which can be imported and called by the module. +pub(crate) struct HostFunctionSet { + /// Functions which defined in the environment. + pub funcs: BTreeMap>, +} +impl HostFunctionSet { + pub fn new() -> Self { + HostFunctionSet { + funcs: BTreeMap::new(), + } + } +} + +pub(crate) struct HostFunction { + pub(crate) f: fn(&mut Runtime, &[sandbox::TypedValue]) + -> Result, + func_type: FunctionType, +} +impl HostFunction { + /// Create a new instance of a host function. + pub fn new( + func_type: FunctionType, + f: fn(&mut Runtime, &[sandbox::TypedValue]) + -> Result, + ) -> Self { + HostFunction { func_type, f } + } + + /// Returns a function pointer of this host function. + pub fn raw_fn_ptr( + &self, + ) -> fn(&mut Runtime, &[sandbox::TypedValue]) + -> Result { + self.f + } + + /// Check if the this function could be invoked with the given function signature. + pub fn func_type_matches(&self, func_type: &FunctionType) -> bool { + &self.func_type == func_type + } +} + +// TODO: ext_balance, ext_address, ext_callvalue, etc. + +// Define a function `fn init_env() -> HostFunctionSet` that returns +// a function set which can be imported by an executed contract. +define_env!(init_env, , + + // gas(amount: u32) + // + // Account for used gas. Traps if gas used is greater than gas limit. + // + // - amount: How much gas is used. + gas(ctx, amount: u32) => { + let amount = <<::T as Trait>::Gas as As>::sa(amount); + + match ctx.gas_meter.charge(amount) { + GasMeterResult::Proceed => Ok(()), + GasMeterResult::OutOfGas => Err(sandbox::HostError), + } + }, + + // ext_put_storage(location_ptr: u32, value_non_null: u32, value_ptr: u32); + // + // Change the value at the given location in storage or remove it. + // + // - location_ptr: pointer into the linear + // memory where the location of the requested value is placed. + // - value_non_null: if set to 0, then the entry + // at the given location will be removed. + // - value_ptr: pointer into the linear memory + // where the value to set is placed. If `value_non_null` is set to 0, then this parameter is ignored. + ext_set_storage(ctx, location_ptr: u32, value_non_null: u32, value_ptr: u32) => { + let mut location = [0; 32]; + + ctx.memory().get(location_ptr, &mut location)?; + + let value = if value_non_null != 0 { + let mut value = [0; 32]; + ctx.memory().get(value_ptr, &mut value)?; + Some(value.to_vec()) + } else { + None + }; + ctx.ext.set_storage(&location, value); + + Ok(()) + }, + + // ext_get_storage(location_ptr: u32, dest_ptr: u32); + // + // Retrieve the value at the given location from the strorage. + // If there is no entry at the given location then all-zero-value + // will be returned. + // + // - location_ptr: pointer into the linear + // memory where the location of the requested value is placed. + // - dest_ptr: pointer where contents of the specified storage location + // should be placed. + ext_get_storage(ctx, location_ptr: u32, dest_ptr: u32) => { + let mut location = [0; 32]; + ctx.memory().get(location_ptr, &mut location)?; + + if let Some(value) = ctx.ext.get_storage(&location) { + ctx.memory().set(dest_ptr, &value)?; + } else { + ctx.memory().set(dest_ptr, &[0u8; 32])?; + } + + Ok(()) + }, + + // ext_call(transfer_to_ptr: u32, transfer_to_len: u32, gas: u64, value_ptr: u32, value_len: u32, input_data_ptr: u32, input_data_len: u32) + ext_call(ctx, callee_ptr: u32, callee_len: u32, gas: u64, value_ptr: u32, value_len: u32, input_data_ptr: u32, input_data_len: u32) -> u32 => { + let mut callee = Vec::new(); + callee.resize(callee_len as usize, 0); + ctx.memory().get(callee_ptr, &mut callee)?; + let callee = + <::T as system::Trait>::AccountId::decode(&mut &callee[..]) + .ok_or_else(|| sandbox::HostError)?; + + let mut value_buf = Vec::new(); + value_buf.resize(value_len as usize, 0); + ctx.memory().get(value_ptr, &mut value_buf)?; + let value = BalanceOf::<::T>::decode(&mut &value_buf[..]) + .ok_or_else(|| sandbox::HostError)?; + + let mut input_data = Vec::new(); + input_data.resize(input_data_len as usize, 0u8); + ctx.memory().get(input_data_ptr, &mut input_data)?; + + let nested_gas_limit = if gas == 0 { + ctx.gas_meter.gas_left() + } else { + <<::T as Trait>::Gas as As>::sa(gas) + }; + let ext = &mut ctx.ext; + let call_outcome = ctx.gas_meter.with_nested(nested_gas_limit, |nested_meter| { + match nested_meter { + Some(nested_meter) => ext.call(&callee, value, nested_meter, &input_data), + // there is not enough gas to allocate for the nested call. + None => Err(()), + } + }); + + match call_outcome { + // TODO: Find a way how to pass return_data back to the this sandbox. + Ok(CallReceipt { .. }) => Ok(0), + Err(_) => Ok(1), + } + }, + + // ext_create(code_ptr: u32, code_len: u32, gas: u64, value_ptr: u32, value_len: u32, input_data_ptr: u32, input_data_len: u32) -> u32 + ext_create( + ctx, code_ptr: u32, + code_len: u32, + gas: u64, + value_ptr: u32, + value_len: u32, + input_data_ptr: u32, + input_data_len: u32 + ) -> u32 => { + let mut value_buf = Vec::new(); + value_buf.resize(value_len as usize, 0); + ctx.memory().get(value_ptr, &mut value_buf)?; + let value = BalanceOf::<::T>::decode(&mut &value_buf[..]) + .ok_or_else(|| sandbox::HostError)?; + + let mut code = Vec::new(); + code.resize(code_len as usize, 0u8); + ctx.memory().get(code_ptr, &mut code)?; + + let mut input_data = Vec::new(); + input_data.resize(input_data_len as usize, 0u8); + ctx.memory().get(input_data_ptr, &mut input_data)?; + + let nested_gas_limit = if gas == 0 { + ctx.gas_meter.gas_left() + } else { + <<::T as Trait>::Gas as As>::sa(gas) + }; + let ext = &mut ctx.ext; + let create_outcome = ctx.gas_meter.with_nested(nested_gas_limit, |nested_meter| { + match nested_meter { + Some(nested_meter) => ext.create(&code, value, nested_meter, &input_data), + // there is not enough gas to allocate for the nested call. + None => Err(()), + } + }); + + match create_outcome { + // TODO: Copy an address of the created contract in the sandbox. + Ok(CreateReceipt { .. }) => Ok(0), + Err(_) => Ok(1), + } + }, + + // ext_return(data_ptr: u32, data_len: u32) -> ! + ext_return(ctx, data_ptr: u32, data_len: u32) => { + let mut data_buf = Vec::new(); + data_buf.resize(data_len as usize, 0); + ctx.memory().get(data_ptr, &mut data_buf)?; + + ctx.store_return_data(data_buf) + .map_err(|_| sandbox::HostError)?; + + // The trap mechanism is used to immediately terminate the execution. + // This trap should be handled appropriately before returning the result + // to the user of this crate. + Err(sandbox::HostError) + }, + + // ext_input_size() -> u32 + // + // Returns size of an input buffer. + ext_input_size(ctx) -> u32 => { + Ok(ctx.input_data.len() as u32) + }, + + // ext_input_copy(dest_ptr: u32, offset: u32, len: u32) + // + // Copy data from an input buffer starting from `offset` with length `len` into the contract memory. + // The region at which the data should be put is specified by `dest_ptr`. + ext_input_copy(ctx, dest_ptr: u32, offset: u32, len: u32) => { + let offset = offset as usize; + if offset > ctx.input_data.len() { + // Offset can't be larger than input buffer length. + return Err(sandbox::HostError); + } + + // This can't panic since `offset <= ctx.input_data.len()`. + let src = &ctx.input_data[offset..]; + if src.len() != len as usize { + return Err(sandbox::HostError); + } + + ctx.memory().set(dest_ptr, src)?; + + Ok(()) + }, +); diff --git a/runtime/contract/src/vm/mod.rs b/runtime/contract/src/vm/mod.rs new file mode 100644 index 0000000000000..0ca9f9e694b5b --- /dev/null +++ b/runtime/contract/src/vm/mod.rs @@ -0,0 +1,553 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! This module provides a means for executing contracts +//! represented in wasm. + +use exec::{CallReceipt, CreateReceipt}; +use gas::{GasMeter, GasMeterResult}; +use rstd::prelude::*; +use runtime_primitives::traits::{As, CheckedMul}; +use {sandbox, balances, system}; +use Trait; + +type BalanceOf = ::Balance; +type AccountIdOf = ::AccountId; + +mod prepare; + +#[macro_use] +mod env_def; + +use self::prepare::{prepare_contract, PreparedContract}; + +/// An interface that provides an access to the external environment in which the +/// smart-contract is executed. +/// +/// This interface is specialised to an account of the executing code, so all +/// operations are implicitly performed on that account. +pub trait Ext { + type T: Trait; + + /// Returns the storage entry of the executing account by the given key. + fn get_storage(&self, key: &[u8]) -> Option>; + + /// Sets the storage entry by the given key to the specified value. + fn set_storage(&mut self, key: &[u8], value: Option>); + + // TODO: Return the address of the created contract. + /// Create a new account for a contract. + /// + /// The newly created account will be associated with the `code`. `value` specifies the amount of value + /// transfered from this to the newly created account. + fn create( + &mut self, + code: &[u8], + value: BalanceOf, + gas_meter: &mut GasMeter, + data: &[u8], + ) -> Result, ()>; + + /// Call (possibly transfering some amount of funds) into the specified account. + fn call( + &mut self, + to: &AccountIdOf, + value: BalanceOf, + gas_meter: &mut GasMeter, + data: &[u8], + ) -> Result; +} + +/// Error that can occur while preparing or executing wasm smart-contract. +#[derive(Debug, PartialEq, Eq)] +pub enum Error { + /// Error happened while serializing the module. + Serialization, + + /// Error happened while deserializing the module. + Deserialization, + + /// Internal memory declaration has been found in the module. + InternalMemoryDeclared, + + /// Gas instrumentation failed. + /// + /// This most likely indicates the module isn't valid. + GasInstrumentation, + + /// Stack instrumentation failed. + /// + /// This most likely indicates the module isn't valid. + StackHeightInstrumentation, + + /// Error happened during invocation of the contract's entrypoint. + /// + /// Most likely because of trap. + Invoke, + + /// Error happened during instantiation. + /// + /// This might indicate that `start` function trapped, or module isn't + /// instantiable and/or unlinkable. + Instantiate, + + /// Memory creation error. + /// + /// This might happen when the memory import has invalid descriptor or + /// requested too much resources. + Memory, +} + +/// Enumerates all possible *special* trap conditions. +/// +/// In this runtime traps used not only for signaling about errors but also +/// to just terminate quickly in some cases. +enum SpecialTrap { + // TODO: Can we pass wrapped memory instance instead of copying? + /// Signals that trap was generated in response to call `ext_return` host function. + Return(Vec), +} + +pub(crate) struct Runtime<'a, 'data, E: Ext + 'a> { + ext: &'a mut E, + input_data: &'data [u8], + config: &'a Config, + memory: sandbox::Memory, + gas_meter: &'a mut GasMeter, + special_trap: Option, +} +impl<'a, 'data, E: Ext + 'a> Runtime<'a, 'data, E> { + fn memory(&self) -> &sandbox::Memory { + &self.memory + } + /// Save a data buffer as a result of the execution. + /// + /// This function also charges gas for the returning. + /// + /// Returns `Err` if there is not enough gas. + fn store_return_data(&mut self, data: Vec) -> Result<(), ()> { + let data_len = <<::T as Trait>::Gas as As>::sa(data.len() as u64); + let price = (self.config.return_data_per_byte_cost) + .checked_mul(&data_len) + .ok_or_else(|| ())?; + + match self.gas_meter.charge(price) { + GasMeterResult::Proceed => { + self.special_trap = Some(SpecialTrap::Return(data)); + Ok(()) + } + GasMeterResult::OutOfGas => Err(()), + } + } +} + +fn to_execution_result( + runtime: Runtime, + run_err: Option, +) -> Result { + // Check the exact type of the error. It could be plain trap or + // special runtime trap the we must recognize. + let return_data = match (run_err, runtime.special_trap) { + // No traps were generated. Proceed normally. + (None, None) => Vec::new(), + // Special case. The trap was the result of the execution `return` host function. + (Some(sandbox::Error::Execution), Some(SpecialTrap::Return(rd))) => rd, + // Any other kind of a trap should result in a failure. + (Some(_), _) => return Err(Error::Invoke), + // Any other case (such as special trap flag without actual trap) signifies + // a logic error. + _ => unreachable!(), + }; + + Ok(ExecutionResult { return_data }) +} + +/// The result of execution of a smart-contract. +#[derive(PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(Debug))] +pub struct ExecutionResult { + /// The result produced by the execution of the contract. + /// + /// The contract can designate some buffer at the execution time via a special function. + /// If contract called this function with non-empty buffer it will be copied here. + /// + /// Note that gas is already charged for returning the data. + pub return_data: Vec, +} + +/// Execute the given code as a contract. +pub fn execute<'a, E: Ext>( + code: &[u8], + input_data: &[u8], + ext: &'a mut E, + gas_meter: &mut GasMeter, +) -> Result { + let config = Config::default(); + let env = env_def::init_env(); + + let PreparedContract { + instrumented_code, + memory, + } = prepare_contract(code, &config, &env)?; + + let mut imports = sandbox::EnvironmentDefinitionBuilder::new(); + for (func_name, ext_func) in &env.funcs { + imports.add_host_func("env", &func_name[..], ext_func.raw_fn_ptr()); + } + imports.add_memory("env", "memory", memory.clone()); + + let mut runtime = Runtime { + ext, + input_data, + config: &config, + memory, + gas_meter, + special_trap: None, + }; + + let mut instance = sandbox::Instance::new(&instrumented_code, &imports, &mut runtime) + .map_err(|_| Error::Instantiate)?; + + let run_result = instance.invoke(b"call", &[], &mut runtime); + + to_execution_result(runtime, run_result.err()) +} + +// TODO: Extract it to the root of the crate +#[derive(Clone)] +struct Config { + /// Gas cost of a growing memory by single page. + grow_mem_cost: T::Gas, + + /// Gas cost of a regular operation. + regular_op_cost: T::Gas, + + /// Gas cost per one byte returned. + return_data_per_byte_cost: T::Gas, + + /// How tall the stack is allowed to grow? + /// + /// See https://wiki.parity.io/WebAssembly-StackHeight to find out + /// how the stack frame cost is calculated. + max_stack_height: u32, + + //// What is the maximal memory pages amount is allowed to have for + /// a contract. + max_memory_pages: u32, +} + +impl Default for Config { + fn default() -> Config { + Config { + grow_mem_cost: T::Gas::sa(1), + regular_op_cost: T::Gas::sa(1), + return_data_per_byte_cost: T::Gas::sa(1), + max_stack_height: 64 * 1024, + max_memory_pages: 16, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use gas::GasMeter; + use std::collections::HashMap; + use tests::Test; + use wabt; + + #[derive(Debug, PartialEq, Eq)] + struct CreateEntry { + code: Vec, + endowment: u64, + data: Vec, + gas_left: u64, + } + #[derive(Debug, PartialEq, Eq)] + struct TransferEntry { + to: u64, + value: u64, + data: Vec, + gas_left: u64, + } + #[derive(Default)] + pub struct MockExt { + storage: HashMap, Vec>, + creates: Vec, + transfers: Vec, + next_account_id: u64, + } + impl Ext for MockExt { + type T = Test; + + fn get_storage(&self, key: &[u8]) -> Option> { + self.storage.get(key).cloned() + } + fn set_storage(&mut self, key: &[u8], value: Option>) { + *self.storage.entry(key.to_vec()).or_insert(Vec::new()) = value.unwrap_or(Vec::new()); + } + fn create( + &mut self, + code: &[u8], + endowment: u64, + gas_meter: &mut GasMeter, + data: &[u8], + ) -> Result, ()> { + self.creates.push(CreateEntry { + code: code.to_vec(), + endowment, + data: data.to_vec(), + gas_left: gas_meter.gas_left(), + }); + let address = self.next_account_id; + self.next_account_id += 1; + + Ok(CreateReceipt { address }) + } + fn call( + &mut self, + to: &u64, + value: u64, + gas_meter: &mut GasMeter, + data: &[u8], + ) -> Result { + self.transfers.push(TransferEntry { + to: *to, + value, + data: data.to_vec(), + gas_left: gas_meter.gas_left(), + }); + // Assume for now that it was just a plain transfer. + // TODO: Add tests for different call outcomes. + Ok(CallReceipt { + return_data: Vec::new(), + }) + } + } + + const CODE_TRANSFER: &str = r#" +(module + ;; ext_call( + ;; callee_ptr: u32, + ;; callee_len: u32, + ;; gas: u64, + ;; value_ptr: u32, + ;; value_len: u32, + ;; input_data_ptr: u32, + ;; input_data_len: u32 + ;;) -> u32 + (import "env" "ext_call" (func $ext_call (param i32 i32 i64 i32 i32 i32 i32) (result i32))) + (import "env" "memory" (memory 1 1)) + (func (export "call") + (drop + (call $ext_call + (i32.const 4) ;; Pointer to "callee" address. + (i32.const 8) ;; Length of "callee" address. + (i64.const 0) ;; How much gas to devote for the execution. 0 = all. + (i32.const 12) ;; Pointer to the buffer with value to transfer + (i32.const 8) ;; Length of the buffer with value to transfer. + (i32.const 20) ;; Pointer to input data buffer address + (i32.const 4) ;; Length of input data buffer + ) + ) + ) + ;; Destination AccountId to transfer the funds. + ;; Represented by u64 (8 bytes long) in little endian. + (data (i32.const 4) "\09\00\00\00\00\00\00\00") + ;; Amount of value to transfer. + ;; Represented by u64 (8 bytes long) in little endian. + (data (i32.const 12) "\06\00\00\00\00\00\00\00") + + (data (i32.const 20) "\01\02\03\04") +) +"#; + + #[test] + fn contract_transfer() { + let code_transfer = wabt::wat2wasm(CODE_TRANSFER).unwrap(); + + let mut mock_ext = MockExt::default(); + execute( + &code_transfer, + &[], + &mut mock_ext, + &mut GasMeter::with_limit(50_000, 1), + ).unwrap(); + + assert_eq!( + &mock_ext.transfers, + &[TransferEntry { + to: 9, + value: 6, + data: vec![ + 1, 2, 3, 4, + ], + gas_left: 49990, + }] + ); + } + + const CODE_CREATE: &str = r#" +(module + ;; ext_create( + ;; code_ptr: u32, + ;; code_len: u32, + ;; gas: u64, + ;; value_ptr: u32, + ;; value_len: u32, + ;; input_data_ptr: u32, + ;; input_data_len: u32, + ;; ) -> u32 + (import "env" "ext_create" (func $ext_create (param i32 i32 i64 i32 i32 i32 i32) (result i32))) + (import "env" "memory" (memory 1 1)) + (func (export "call") + (drop + (call $ext_create + (i32.const 12) ;; Pointer to `code` + (i32.const 8) ;; Length of `code` + (i64.const 0) ;; How much gas to devote for the execution. 0 = all. + (i32.const 4) ;; Pointer to the buffer with value to transfer + (i32.const 8) ;; Length of the buffer with value to transfer + (i32.const 20) ;; Pointer to input data buffer address + (i32.const 4) ;; Length of input data buffer + ) + ) + ) + ;; Amount of value to transfer. + ;; Represented by u64 (8 bytes long) in little endian. + (data (i32.const 4) "\03\00\00\00\00\00\00\00") + ;; Embedded wasm code. + (data (i32.const 12) "\00\61\73\6d\01\00\00\00") + ;; Input data to pass to the contract being created. + (data (i32.const 20) "\01\02\03\04") +) +"#; + + #[test] + fn contract_create() { + let code_create = wabt::wat2wasm(CODE_CREATE).unwrap(); + + let mut mock_ext = MockExt::default(); + execute( + &code_create, + &[], + &mut mock_ext, + &mut GasMeter::with_limit(50_000, 1), + ).unwrap(); + + assert_eq!( + &mock_ext.creates, + &[CreateEntry { + code: vec![0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00], + endowment: 3, + data: vec![ + 1, 2, 3, 4, + ], + gas_left: 49990, + }] + ); + } + + const CODE_MEM: &str = r#" +(module + ;; Internal memory is not allowed. + (memory 1 1) + + (func (export "call") + nop + ) +) +"#; + + #[test] + fn contract_internal_mem() { + let code_mem = wabt::wat2wasm(CODE_MEM).unwrap(); + + let mut mock_ext = MockExt::default(); + + assert_matches!( + execute( + &code_mem, + &[], + &mut mock_ext, + &mut GasMeter::with_limit(100_000, 1) + ), + Err(_) + ); + } + + const CODE_TRANSFER_LIMITED_GAS: &str = r#" +(module + ;; ext_call( + ;; callee_ptr: u32, + ;; callee_len: u32, + ;; gas: u64, + ;; value_ptr: u32, + ;; value_len: u32, + ;; input_data_ptr: u32, + ;; input_data_len: u32 + ;;) -> u32 + (import "env" "ext_call" (func $ext_call (param i32 i32 i64 i32 i32 i32 i32) (result i32))) + (import "env" "memory" (memory 1 1)) + (func (export "call") + (drop + (call $ext_call + (i32.const 4) ;; Pointer to "callee" address. + (i32.const 8) ;; Length of "callee" address. + (i64.const 228) ;; How much gas to devote for the execution. + (i32.const 12) ;; Pointer to the buffer with value to transfer + (i32.const 8) ;; Length of the buffer with value to transfer. + (i32.const 20) ;; Pointer to input data buffer address + (i32.const 4) ;; Length of input data buffer + ) + ) + ) + ;; Destination AccountId to transfer the funds. + ;; Represented by u64 (8 bytes long) in little endian. + (data (i32.const 4) "\09\00\00\00\00\00\00\00") + ;; Amount of value to transfer. + ;; Represented by u64 (8 bytes long) in little endian. + (data (i32.const 12) "\06\00\00\00\00\00\00\00") + + (data (i32.const 20) "\01\02\03\04") +) +"#; + + #[test] + fn contract_call_limited_gas() { + let code_transfer = wabt::wat2wasm(CODE_TRANSFER_LIMITED_GAS).unwrap(); + + let mut mock_ext = MockExt::default(); + execute( + &code_transfer, + &[], + &mut mock_ext, + &mut GasMeter::with_limit(50_000, 1), + ).unwrap(); + + assert_eq!( + &mock_ext.transfers, + &[TransferEntry { + to: 9, + value: 6, + data: vec![ + 1, 2, 3, 4, + ], + gas_left: 228, + }] + ); + } +} diff --git a/runtime/contract/src/vm/prepare.rs b/runtime/contract/src/vm/prepare.rs new file mode 100644 index 0000000000000..b15f9cbc3a977 --- /dev/null +++ b/runtime/contract/src/vm/prepare.rs @@ -0,0 +1,286 @@ +// Copyright 2018 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 . + +//! Module that takes care of loading, checking and preprocessing of a +//! wasm module before execution. + +use super::env_def::HostFunctionSet; +use super::{Config, Error, Ext}; +use rstd::prelude::*; +use parity_wasm::elements::{self, External, MemoryType, Type}; +use pwasm_utils; +use pwasm_utils::rules; +use runtime_primitives::traits::As; +use sandbox; +use Trait; + +struct ContractModule<'a, T: Trait + 'a> { + // 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, + config: &'a Config, +} + +impl<'a, T: Trait> ContractModule<'a, T> { + fn new(original_code: &[u8], config: &'a Config) -> Result, Error> { + let module = + elements::deserialize_buffer(original_code).map_err(|_| Error::Deserialization)?; + Ok(ContractModule { + module: Some(module), + config, + }) + } + + /// Ensures that module doesn't declare internal memories. + /// + /// In this runtime we only allow wasm module to import memory from the environment. + /// 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<(), Error> { + let module = self + .module + .as_ref() + .expect("On entry to the function `module` can't be None; qed"); + if module + .memory_section() + .map_or(false, |ms| ms.entries().len() > 0) + { + return Err(Error::InternalMemoryDeclared); + } + Ok(()) + } + + fn inject_gas_metering(&mut self) -> Result<(), Error> { + let gas_rules = rules::Set::new(self.config.regular_op_cost.as_(), Default::default()) + .with_grow_cost(self.config.grow_mem_cost.as_()) + .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) + .map_err(|_| Error::GasInstrumentation)?; + + self.module = Some(contract_module); + Ok(()) + } + + fn inject_stack_height_metering(&mut self) -> Result<(), Error> { + let module = self + .module + .take() + .expect("On entry to the function `module` can't be `None`; qed"); + + let contract_module = + pwasm_utils::stack_height::inject_limiter(module, self.config.max_stack_height) + .map_err(|_| Error::StackHeightInstrumentation)?; + + self.module = Some(contract_module); + Ok(()) + } + + /// Scan an import section if any. + /// + /// This accomplishes two tasks: + /// + /// - checks any imported function against defined host functions set, incl. + /// their signatures. + /// - if there is a memory import, returns it's descriptor + fn scan_imports(&self, env: &HostFunctionSet) -> Result, Error> { + let module = self + .module + .as_ref() + .expect("On entry to the function `module` can't be `None`; qed"); + + let types = module.type_section().map(|ts| ts.types()).unwrap_or(&[]); + let import_entries = module + .import_section() + .map(|is| is.entries()) + .unwrap_or(&[]); + + let mut imported_mem_type = None; + + for import in import_entries { + if import.module() != "env" { + // This import tries to import something from non-"env" module, + // but all imports are located in "env" at the moment. + return Err(Error::Instantiate); + } + + let type_idx = match import.external() { + &External::Function(ref type_idx) => type_idx, + &External::Memory(ref memory_type) => { + imported_mem_type = Some(memory_type); + continue; + } + _ => continue, + }; + + let Type::Function(ref func_ty) = types + .get(*type_idx as usize) + .ok_or_else(|| Error::Instantiate)?; + + let ext_func = env + .funcs + .get(import.field()) + .ok_or_else(|| Error::Instantiate)?; + if !ext_func.func_type_matches(func_ty) { + return Err(Error::Instantiate); + } + } + Ok(imported_mem_type) + } + + fn into_wasm_code(mut self) -> Result, Error> { + elements::serialize( + self.module + .take() + .expect("On entry to the function `module` can't be `None`; qed"), + ).map_err(|_| Error::Serialization) + } +} + +pub(super) struct PreparedContract { + pub instrumented_code: Vec, + pub memory: sandbox::Memory, +} + +/// Loads the given module given in `original_code`, performs some checks on it and +/// does some preprocessing. +/// +/// The checks are: +/// +/// - module doesn't define an internal memory instance, +/// - imported memory (if any) doesn't reserve more memory than permitted by the `config`, +/// - 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(super) fn prepare_contract( + original_code: &[u8], + config: &Config, + env: &HostFunctionSet, +) -> Result { + let mut contract_module = ContractModule::new(original_code, config)?; + contract_module.ensure_no_internal_memory()?; + contract_module.inject_gas_metering()?; + contract_module.inject_stack_height_metering()?; + + let memory = if let Some(memory_type) = contract_module.scan_imports(env)? { + // Inspect the module to extract the initial and maximum page count. + let limits = memory_type.limits(); + match (limits.initial(), limits.maximum()) { + (initial, Some(maximum)) if initial > maximum => { + // Requested initial number of pages should not exceed the requested maximum. + return Err(Error::Memory); + } + (_, Some(maximum)) if maximum > config.max_memory_pages => { + // Maximum number of pages should not exceed the configured maximum. + return Err(Error::Memory); + } + (_, None) => { + // Maximum number of pages should be always declared. + // This isn't a hard requirement and can be treated as a maxiumum set + // to configured maximum. + return Err(Error::Memory); + } + (initial, maximum) => sandbox::Memory::new(initial, maximum), + } + } else { + // If none memory imported then just crate an empty placeholder. + // Any access to it will lead to out of bounds trap. + sandbox::Memory::new(0, Some(0)) + }; + let memory = memory.map_err(|_| Error::Memory)?; + + Ok(PreparedContract { + instrumented_code: contract_module.into_wasm_code()?, + memory, + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::fmt; + use tests::Test; + use vm::tests::MockExt; + use wabt; + + impl fmt::Debug for PreparedContract { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "PreparedContract {{ .. }}") + } + } + + fn parse_and_prepare_wat(wat: &str) -> Result { + let wasm = wabt::Wat2Wasm::new().validate(false).convert(wat).unwrap(); + let config = Config::::default(); + let env = ::vm::env_def::init_env(); + prepare_contract::(wasm.as_ref(), &config, &env) + } + + #[test] + fn internal_memory_declaration() { + let r = parse_and_prepare_wat(r#"(module (memory 1 1))"#); + assert_matches!(r, Err(Error::InternalMemoryDeclared)); + } + + #[test] + fn memory() { + // This test assumes that maximum page number is configured to a certain number. + assert_eq!(Config::::default().max_memory_pages, 16); + + let r = parse_and_prepare_wat(r#"(module (import "env" "memory" (memory 1 1)))"#); + assert_matches!(r, Ok(_)); + + // No memory import + let r = parse_and_prepare_wat(r#"(module)"#); + assert_matches!(r, Ok(_)); + + // initial exceed maximum + let r = parse_and_prepare_wat(r#"(module (import "env" "memory" (memory 16 1)))"#); + assert_matches!(r, Err(Error::Memory)); + + // no maximum + let r = parse_and_prepare_wat(r#"(module (import "env" "memory" (memory 1)))"#); + assert_matches!(r, Err(Error::Memory)); + + // requested maximum exceed configured maximum + let r = parse_and_prepare_wat(r#"(module (import "env" "memory" (memory 1 17)))"#); + assert_matches!(r, Err(Error::Memory)); + } + + #[test] + fn imports() { + // nothing can be imported from non-"env" module for now. + let r = parse_and_prepare_wat(r#"(module (import "another_module" "memory" (memory 1 1)))"#); + assert_matches!(r, Err(Error::Instantiate)); + + let r = parse_and_prepare_wat(r#"(module (import "env" "gas" (func (param i32))))"#); + assert_matches!(r, Ok(_)); + + // wrong signature + let r = parse_and_prepare_wat(r#"(module (import "env" "gas" (func (param i64))))"#); + assert_matches!(r, Err(Error::Instantiate)); + + // unknown function name + let r = parse_and_prepare_wat(r#"(module (import "env" "unknown_func" (func)))"#); + assert_matches!(r, Err(Error::Instantiate)); + } +} diff --git a/runtime/council/Cargo.toml b/runtime/council/Cargo.toml new file mode 100644 index 0000000000000..7d3b6c24d7d94 --- /dev/null +++ b/runtime/council/Cargo.toml @@ -0,0 +1,43 @@ +[package] +name = "substrate-runtime-council" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +hex-literal = "0.1.0" +integer-sqrt = { git = "https://github.com/paritytech/integer-sqrt-rs.git", branch = "master" } +serde = { version = "1.0", default_features = false } +serde_derive = { version = "1.0", optional = true } +safe-mix = { version = "1.0", default_features = false} +substrate-keyring = { path = "../../core/keyring", optional = true } +substrate-codec = { path = "../../core/codec", default_features = false } +substrate-codec-derive = { path = "../../core/codec/derive", default_features = false } +substrate-primitives = { path = "../../core/primitives", default_features = false } +substrate-runtime-std = { path = "../../core/runtime-std", default_features = false } +substrate-runtime-io = { path = "../../core/runtime-io", default_features = false } +substrate-runtime-support = { path = "../support", default_features = false } +substrate-runtime-primitives = { path = "../primitives", default_features = false } +substrate-runtime-consensus = { path = "../consensus", default_features = false } +substrate-runtime-balances = { path = "../balances", default_features = false } +substrate-runtime-democracy = { path = "../democracy", default_features = false } +substrate-runtime-system = { path = "../system", default_features = false } + +[features] +default = ["std"] +std = [ + "serde/std", + "serde_derive", + "safe-mix/std", + "substrate-keyring", + "substrate-codec/std", + "substrate-codec-derive/std", + "substrate-primitives/std", + "substrate-runtime-std/std", + "substrate-runtime-io/std", + "substrate-runtime-support/std", + "substrate-runtime-primitives/std", + "substrate-runtime-consensus/std", + "substrate-runtime-balances/std", + "substrate-runtime-democracy/std", + "substrate-runtime-system/std", +] diff --git a/runtime/council/src/lib.rs b/runtime/council/src/lib.rs new file mode 100644 index 0000000000000..1c0883877fcba --- /dev/null +++ b/runtime/council/src/lib.rs @@ -0,0 +1,238 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Council system: Handles the voting in and maintenance of council members. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(feature = "std")] +extern crate serde; + +#[cfg(feature = "std")] +#[macro_use] +extern crate serde_derive; + +#[cfg(test)] +#[macro_use] +extern crate hex_literal; + +extern crate integer_sqrt; +extern crate substrate_codec as codec; +#[macro_use] extern crate substrate_codec_derive; +extern crate substrate_primitives; +#[cfg(feature = "std")] extern crate substrate_keyring as keyring; +#[macro_use] extern crate substrate_runtime_std as rstd; +extern crate substrate_runtime_io as runtime_io; +#[macro_use] extern crate substrate_runtime_support; +extern crate substrate_runtime_primitives as primitives; +extern crate substrate_runtime_balances as balances; +extern crate substrate_runtime_democracy as democracy; +extern crate substrate_runtime_system as system; + +#[cfg(feature = "std")] +use rstd::prelude::*; +#[cfg(feature = "std")] +use std::collections::HashMap; +#[cfg(feature = "std")] +use primitives::traits::As; +#[cfg(feature = "std")] +use substrate_runtime_support::StorageValue; + +pub mod voting; +pub mod motions; +pub mod seats; + +pub use seats::{Trait, Module, RawEvent, Event, VoteIndex}; + +#[cfg(feature = "std")] +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +#[serde(deny_unknown_fields)] +pub struct GenesisConfig { + // for the voting onto the council + pub candidacy_bond: T::Balance, + pub voter_bond: T::Balance, + pub present_slash_per_voter: T::Balance, + pub carry_count: u32, + pub active_council: Vec<(T::AccountId, T::BlockNumber)>, + pub approval_voting_period: T::BlockNumber, + pub presentation_duration: T::BlockNumber, + pub desired_seats: u32, + pub term_duration: T::BlockNumber, + pub inactive_grace_period: T::BlockNumber, + + // for the council's votes. + pub cooloff_period: T::BlockNumber, + pub voting_period: T::BlockNumber, +} + +#[cfg(feature = "std")] +impl Default for GenesisConfig { + fn default() -> Self { + GenesisConfig { + candidacy_bond: T::Balance::sa(9), + voter_bond: T::Balance::sa(0), + present_slash_per_voter: T::Balance::sa(1), + carry_count: 2, + inactive_grace_period: T::BlockNumber::sa(1), + active_council: vec![], + approval_voting_period: T::BlockNumber::sa(1000), + presentation_duration: T::BlockNumber::sa(1000), + desired_seats: 0, + term_duration: T::BlockNumber::sa(5), + cooloff_period: T::BlockNumber::sa(1000), + voting_period: T::BlockNumber::sa(3), + } + } +} + +#[cfg(feature = "std")] +impl primitives::BuildStorage for GenesisConfig +{ + fn build_storage(self) -> ::std::result::Result, Vec>, String> { + use codec::Encode; + + Ok(map![ + Self::hash(>::key()).to_vec() => self.candidacy_bond.encode(), + Self::hash(>::key()).to_vec() => self.voter_bond.encode(), + Self::hash(>::key()).to_vec() => self.present_slash_per_voter.encode(), + Self::hash(>::key()).to_vec() => self.carry_count.encode(), + Self::hash(>::key()).to_vec() => self.presentation_duration.encode(), + Self::hash(>::key()).to_vec() => self.approval_voting_period.encode(), + Self::hash(>::key()).to_vec() => self.term_duration.encode(), + Self::hash(>::key()).to_vec() => self.desired_seats.encode(), + Self::hash(>::key()).to_vec() => self.inactive_grace_period.encode(), + Self::hash(>::key()).to_vec() => self.active_council.encode(), + + Self::hash(>::key()).to_vec() => self.cooloff_period.encode(), + Self::hash(>::key()).to_vec() => self.voting_period.encode(), + Self::hash(>::key()).to_vec() => vec![0u8; 0].encode() + ]) + } +} + +#[cfg(test)] +mod tests { + // These re-exports are here for a reason, edit with care + pub use super::*; + pub use runtime_io::with_externalities; + pub use substrate_primitives::H256; + pub use primitives::BuildStorage; + pub use primitives::traits::{BlakeTwo256}; + pub use primitives::testing::{Digest, Header}; + pub use substrate_primitives::Blake2Hasher; + pub use {seats, motions, voting}; + + impl_outer_origin! { + pub enum Origin for Test { + motions + } + } + + impl_outer_event! { + pub enum Event for Test { + balances, democracy, seats, voting, motions + } + } + + impl_outer_dispatch! { + pub enum Call where origin: Origin { + Balances, + Democracy, + } + } + + // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. + #[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] + pub struct Test; + impl system::Trait for Test { + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type Digest = Digest; + type AccountId = u64; + type Header = Header; + type Event = Event; + } + impl balances::Trait for Test { + type Balance = u64; + type AccountIndex = u64; + type OnFreeBalanceZero = (); + type EnsureAccountLiquid = (); + type Event = Event; + } + impl democracy::Trait for Test { + type Proposal = Call; + type Event = Event; + } + impl seats::Trait for Test { + type Event = Event; + } + impl motions::Trait for Test { + type Origin = Origin; + type Proposal = Call; + type Event = Event; + } + impl voting::Trait for Test { + type Event = Event; + } + + pub fn new_test_ext(with_council: bool) -> runtime_io::TestExternalities { + let mut t = system::GenesisConfig::::default().build_storage().unwrap(); + t.extend(balances::GenesisConfig::{ + balances: vec![(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)], + transaction_base_fee: 0, + transaction_byte_fee: 0, + existential_deposit: 0, + transfer_fee: 0, + creation_fee: 0, + reclaim_rebate: 0, + }.build_storage().unwrap()); + t.extend(democracy::GenesisConfig::{ + launch_period: 1, + voting_period: 3, + minimum_deposit: 1, + }.build_storage().unwrap()); + t.extend(GenesisConfig::{ + candidacy_bond: 9, + voter_bond: 3, + present_slash_per_voter: 1, + carry_count: 2, + inactive_grace_period: 1, + active_council: if with_council { vec![ + (1, 10), + (2, 10), + (3, 10) + ] } else { vec![] }, + approval_voting_period: 4, + presentation_duration: 2, + desired_seats: 2, + term_duration: 5, + cooloff_period: 2, + voting_period: 1, + }.build_storage().unwrap()); + t.into() + } + + pub type System = system::Module; + pub type Balances = balances::Module; + pub type Democracy = democracy::Module; + pub type Council = seats::Module; + pub type CouncilVoting = voting::Module; + pub type CouncilMotions = motions::Module; +} \ No newline at end of file diff --git a/runtime/council/src/motions.rs b/runtime/council/src/motions.rs new file mode 100644 index 0000000000000..b9b26df00f015 --- /dev/null +++ b/runtime/council/src/motions.rs @@ -0,0 +1,372 @@ +// Copyright 2017 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::*; +use rstd::result; +use substrate_primitives::u32_trait::Value as U32; +use primitives::traits::{Hash, EnsureOrigin, MaybeSerializeDebug, OnFinalise}; +use substrate_runtime_support::dispatch::{Result, Dispatchable, Parameter}; +use substrate_runtime_support::{StorageValue, StorageMap}; +use super::{Trait as CouncilTrait, Module as Council}; +use system::{self, ensure_signed}; + +/// Simple index type for proposal counting. +pub type ProposalIndex = u32; + +pub trait Trait: CouncilTrait + MaybeSerializeDebug { + /// The outer origin type. + type Origin: From; + + /// The outer call dispatch type. + type Proposal: Parameter + Dispatchable::Origin> + MaybeSerializeDebug; + + /// 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 Origin { + /// It has been condoned by a given number of council members. + Members(u32), +} + +/// Event for this module. +decl_event!( + pub enum Event with RawEvent + where ::Hash, ::AccountId + { + /// A motion (given hash) has been proposed (by given account) with a threshold (given u32). + Proposed(AccountId, ProposalIndex, Hash, u32), + /// A motion (given hash) has been voted on by given account, leaving + /// a tally (yes votes and no votes given as u32s respectively). + Voted(AccountId, Hash, bool, u32, u32), + /// 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), + } +); + +decl_module! { + #[cfg_attr(feature = "std", serde(bound(deserialize = "::Proposal: ::serde::de::DeserializeOwned")))] + pub struct Module for enum Call where origin: ::Origin { + fn propose(origin, threshold: u32, proposal: Box<::Proposal>) -> Result; + fn vote(origin, proposal: T::Hash, index: ProposalIndex, approve: bool) -> Result; + } +} + +decl_storage! { + trait Store for Module as CouncilMotions { + /// The (hashes of) the active proposals. + pub Proposals get(proposals): default Vec; + /// Actual proposal for a given hash, if it's current. + pub ProposalOf get(proposal_of): map [ T::Hash => ::Proposal ]; + /// Votes for a given proposal: (required_yes_votes, yes_voters, no_voters). + pub Voting get(voting): map [ T::Hash => (ProposalIndex, u32, Vec, Vec) ]; + /// Proposals so far. + pub ProposalCount get(proposal_count): default u32; + } +} + +impl Module { + + /// Deposit one of this module's events. + fn deposit_event(event: Event) { + >::deposit_event(::Event::from(event).into()); + } + + pub fn is_councillor(who: &T::AccountId) -> bool { + >::active_council().iter() + .any(|&(ref a, _)| a == who) + } + + // Dispatch + fn propose(origin: ::Origin, threshold: u32, proposal: Box<::Proposal>) -> Result { + 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 ok = proposal.dispatch(Origin::Members(1).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); + >::insert(proposal_hash, (index, threshold, vec![who.clone()], vec![])); + + Self::deposit_event(RawEvent::Proposed(who, index, proposal_hash, threshold)); + } + Ok(()) + } + + fn vote(origin: ::Origin, proposal: T::Hash, index: ProposalIndex, approve: bool) -> Result { + 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.0 == index, "mismatched index"); + + let position_yes = voting.2.iter().position(|a| a == &who); + let position_no = voting.3.iter().position(|a| a == &who); + + if approve { + if position_yes.is_none() { + voting.2.push(who.clone()); + } else { + return Err("duplicate vote ignored") + } + if let Some(pos) = position_no { + voting.3.swap_remove(pos); + } + } else { + if position_no.is_none() { + voting.3.push(who.clone()); + } else { + return Err("duplicate vote ignored") + } + if let Some(pos) = position_yes { + voting.2.swap_remove(pos); + } + } + + let yes_votes = voting.2.len() as u32; + let no_votes = voting.3.len() as u32; + Self::deposit_event(RawEvent::Voted(who, proposal, approve, yes_votes, no_votes)); + + let threshold = voting.1; + let potential_votes = >::active_council().len() as u32; + let approved = yes_votes >= threshold; + let disapproved = potential_votes.saturating_sub(no_votes) < threshold; + if approved || disapproved { + if approved { + Self::deposit_event(RawEvent::Approved(proposal)); + + // execute motion, assuming it exists. + if let Some(p) = >::take(&proposal) { + let ok = p.dispatch(Origin::Members(threshold).into()).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); + } + + Ok(()) + } +} + +impl OnFinalise for Module { + fn on_finalise(_n: T::BlockNumber) { + } +} + +/// 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: u32) -> result::Result + where OuterOrigin: Into> +{ + match o.into() { + Some(Origin::Members(x)) if x >= n => Ok(n), + _ => Err("bad origin: expected to be a threshold number of council members"), + } +} + +pub struct EnsureMembers(::rstd::marker::PhantomData); +impl EnsureOrigin for EnsureMembers + where O: Into> +{ + type Success = u32; + fn ensure_origin(o: O) -> result::Result { + ensure_council_members(o, N::VALUE) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use super::RawEvent; + use ::tests::*; + use ::tests::{Call, Origin, Event as OuterEvent}; + use substrate_runtime_support::Hashable; + use system::{EventRecord, Phase}; + + #[test] + fn motions_basic_environment_works() { + with_externalities(&mut new_test_ext(true), || { + 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(balances::address::Address::Id(42), value, 0)) + } + + #[test] + fn motions_propose_works() { + with_externalities(&mut new_test_ext(true), || { + 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((0, 3, vec![1], Vec::::new()))); + + assert_eq!(System::events(), vec![ + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: OuterEvent::motions(RawEvent::Proposed(1, 0, hex!["a900ca23832b1f42a5d4af5d0ece88da63fbb4049cc00bac3f741eabb5a79c45"].into(), 3)) + } + ]); + }); + } + + #[test] + fn motions_ignoring_non_council_proposals_works() { + with_externalities(&mut new_test_ext(true), || { + 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 new_test_ext(true), || { + 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 new_test_ext(true), || { + 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 new_test_ext(true), || { + 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((0, 2, vec![1], Vec::::new()))); + 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((0, 2, Vec::::new(), 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!["a900ca23832b1f42a5d4af5d0ece88da63fbb4049cc00bac3f741eabb5a79c45"].into(), 2)) + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: OuterEvent::motions(RawEvent::Voted(1, hex!["a900ca23832b1f42a5d4af5d0ece88da63fbb4049cc00bac3f741eabb5a79c45"].into(), false, 0, 1)) + } + ]); + }); + } + + #[test] + fn motions_disapproval_works() { + with_externalities(&mut new_test_ext(true), || { + 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!["a900ca23832b1f42a5d4af5d0ece88da63fbb4049cc00bac3f741eabb5a79c45"].into(), 3)) + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: OuterEvent::motions(RawEvent::Voted(2, hex!["a900ca23832b1f42a5d4af5d0ece88da63fbb4049cc00bac3f741eabb5a79c45"].into(), false, 1, 1)) + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: OuterEvent::motions(RawEvent::Disapproved(hex!["a900ca23832b1f42a5d4af5d0ece88da63fbb4049cc00bac3f741eabb5a79c45"].into())) + } + ]); + }); + } + + #[test] + fn motions_approval_works() { + with_externalities(&mut new_test_ext(true), || { + 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!["a900ca23832b1f42a5d4af5d0ece88da63fbb4049cc00bac3f741eabb5a79c45"].into(), 2)) + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: OuterEvent::motions(RawEvent::Voted(2, hex!["a900ca23832b1f42a5d4af5d0ece88da63fbb4049cc00bac3f741eabb5a79c45"].into(), true, 2, 0)) + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: OuterEvent::motions(RawEvent::Approved(hex!["a900ca23832b1f42a5d4af5d0ece88da63fbb4049cc00bac3f741eabb5a79c45"].into())) + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: OuterEvent::motions(RawEvent::Executed(hex!["a900ca23832b1f42a5d4af5d0ece88da63fbb4049cc00bac3f741eabb5a79c45"].into(), false)) + } + ]); + }); + } +} diff --git a/runtime/council/src/seats.rs b/runtime/council/src/seats.rs new file mode 100644 index 0000000000000..294035a25a941 --- /dev/null +++ b/runtime/council/src/seats.rs @@ -0,0 +1,1355 @@ +// Copyright 2017 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, As, Lookup, OnFinalise}; +use runtime_io::print; +use substrate_runtime_support::{StorageValue, StorageMap, dispatch::Result}; +use democracy; +use balances::{self, address::Address}; +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 councilers: + +// 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 councilers will 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 prevent 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. + +pub type VoteIndex = u32; + +pub trait Trait: democracy::Trait { + type Event: From> + Into<::Event>; +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + fn set_approvals(origin, votes: Vec, index: VoteIndex) -> Result; + fn reap_inactive_voter(origin, reporter_index: u32, who: Address, who_index: u32, assumed_vote_index: VoteIndex) -> Result; + fn retract_voter(origin, index: u32) -> Result; + fn submit_candidacy(origin, slot: u32) -> Result; + fn present_winner(origin, candidate: Address, total: T::Balance, index: VoteIndex) -> Result; + + fn set_desired_seats(origin, count: u32) -> Result; + fn remove_member(origin, who: Address) -> Result; + fn set_presentation_duration(origin, count: T::BlockNumber) -> Result; + fn set_term_duration(origin, count: T::BlockNumber) -> Result; + } +} + +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): required T::Balance; + /// How much should be locked up in order to be able to submit votes. + pub VotingBond get(voting_bond): required T::Balance; + /// The punishment, per voter, if you provide an invalid presentation. + pub PresentSlashPerVoter get(present_slash_per_voter): required T::Balance; + /// How many runners-up should have their approvals persist until the next vote. + pub CarryCount get(carry_count): required u32; + /// How long to give each top candidate to present themselves after the vote ends. + pub PresentationDuration get(presentation_duration): required T::BlockNumber; + /// How many votes need to go by after a voter's last vote before they can be reaped if their + /// approvals are moot. + pub InactiveGracePeriod get(inactivity_grace_period): required VoteIndex; + /// How often (in blocks) to check for new votes. + pub VotingPeriod get(voting_period): required T::BlockNumber; + /// How long each position is active for. + pub TermDuration get(term_duration): required T::BlockNumber; + /// Number of accounts that should be sitting on the council. + pub DesiredSeats get(desired_seats): required u32; + + // permanent state (always relevant, changes only at the finalisation of voting) + /// The current council. When there's a vote going on, this should still be used for executive + /// matters. + pub ActiveCouncil get(active_council): default Vec<(T::AccountId, T::BlockNumber)>; + /// The total number of votes that have happened or are in progress. + pub VoteCount get(vote_index): default VoteIndex; + + // persistent state (always relevant, changes constantly) + /// The last cleared vote index that this voter was last active at. + pub ApprovalsOf get(approvals_of): default map [ T::AccountId => 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 => (VoteIndex, u32) ]; + /// The last cleared vote index that this voter was last active at. + pub LastActiveOf get(voter_last_active): map [ T::AccountId => VoteIndex ]; + /// The present voter list. + pub Voters get(voters): default Vec; + /// The present candidate list. + pub Candidates get(candidates): default Vec; // has holes + pub CandidateCount get(candidate_count): default u32; + + // temporary state (only relevant during finalisation/presentation) + /// The accounts holding the seats that will become free on the next tally. + pub NextFinalise get(next_finalise): (T::BlockNumber, u32, Vec); + /// The stakes as they were at the point that the vote ended. + pub SnapshotedStakes get(snapshoted_stakes): required Vec; + /// Get the leaderboard if we;re in the presentation phase. + pub Leaderboard get(leaderboard): Vec<(T::Balance, T::AccountId)>; // ORDERED low -> high + } +} + +decl_event!( + /// An event in this module. + pub enum Event with RawEvent + 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). + TallyFinalised(Vec, Vec), + } +); + +impl Module { + + /// Deposit one of this module's events. + fn deposit_event(event: Event) { + >::deposit_event(::Event::from(event).into()); + } + + // 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) + } + + /// 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_finalise() { + // 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) + } + } + + // dispatch + + /// Set candidate approvals. Approval slots stay valid as long as candidates in those slots + /// are registered. + fn set_approvals(origin: T::Origin, votes: Vec, index: VoteIndex) -> Result { + let who = ensure_signed(origin)?; + + ensure!(!Self::presentation_active(), "no approval changes during presentation period"); + ensure!(index == Self::vote_index(), "incorrect vote index"); + if !>::exists(&who) { + // not yet a voter - deduct bond. + // NOTE: this must be the last potential bailer, since it changes state. + >::reserve(&who, Self::voting_bond())?; + + >::put({ + let mut v = Self::voters(); + v.push(who.clone()); + v + }); + } + >::insert(&who, index); + >::insert(&who, votes); + Ok(()) + } + + /// 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. + /// + /// May be called by anyone. Returns the voter deposit to `signed`. + fn reap_inactive_voter( + origin: T::Origin, + reporter_index: u32, + who: Address, + who_index: u32, + assumed_vote_index: VoteIndex + ) -> Result { + let reporter = ensure_signed(origin)?; + + let who = >::lookup(who)?; + ensure!(!Self::presentation_active(), "cannot reap during presentation period"); + ensure!(Self::voter_last_active(&reporter).is_some(), "reporter must be a voter"); + let last_active = Self::voter_last_active(&who).ok_or("target for inactivity cleanup must be active")?; + ensure!(assumed_vote_index == Self::vote_index(), "vote index not current"); + ensure!(last_active < assumed_vote_index - Self::inactivity_grace_period(), "cannot reap during grace perid"); + let voters = Self::voters(); + let reporter_index = reporter_index as usize; + let who_index = who_index as usize; + ensure!(reporter_index < voters.len() && voters[reporter_index] == reporter, "bad reporter index"); + ensure!(who_index < voters.len() && voters[who_index] == who, "bad target index"); + + // will definitely kill one of signed or who now. + + let valid = !Self::approvals_of(&who).iter() + .zip(Self::candidates().iter()) + .any(|(&appr, addr)| + appr && + *addr != T::AccountId::default() && + Self::candidate_reg_info(addr).map_or(false, |x| x.0 <= last_active)/*defensive only: all items in candidates list are registered*/ + ); + + Self::remove_voter( + if valid { &who } else { &reporter }, + if valid { who_index } else { reporter_index }, + voters + ); + if valid { + // This only fails if `who` 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. + >::repatriate_reserved(&who, &reporter, Self::voting_bond())?; + Self::deposit_event(RawEvent::VoterReaped(who, reporter)); + } else { + >::slash_reserved(&reporter, Self::voting_bond()); + Self::deposit_event(RawEvent::BadReaperSlashed(reporter)); + } + Ok(()) + } + + /// Remove a voter. All votes are cancelled and the voter deposit is returned. + fn retract_voter(origin: T::Origin, index: u32) -> Result { + let who = ensure_signed(origin)?; + + ensure!(!Self::presentation_active(), "cannot retract when presenting"); + ensure!(>::exists(&who), "cannot retract non-voter"); + let voters = Self::voters(); + let index = index as usize; + ensure!(index < voters.len(), "retraction index invalid"); + ensure!(voters[index] == who, "retraction index mismatch"); + + Self::remove_voter(&who, index, voters); + >::unreserve(&who, Self::voting_bond()); + Ok(()) + } + + /// Submit oneself for candidacy. + /// + /// Account must have enough transferrable funds in it to pay the bond. + fn submit_candidacy(origin: T::Origin, slot: u32) -> Result { + 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. + >::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); + Ok(()) + } + + /// 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 + fn present_winner( + origin: T::Origin, + candidate: Address, + total: T::Balance, + index: VoteIndex + ) -> Result { + let who = ensure_signed(origin)?; + + let candidate = >::lookup(candidate)?; + ensure!(index == Self::vote_index(), "index not current"); + let (_, _, expiring) = Self::next_finalise().ok_or("cannot present outside of presentation period")?; + let stakes = Self::snapshoted_stakes(); + let voters = Self::voters(); + let bad_presentation_punishment = Self::present_slash_per_voter() * T::Balance::sa(voters.len() as u64); + ensure!(>::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 (registered_since, candidate_index): (VoteIndex, u32) = + Self::candidate_reg_info(&candidate).ok_or("presented candidate must be current")?; + let actual_total = voters.iter() + .zip(stakes.iter()) + .filter_map(|(voter, stake)| + match Self::voter_last_active(voter) { + Some(b) if b >= registered_since => + Self::approvals_of(voter).get(candidate_index as usize) + .and_then(|approved| if *approved { Some(*stake) } 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 _ = >::slash(&who, bad_presentation_punishment); + 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: T::Origin, count: u32) -> Result { + ensure_root(origin)?; + >::put(count); + Ok(()) + } + + /// Remove a particular member. A tally will happen instantly (if not already in a presentation + /// period) to fill the seat if removal means that the desired members are not met. + /// This is effective immediately. + fn remove_member(origin: T::Origin, who: Address) -> Result { + ensure_root(origin)?; + let who = >::lookup(who)?; + let new_council: Vec<(T::AccountId, T::BlockNumber)> = Self::active_council() + .into_iter() + .filter(|i| i.0 != who) + .collect(); + >::put(new_council); + Ok(()) + } + + /// Set the presentation duration. If there is currently a vote being presented for, will + /// invoke `finalise_vote`. + fn set_presentation_duration(origin: T::Origin, count: T::BlockNumber) -> Result { + ensure_root(origin)?; + >::put(count); + Ok(()) + } + + /// Set the presentation duration. If there is current a vote being presented for, will + /// invoke `finalise_vote`. + fn set_term_duration(origin: T::Origin, count: T::BlockNumber) -> Result { + ensure_root(origin)?; + >::put(count); + Ok(()) + } + + // 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_finalise() { + if block_number == number { + Self::finalise_tally()? + } + } + Ok(()) + } + + /// Remove a voter from the system. Trusts that Self::voters()[index] != voter. + fn remove_voter(voter: &T::AccountId, index: usize, mut voters: Vec) { + >::put({ voters.swap_remove(index); voters }); + >::remove(voter); + >::remove(voter); + } + + /// Close the voting, snapshot the staking and 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::>(); + if active_council.len() - expiring.len() < desired_seats { + let empty_seats = desired_seats - (active_council.len() - expiring.len()); + >::put((number + Self::presentation_duration(), empty_seats as u32, expiring)); + + let voters = Self::voters(); + let votes = voters.iter().map(>::total_balance).collect::>(); + >::put(votes); + + // initialise leaderboard. + let leaderboard_size = empty_seats + Self::carry_count() as usize; + >::put(vec![(T::Balance::zero(), T::AccountId::default()); leaderboard_size]); + + Self::deposit_event(RawEvent::TallyStarted(empty_seats as u32)); + } + } + + /// Finalise 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 finalise_tally() -> Result { + >::kill(); + let (_, coming, expiring): (T::BlockNumber, u32, Vec) = + >::take().ok_or("finalise can only be called after a tally is started.")?; + let leaderboard: Vec<(T::Balance, 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| {>::unreserve(a, candidacy_bond);}) + .collect(); + let active_council = Self::active_council(); + let outgoing = 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); + + // 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::TallyFinalised(incoming, outgoing)); + + >::put(new_candidates); + >::put(count); + >::put(Self::vote_index() + 1); + Ok(()) + } +} + +impl OnFinalise for Module { + fn on_finalise(n: T::BlockNumber) { + if let Err(e) = Self::end_block(n) { + print("Guru meditation"); + print(e); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ::tests::*; + + #[test] + fn params_should_work() { + with_externalities(&mut new_test_ext(false), || { + 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(), 9); + assert_eq!(Council::voting_bond(), 3); + assert_eq!(Council::present_slash_per_voter(), 1); + assert_eq!(Council::presentation_duration(), 2); + 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_finalise(), 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(), Vec::::new()); + assert_eq!(Council::voter_last_active(1), None); + assert_eq!(Council::approvals_of(1), vec![]); + }); + } + + #[test] + fn simple_candidate_submission_should_work() { + with_externalities(&mut new_test_ext(false), || { + 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 = new_test_ext(false); + 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() { + with_externalities(&mut new_test_ext_with_candidate_holes(), || { + 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 new_test_ext(false), || { + 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 new_test_ext(false), || { + 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 new_test_ext(false), || { + 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 new_test_ext(false), || { + 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 voting_should_work() { + with_externalities(&mut new_test_ext(false), || { + 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)); + assert_ok!(Council::set_approvals(Origin::signed(4), vec![true], 0)); + + assert_eq!(Council::approvals_of(1), vec![true]); + assert_eq!(Council::approvals_of(4), vec![true]); + assert_eq!(Council::voters(), 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)); + assert_ok!(Council::set_approvals(Origin::signed(3), vec![false, true, true], 0)); + + assert_eq!(Council::approvals_of(1), vec![true]); + assert_eq!(Council::approvals_of(4), vec![true]); + assert_eq!(Council::approvals_of(2), vec![false, true, true]); + assert_eq!(Council::approvals_of(3), vec![false, true, true]); + + assert_eq!(Council::voters(), vec![1, 4, 2, 3]); + }); + } + + #[test] + fn resubmitting_voting_should_work() { + with_externalities(&mut new_test_ext(false), || { + 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)); + + assert_eq!(Council::approvals_of(4), vec![true]); + + 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(4), vec![true, false, true], 0)); + + assert_eq!(Council::approvals_of(4), vec![true, false, true]); + }); + } + + #[test] + fn retracting_voter_should_work() { + with_externalities(&mut new_test_ext(false), || { + 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_ok!(Council::set_approvals(Origin::signed(1), vec![true], 0)); + assert_ok!(Council::set_approvals(Origin::signed(2), vec![false, true, true], 0)); + assert_ok!(Council::set_approvals(Origin::signed(3), vec![false, true, true], 0)); + assert_ok!(Council::set_approvals(Origin::signed(4), vec![true, false, true], 0)); + + assert_eq!(Council::voters(), vec![1, 2, 3, 4]); + assert_eq!(Council::approvals_of(1), vec![true]); + assert_eq!(Council::approvals_of(2), vec![false, true, true]); + assert_eq!(Council::approvals_of(3), vec![false, true, true]); + assert_eq!(Council::approvals_of(4), vec![true, false, true]); + + assert_ok!(Council::retract_voter(Origin::signed(1), 0)); + + assert_eq!(Council::voters(), vec![4, 2, 3]); + assert_eq!(Council::approvals_of(1), Vec::::new()); + assert_eq!(Council::approvals_of(2), vec![false, true, true]); + assert_eq!(Council::approvals_of(3), vec![false, true, true]); + assert_eq!(Council::approvals_of(4), vec![true, false, true]); + + assert_ok!(Council::retract_voter(Origin::signed(2), 1)); + + assert_eq!(Council::voters(), vec![4, 3]); + assert_eq!(Council::approvals_of(1), Vec::::new()); + assert_eq!(Council::approvals_of(2), Vec::::new()); + assert_eq!(Council::approvals_of(3), vec![false, true, true]); + assert_eq!(Council::approvals_of(4), vec![true, false, true]); + + assert_ok!(Council::retract_voter(Origin::signed(3), 1)); + + assert_eq!(Council::voters(), vec![4]); + assert_eq!(Council::approvals_of(1), Vec::::new()); + assert_eq!(Council::approvals_of(2), Vec::::new()); + assert_eq!(Council::approvals_of(3), Vec::::new()); + assert_eq!(Council::approvals_of(4), vec![true, false, true]); + }); + } + + #[test] + fn invalid_retraction_index_should_not_work() { + with_externalities(&mut new_test_ext(false), || { + 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)); + assert_ok!(Council::set_approvals(Origin::signed(2), vec![true], 0)); + assert_eq!(Council::voters(), 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 new_test_ext(false), || { + 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)); + assert_noop!(Council::retract_voter(Origin::signed(1), 1), "retraction index invalid"); + }); + } + + #[test] + fn non_voter_retraction_should_not_work() { + with_externalities(&mut new_test_ext(false), || { + 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)); + assert_noop!(Council::retract_voter(Origin::signed(2), 0), "cannot retract non-voter"); + }); + } + + #[test] + fn simple_tally_should_work() { + with_externalities(&mut new_test_ext(false), || { + 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, false], 0)); + assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, true], 0)); + assert_eq!(Council::voters(), vec![2, 5]); + assert_eq!(Council::approvals_of(2), vec![true, false]); + assert_eq!(Council::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.into(), 20, 0), Ok(())); + assert_eq!(Council::present_winner(Origin::signed(4), 5.into(), 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_last_active(2), Some(0)); + assert_eq!(Council::voter_last_active(5), Some(0)); + }); + } + + #[test] + fn double_presentations_should_be_punished() { + with_externalities(&mut new_test_ext(false), || { + 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)); + assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, true], 0)); + assert_ok!(Council::end_block(System::block_number())); + + System::set_block_number(6); + assert_ok!(Council::present_winner(Origin::signed(4), 2.into(), 20, 0)); + assert_ok!(Council::present_winner(Origin::signed(4), 5.into(), 50, 0)); + assert_eq!(Council::present_winner(Origin::signed(4), 5.into(), 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 new_test_ext(false), || { + 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)); + assert_ok!(Council::end_block(System::block_number())); + + System::set_block_number(6); + assert_ok!(Council::present_winner(Origin::signed(4), 2.into(), 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)); + assert_ok!(Council::end_block(System::block_number())); + + System::set_block_number(10); + assert_ok!(Council::present_winner(Origin::signed(4), 5.into(), 50, 1)); + assert_ok!(Council::end_block(System::block_number())); + + assert_ok!(Council::reap_inactive_voter(Origin::signed(5), + Council::voters().iter().position(|&i| i == 5).unwrap() as u32, + 2.into(), Council::voters().iter().position(|&i| i == 2).unwrap() as u32, + 2 + )); + + assert_eq!(Council::voters(), vec![5]); + assert_eq!(Council::approvals_of(2).len(), 0); + assert_eq!(Balances::total_balance(&2), 17); + assert_eq!(Balances::total_balance(&5), 53); + }); + } + + #[test] + fn presenting_for_double_election_should_not_work() { + with_externalities(&mut new_test_ext(false), || { + 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)); + assert_ok!(Council::end_block(System::block_number())); + + System::set_block_number(6); + assert_ok!(Council::present_winner(Origin::signed(4), 2.into(), 20, 0)); + assert_ok!(Council::end_block(System::block_number())); + + System::set_block_number(8); + assert_eq!(Council::submit_candidacy(Origin::signed(2), 0), Ok(())); + assert_ok!(Council::set_approvals(Origin::signed(2), vec![true], 1)); + assert_ok!(Council::end_block(System::block_number())); + + System::set_block_number(10); + assert_noop!(Council::present_winner(Origin::signed(4), 2.into(), 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 new_test_ext(false), || { + 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)); + assert_ok!(Council::end_block(System::block_number())); + + System::set_block_number(6); + assert_ok!(Council::present_winner(Origin::signed(4), 2.into(), 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)); + assert_ok!(Council::end_block(System::block_number())); + + System::set_block_number(10); + assert_ok!(Council::present_winner(Origin::signed(4), 5.into(), 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), + Council::voters().iter().position(|&i| i == 5).unwrap() as u32, + 2.into(), Council::voters().iter().position(|&i| i == 2).unwrap() as u32, + 2 + )); + + assert_eq!(Council::voters(), vec![5]); + assert_eq!(Council::approvals_of(2).len(), 0); + assert_eq!(Balances::total_balance(&2), 17); + assert_eq!(Balances::total_balance(&5), 53); + }); + } + + #[test] + fn retracting_inactive_voter_with_bad_reporter_index_should_not_work() { + with_externalities(&mut new_test_ext(false), || { + 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)); + assert_ok!(Council::end_block(System::block_number())); + + System::set_block_number(6); + assert_ok!(Council::present_winner(Origin::signed(4), 2.into(), 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)); + assert_ok!(Council::end_block(System::block_number())); + + System::set_block_number(10); + assert_ok!(Council::present_winner(Origin::signed(4), 5.into(), 50, 1)); + assert_ok!(Council::end_block(System::block_number())); + + assert_noop!(Council::reap_inactive_voter(Origin::signed(2), + 42, + 2.into(), Council::voters().iter().position(|&i| i == 2).unwrap() as u32, + 2 + ), "bad reporter index"); + }); + } + + #[test] + fn retracting_inactive_voter_with_bad_target_index_should_not_work() { + with_externalities(&mut new_test_ext(false), || { + 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)); + assert_ok!(Council::end_block(System::block_number())); + + System::set_block_number(6); + assert_ok!(Council::present_winner(Origin::signed(4), 2.into(), 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)); + assert_ok!(Council::end_block(System::block_number())); + + System::set_block_number(10); + assert_ok!(Council::present_winner(Origin::signed(4), 5.into(), 50, 1)); + assert_ok!(Council::end_block(System::block_number())); + + assert_noop!(Council::reap_inactive_voter(Origin::signed(2), + Council::voters().iter().position(|&i| i == 2).unwrap() as u32, + 2.into(), 42, + 2 + ), "bad target index"); + }); + } + + #[test] + fn attempting_to_retract_active_voter_should_slash_reporter() { + with_externalities(&mut new_test_ext(false), || { + 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)); + assert_ok!(Council::set_approvals(Origin::signed(3), vec![false, true, false, false], 0)); + assert_ok!(Council::set_approvals(Origin::signed(4), vec![false, false, true, false], 0)); + assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, false, false, true], 0)); + assert_ok!(Council::end_block(System::block_number())); + + System::set_block_number(6); + assert_ok!(Council::present_winner(Origin::signed(4), 2.into(), 20, 0)); + assert_ok!(Council::present_winner(Origin::signed(4), 3.into(), 30, 0)); + assert_ok!(Council::present_winner(Origin::signed(4), 4.into(), 40, 0)); + assert_ok!(Council::present_winner(Origin::signed(4), 5.into(), 50, 0)); + assert_ok!(Council::end_block(System::block_number())); + + System::set_block_number(8); + assert_ok!(Council::set_desired_seats(Origin::ROOT, 3)); + assert_ok!(Council::end_block(System::block_number())); + + System::set_block_number(10); + assert_ok!(Council::present_winner(Origin::signed(4), 2.into(), 20, 1)); + assert_ok!(Council::present_winner(Origin::signed(4), 3.into(), 30, 1)); + assert_ok!(Council::end_block(System::block_number())); + + assert_ok!(Council::reap_inactive_voter(Origin::signed(4), + Council::voters().iter().position(|&i| i == 4).unwrap() as u32, + 2.into(), Council::voters().iter().position(|&i| i == 2).unwrap() as u32, + 2 + )); + + assert_eq!(Council::voters(), vec![2, 3, 5]); + assert_eq!(Council::approvals_of(4).len(), 0); + assert_eq!(Balances::total_balance(&4), 37); + }); + } + + #[test] + fn attempting_to_retract_inactive_voter_by_nonvoter_should_not_work() { + with_externalities(&mut new_test_ext(false), || { + 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)); + assert_ok!(Council::end_block(System::block_number())); + + System::set_block_number(6); + assert_ok!(Council::present_winner(Origin::signed(4), 2.into(), 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)); + assert_ok!(Council::end_block(System::block_number())); + + System::set_block_number(10); + assert_ok!(Council::present_winner(Origin::signed(4), 5.into(), 50, 1)); + assert_ok!(Council::end_block(System::block_number())); + + assert_noop!(Council::reap_inactive_voter(Origin::signed(4), + 0, + 2.into(), Council::voters().iter().position(|&i| i == 2).unwrap() as u32, + 2 + ), "reporter must be a voter"); + }); + } + + #[test] + fn presenting_loser_should_not_work() { + with_externalities(&mut new_test_ext(false), || { + 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)); + assert_ok!(Council::submit_candidacy(Origin::signed(2), 1)); + assert_ok!(Council::set_approvals(Origin::signed(2), vec![false, true], 0)); + assert_ok!(Council::submit_candidacy(Origin::signed(3), 2)); + assert_ok!(Council::set_approvals(Origin::signed(3), vec![false, false, true], 0)); + assert_ok!(Council::submit_candidacy(Origin::signed(4), 3)); + assert_ok!(Council::set_approvals(Origin::signed(4), vec![false, false, false, true], 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)); + assert_ok!(Council::end_block(System::block_number())); + + System::set_block_number(6); + assert_ok!(Council::present_winner(Origin::signed(4), 1.into(), 60, 0)); + assert_ok!(Council::present_winner(Origin::signed(4), 3.into(), 30, 0)); + assert_ok!(Council::present_winner(Origin::signed(4), 4.into(), 40, 0)); + assert_ok!(Council::present_winner(Origin::signed(4), 5.into(), 50, 0)); + assert_noop!(Council::present_winner(Origin::signed(4), 2.into(), 20, 0), "candidate not worthy of leaderboard"); + }); + } + + #[test] + fn presenting_loser_first_should_not_matter() { + with_externalities(&mut new_test_ext(false), || { + 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)); + assert_ok!(Council::submit_candidacy(Origin::signed(2), 1)); + assert_ok!(Council::set_approvals(Origin::signed(2), vec![false, true], 0)); + assert_ok!(Council::submit_candidacy(Origin::signed(3), 2)); + assert_ok!(Council::set_approvals(Origin::signed(3), vec![false, false, true], 0)); + assert_ok!(Council::submit_candidacy(Origin::signed(4), 3)); + assert_ok!(Council::set_approvals(Origin::signed(4), vec![false, false, false, true], 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)); + assert_ok!(Council::end_block(System::block_number())); + + System::set_block_number(6); + assert_ok!(Council::present_winner(Origin::signed(4), 2.into(), 20, 0)); + assert_ok!(Council::present_winner(Origin::signed(4), 1.into(), 60, 0)); + assert_ok!(Council::present_winner(Origin::signed(4), 3.into(), 30, 0)); + assert_ok!(Council::present_winner(Origin::signed(4), 4.into(), 40, 0)); + assert_ok!(Council::present_winner(Origin::signed(4), 5.into(), 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 new_test_ext(false), || { + System::set_block_number(4); + assert!(!Council::presentation_active()); + assert_noop!(Council::present_winner(Origin::signed(5), 5.into(), 1, 0), "cannot present outside of presentation period"); + }); + } + + #[test] + fn present_with_invalid_vote_index_should_not_work() { + with_externalities(&mut new_test_ext(false), || { + 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)); + assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, true], 0)); + assert_ok!(Council::end_block(System::block_number())); + + System::set_block_number(6); + assert_noop!(Council::present_winner(Origin::signed(4), 2.into(), 20, 1), "index not current"); + }); + } + + #[test] + fn present_when_presenter_is_poor_should_not_work() { + with_externalities(&mut new_test_ext(false), || { + System::set_block_number(4); + assert!(!Council::presentation_active()); + + assert_ok!(Council::submit_candidacy(Origin::signed(1), 0)); + assert_ok!(Council::submit_candidacy(Origin::signed(5), 1)); + assert_ok!(Council::set_approvals(Origin::signed(2), vec![true, false], 0)); + assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, true], 0)); + assert_ok!(Council::end_block(System::block_number())); + + System::set_block_number(6); + assert_eq!(Balances::free_balance(&1), 1); + assert_eq!(Balances::reserved_balance(&1), 9); + assert_noop!(Council::present_winner(Origin::signed(1), 1.into(), 20, 0), "presenter must have sufficient slashable funds"); + }); + } + + #[test] + fn invalid_present_tally_should_slash() { + with_externalities(&mut new_test_ext(false), || { + 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)); + assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, true], 0)); + assert_ok!(Council::end_block(System::block_number())); + + System::set_block_number(6); + assert_err!(Council::present_winner(Origin::signed(4), 2.into(), 80, 0), "incorrect total"); + + assert_eq!(Balances::total_balance(&4), 38); + }); + } + + #[test] + fn runners_up_should_be_kept() { + with_externalities(&mut new_test_ext(false), || { + 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)); + assert_ok!(Council::submit_candidacy(Origin::signed(2), 1)); + assert_ok!(Council::set_approvals(Origin::signed(2), vec![false, true], 0)); + assert_ok!(Council::submit_candidacy(Origin::signed(3), 2)); + assert_ok!(Council::set_approvals(Origin::signed(3), vec![false, false, true], 0)); + assert_ok!(Council::submit_candidacy(Origin::signed(4), 3)); + assert_ok!(Council::set_approvals(Origin::signed(4), vec![false, false, false, true], 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)); + + 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.into(), 60, 0)); + assert_eq!(Council::leaderboard(), Some(vec![ + (0, 0), + (0, 0), + (0, 0), + (60, 1) + ])); + assert_ok!(Council::present_winner(Origin::signed(4), 3.into(), 30, 0)); + assert_ok!(Council::present_winner(Origin::signed(4), 4.into(), 40, 0)); + assert_ok!(Council::present_winner(Origin::signed(4), 5.into(), 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_last_active(2), Some(0)); + assert_eq!(Council::voter_last_active(3), Some(0)); + assert_eq!(Council::voter_last_active(4), Some(0)); + assert_eq!(Council::voter_last_active(5), Some(0)); + assert_eq!(Council::voter_last_active(6), Some(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 new_test_ext(false), || { + 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)); + assert_ok!(Council::submit_candidacy(Origin::signed(2), 1)); + assert_ok!(Council::set_approvals(Origin::signed(2), vec![false, true], 0)); + assert_ok!(Council::submit_candidacy(Origin::signed(3), 2)); + assert_ok!(Council::set_approvals(Origin::signed(3), vec![false, false, true], 0)); + assert_ok!(Council::submit_candidacy(Origin::signed(4), 3)); + assert_ok!(Council::set_approvals(Origin::signed(4), vec![false, false, false, true], 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)); + assert_ok!(Council::end_block(System::block_number())); + + System::set_block_number(6); + assert_ok!(Council::present_winner(Origin::signed(4), 1.into(), 60, 0)); + assert_ok!(Council::present_winner(Origin::signed(4), 3.into(), 30, 0)); + assert_ok!(Council::present_winner(Origin::signed(4), 4.into(), 40, 0)); + assert_ok!(Council::present_winner(Origin::signed(4), 5.into(), 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)); + assert_ok!(Council::set_desired_seats(Origin::ROOT, 3)); + assert_ok!(Council::end_block(System::block_number())); + + System::set_block_number(10); + assert_ok!(Council::present_winner(Origin::signed(4), 3.into(), 90, 1)); + assert_ok!(Council::present_winner(Origin::signed(4), 4.into(), 40, 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_last_active(2), Some(0)); + assert_eq!(Council::voter_last_active(3), Some(0)); + assert_eq!(Council::voter_last_active(4), Some(0)); + assert_eq!(Council::voter_last_active(5), Some(0)); + assert_eq!(Council::voter_last_active(6), Some(1)); + + assert_eq!(Council::candidate_reg_info(4), Some((0, 3))); + }); + } +} diff --git a/runtime/council/src/voting.rs b/runtime/council/src/voting.rs new file mode 100644 index 0000000000000..450eb034f12cc --- /dev/null +++ b/runtime/council/src/voting.rs @@ -0,0 +1,505 @@ +// Copyright 2017 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::*; +use rstd::borrow::Borrow; +use primitives::traits::{OnFinalise, Hash}; +use runtime_io::print; +use substrate_runtime_support::dispatch::Result; +use substrate_runtime_support::{StorageValue, StorageMap, IsSubType}; +use {system, democracy}; +use super::{Trait as CouncilTrait, Module as Council}; +use system::{ensure_signed, ensure_root}; + +pub trait Trait: CouncilTrait { + type Event: From> + Into<::Event>; +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + fn propose(origin, proposal: Box) -> Result; + fn vote(origin, proposal: T::Hash, approve: bool) -> Result; + fn veto(origin, proposal_hash: T::Hash) -> Result; + + fn set_cooloff_period(origin, blocks: T::BlockNumber) -> Result; + fn set_voting_period(origin, blocks: T::BlockNumber) -> Result; + } +} + +decl_storage! { + trait Store for Module as CouncilVoting { + pub CooloffPeriod get(cooloff_period): required T::BlockNumber; + pub VotingPeriod get(voting_period): required T::BlockNumber; + pub Proposals get(proposals): required Vec<(T::BlockNumber, T::Hash)>; // ordered by expiry. + pub ProposalOf get(proposal_of): map [ T::Hash => T::Proposal ]; + pub ProposalVoters get(proposal_voters): default map [ T::Hash => Vec ]; + pub CouncilVoteOf get(vote_of): map [ (T::Hash, T::AccountId) => bool ]; + pub VetoedProposal get(veto_of): map [ T::Hash => (T::BlockNumber, Vec) ]; + } +} + +/// An event in this module. +decl_event!( + pub enum Event with RawEvent + where ::Hash + { + /// A voting tally has happened for a referendum cancelation vote. + /// Last three are yes, no, abstain counts. + TallyCancelation(Hash, u32, u32, u32), + /// A voting tally has happened for a referendum vote. + /// Last three are yes, no, abstain counts. + TallyReferendum(Hash, u32, u32, u32), + } +); + +impl Module { + + /// Deposit one of this module's events. + fn deposit_event(event: Event) { + >::deposit_event(::Event::from(event).into()); + } + + pub fn is_vetoed>(proposal: B) -> bool { + Self::veto_of(proposal.borrow()) + .map(|(expiry, _): (T::BlockNumber, Vec)| >::block_number() < expiry) + .unwrap_or(false) + } + + pub fn will_still_be_councillor_at(who: &T::AccountId, n: T::BlockNumber) -> bool { + >::active_council().iter() + .find(|&&(ref a, _)| a == who) + .map(|&(_, expires)| expires > n) + .unwrap_or(false) + } + + pub fn is_councillor(who: &T::AccountId) -> bool { + >::active_council().iter() + .any(|&(ref a, _)| a == who) + } + + pub fn tally(proposal_hash: &T::Hash) -> (u32, u32, u32) { + Self::generic_tally(proposal_hash, |w: &T::AccountId, p: &T::Hash| Self::vote_of((*p, w.clone()))) + } + + // Dispatch + fn propose(origin: T::Origin, proposal: Box) -> Result { + let who = ensure_signed(origin)?; + + let expiry = >::block_number() + Self::voting_period(); + ensure!(Self::will_still_be_councillor_at(&who, expiry), "proposer would not be on council"); + + let proposal_hash = T::Hashing::hash_of(&proposal); + + ensure!(!>::exists(proposal_hash), "duplicate proposals not allowed"); + ensure!(!Self::is_vetoed(&proposal_hash), "proposal is vetoed"); + + let mut proposals = Self::proposals(); + proposals.push((expiry, proposal_hash)); + proposals.sort_by_key(|&(expiry, _)| expiry); + Self::set_proposals(&proposals); + + >::insert(proposal_hash, *proposal); + >::insert(proposal_hash, vec![who.clone()]); + >::insert((proposal_hash, who.clone()), true); + + Ok(()) + } + + fn vote(origin: T::Origin, proposal: T::Hash, approve: bool) -> Result { + let who = ensure_signed(origin)?; + + ensure!(Self::is_councillor(&who), "only councillors may vote on council proposals"); + + if Self::vote_of((proposal, who.clone())).is_none() { + >::mutate(proposal, |voters| voters.push(who.clone())); + } + >::insert((proposal, who), approve); + Ok(()) + } + + fn veto(origin: T::Origin, proposal_hash: T::Hash) -> Result { + let who = ensure_signed(origin)?; + + ensure!(Self::is_councillor(&who), "only councillors may veto council proposals"); + ensure!(>::exists(&proposal_hash), "proposal must exist to be vetoed"); + + let mut existing_vetoers = Self::veto_of(&proposal_hash) + .map(|pair| pair.1) + .unwrap_or_else(Vec::new); + let insert_position = existing_vetoers.binary_search(&who) + .err().ok_or("a councillor may not veto a proposal twice")?; + existing_vetoers.insert(insert_position, who); + Self::set_veto_of(&proposal_hash, >::block_number() + Self::cooloff_period(), existing_vetoers); + + Self::set_proposals(&Self::proposals().into_iter().filter(|&(_, h)| h != proposal_hash).collect::>()); + >::remove(proposal_hash); + >::remove(proposal_hash); + for (c, _) in >::active_council() { + >::remove((proposal_hash, c)); + } + Ok(()) + } + + fn set_cooloff_period(origin: T::Origin, blocks: T::BlockNumber) -> Result { + ensure_root(origin)?; + >::put(blocks); + Ok(()) + } + + fn set_voting_period(origin: T::Origin, blocks: T::BlockNumber) -> Result { + ensure_root(origin)?; + >::put(blocks); + Ok(()) + } + + // private + + + fn set_veto_of(proposal: &T::Hash, expiry: T::BlockNumber, vetoers: Vec) { + >::insert(proposal, (expiry, vetoers)); + } + + fn kill_veto_of(proposal: &T::Hash) { + >::remove(proposal); + } + + fn take_tally(proposal_hash: &T::Hash) -> (u32, u32, u32) { + Self::generic_tally(proposal_hash, |w: &T::AccountId, p: &T::Hash| >::take((*p, w.clone()))) + } + + fn generic_tally Option>(proposal_hash: &T::Hash, vote_of: F) -> (u32, u32, u32) { + let c = >::active_council(); + let (approve, reject) = c.iter() + .filter_map(|&(ref a, _)| vote_of(a, proposal_hash)) + .map(|approve| if approve { (1, 0) } else { (0, 1) }) + .fold((0, 0), |(a, b), (c, d)| (a + c, b + d)); + (approve, reject, c.len() as u32 - approve - reject) + } + + fn set_proposals(p: &Vec<(T::BlockNumber, T::Hash)>) { + >::put(p); + } + + fn take_proposal_if_expiring_at(n: T::BlockNumber) -> Option<(T::Proposal, T::Hash)> { + let proposals = Self::proposals(); + match proposals.first() { + Some(&(expiry, hash)) if expiry == n => { + // yes this is horrible, but fixing it will need substantial work in storage. + Self::set_proposals(&proposals[1..].to_vec()); + >::take(hash).map(|p| (p, hash)) /* defensive only: all queued proposal hashes must have associated proposals*/ + } + _ => None, + } + } + + fn end_block(now: T::BlockNumber) -> Result { + while let Some((proposal, proposal_hash)) = Self::take_proposal_if_expiring_at(now) { + let tally = Self::take_tally(&proposal_hash); + if let Some(&democracy::Call::cancel_referendum(ref_index)) = IsSubType::>::is_aux_sub_type(&proposal) { + Self::deposit_event(RawEvent::TallyCancelation(proposal_hash, tally.0, tally.1, tally.2)); + if let (_, 0, 0) = tally { + >::internal_cancel_referendum(ref_index); + } + } else { + Self::deposit_event(RawEvent::TallyReferendum(proposal_hash.clone(), tally.0, tally.1, tally.2)); + if tally.0 > tally.1 + tally.2 { + Self::kill_veto_of(&proposal_hash); + match tally { + (_, 0, 0) => >::internal_start_referendum(proposal, democracy::VoteThreshold::SuperMajorityAgainst).map(|_| ())?, + _ => >::internal_start_referendum(proposal, democracy::VoteThreshold::SimpleMajority).map(|_| ())?, + }; + } + } + } + Ok(()) + } +} + +impl OnFinalise for Module { + fn on_finalise(n: T::BlockNumber) { + if let Err(e) = Self::end_block(n) { + print("Guru meditation"); + print(e); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ::tests::*; + use ::tests::{Call, Origin}; + use substrate_runtime_support::Hashable; + use democracy::VoteThreshold; + + #[test] + fn basic_environment_works() { + with_externalities(&mut new_test_ext(true), || { + System::set_block_number(1); + assert_eq!(Balances::free_balance(&42), 0); + assert_eq!(CouncilVoting::cooloff_period(), 2); + assert_eq!(CouncilVoting::voting_period(), 1); + assert_eq!(CouncilVoting::will_still_be_councillor_at(&1, 1), true); + assert_eq!(CouncilVoting::will_still_be_councillor_at(&1, 10), false); + assert_eq!(CouncilVoting::will_still_be_councillor_at(&4, 10), false); + assert_eq!(CouncilVoting::is_councillor(&1), true); + assert_eq!(CouncilVoting::is_councillor(&4), false); + assert_eq!(CouncilVoting::proposals(), Vec::<(u64, H256)>::new()); + assert_eq!(CouncilVoting::proposal_voters(H256::default()), Vec::::new()); + assert_eq!(CouncilVoting::is_vetoed(&H256::default()), false); + assert_eq!(CouncilVoting::vote_of((H256::default(), 1)), None); + assert_eq!(CouncilVoting::tally(&H256::default()), (0, 0, 3)); + }); + } + + fn set_balance_proposal(value: u64) -> Call { + Call::Balances(balances::Call::set_balance(balances::address::Address::Id(42), value, 0)) + } + + fn cancel_referendum_proposal(id: u32) -> Call { + Call::Democracy(democracy::Call::cancel_referendum(id)) + } + + #[test] + fn referendum_cancellation_should_work_when_unanimous() { + with_externalities(&mut new_test_ext(true), || { + System::set_block_number(1); + let proposal = set_balance_proposal(42); + assert_ok!(Democracy::internal_start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove), 0); + assert_eq!(Democracy::active_referendums(), vec![(0, 4, proposal, VoteThreshold::SuperMajorityApprove)]); + + let cancellation = cancel_referendum_proposal(0); + let hash = cancellation.blake2_256().into(); + assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(cancellation))); + assert_ok!(CouncilVoting::vote(Origin::signed(2), hash, true)); + assert_ok!(CouncilVoting::vote(Origin::signed(3), hash, true)); + assert_eq!(CouncilVoting::proposals(), vec![(2, hash)]); + assert_ok!(CouncilVoting::end_block(System::block_number())); + + System::set_block_number(2); + assert_ok!(CouncilVoting::end_block(System::block_number())); + assert_eq!(Democracy::active_referendums(), vec![]); + assert_eq!(Balances::free_balance(&42), 0); + }); + } + + #[test] + fn referendum_cancellation_should_fail_when_not_unanimous() { + with_externalities(&mut new_test_ext(true), || { + System::set_block_number(1); + let proposal = set_balance_proposal(42); + assert_ok!(Democracy::internal_start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove), 0); + + let cancellation = cancel_referendum_proposal(0); + let hash = cancellation.blake2_256().into(); + assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(cancellation))); + assert_ok!(CouncilVoting::vote(Origin::signed(2), hash, true)); + assert_ok!(CouncilVoting::vote(Origin::signed(3), hash, false)); + assert_ok!(CouncilVoting::end_block(System::block_number())); + + System::set_block_number(2); + assert_ok!(CouncilVoting::end_block(System::block_number())); + assert_eq!(Democracy::active_referendums(), vec![(0, 4, proposal, VoteThreshold::SuperMajorityApprove)]); + }); + } + + #[test] + fn referendum_cancellation_should_fail_when_abstentions() { + with_externalities(&mut new_test_ext(true), || { + System::set_block_number(1); + let proposal = set_balance_proposal(42); + assert_ok!(Democracy::internal_start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove), 0); + + let cancellation = cancel_referendum_proposal(0); + let hash = cancellation.blake2_256().into(); + assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(cancellation))); + assert_ok!(CouncilVoting::vote(Origin::signed(2), hash, true)); + assert_ok!(CouncilVoting::end_block(System::block_number())); + + System::set_block_number(2); + assert_ok!(CouncilVoting::end_block(System::block_number())); + assert_eq!(Democracy::active_referendums(), vec![(0, 4, proposal, VoteThreshold::SuperMajorityApprove)]); + }); + } + + #[test] + fn veto_should_work() { + with_externalities(&mut new_test_ext(true), || { + System::set_block_number(1); + let proposal = set_balance_proposal(42); + let hash = proposal.blake2_256().into(); + assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone()))); + assert_ok!(CouncilVoting::veto(Origin::signed(2), hash)); + assert_eq!(CouncilVoting::proposals().len(), 0); + assert_eq!(Democracy::active_referendums().len(), 0); + }); + } + + #[test] + fn double_veto_should_not_work() { + with_externalities(&mut new_test_ext(true), || { + System::set_block_number(1); + let proposal = set_balance_proposal(42); + let hash = proposal.blake2_256().into(); + assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone()))); + assert_ok!(CouncilVoting::veto(Origin::signed(2), hash)); + + System::set_block_number(3); + assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone()))); + assert_noop!(CouncilVoting::veto(Origin::signed(2), hash), "a councillor may not veto a proposal twice"); + }); + } + + #[test] + fn retry_in_cooloff_should_not_work() { + with_externalities(&mut new_test_ext(true), || { + System::set_block_number(1); + let proposal = set_balance_proposal(42); + let hash = proposal.blake2_256().into(); + assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone()))); + assert_ok!(CouncilVoting::veto(Origin::signed(2), hash)); + + System::set_block_number(2); + assert_noop!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone())), "proposal is vetoed"); + }); + } + + #[test] + fn retry_after_cooloff_should_work() { + with_externalities(&mut new_test_ext(true), || { + System::set_block_number(1); + let proposal = set_balance_proposal(42); + let hash = proposal.blake2_256().into(); + assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone()))); + assert_ok!(CouncilVoting::veto(Origin::signed(2), hash)); + + System::set_block_number(3); + assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone()))); + assert_ok!(CouncilVoting::vote(Origin::signed(2), hash, false)); + assert_ok!(CouncilVoting::vote(Origin::signed(3), hash, true)); + assert_ok!(CouncilVoting::end_block(System::block_number())); + + System::set_block_number(4); + assert_ok!(CouncilVoting::end_block(System::block_number())); + assert_eq!(CouncilVoting::proposals().len(), 0); + assert_eq!(Democracy::active_referendums(), vec![(0, 7, set_balance_proposal(42), VoteThreshold::SimpleMajority)]); + }); + } + + #[test] + fn alternative_double_veto_should_work() { + with_externalities(&mut new_test_ext(true), || { + System::set_block_number(1); + let proposal = set_balance_proposal(42); + let hash = proposal.blake2_256().into(); + assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone()))); + assert_ok!(CouncilVoting::veto(Origin::signed(2), hash)); + + System::set_block_number(3); + assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone()))); + assert_ok!(CouncilVoting::veto(Origin::signed(3), hash)); + assert_eq!(CouncilVoting::proposals().len(), 0); + assert_eq!(Democracy::active_referendums().len(), 0); + }); + } + + #[test] + fn simple_propose_should_work() { + with_externalities(&mut new_test_ext(true), || { + System::set_block_number(1); + let proposal = set_balance_proposal(42); + let hash = proposal.blake2_256().into(); + assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone()))); + assert_eq!(CouncilVoting::proposals().len(), 1); + assert_eq!(CouncilVoting::proposal_voters(&hash), vec![1]); + assert_eq!(CouncilVoting::vote_of((hash, 1)), Some(true)); + assert_eq!(CouncilVoting::tally(&hash), (1, 0, 2)); + }); + } + + #[test] + fn unvoted_proposal_should_expire_without_action() { + with_externalities(&mut new_test_ext(true), || { + System::set_block_number(1); + let proposal = set_balance_proposal(42); + assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone()))); + assert_eq!(CouncilVoting::tally(&proposal.blake2_256().into()), (1, 0, 2)); + assert_ok!(CouncilVoting::end_block(System::block_number())); + + System::set_block_number(2); + assert_ok!(CouncilVoting::end_block(System::block_number())); + assert_eq!(CouncilVoting::proposals().len(), 0); + assert_eq!(Democracy::active_referendums().len(), 0); + }); + } + + #[test] + fn unanimous_proposal_should_expire_with_biased_referendum() { + with_externalities(&mut new_test_ext(true), || { + System::set_block_number(1); + let proposal = set_balance_proposal(42); + assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone()))); + assert_ok!(CouncilVoting::vote(Origin::signed(2), proposal.blake2_256().into(), true)); + assert_ok!(CouncilVoting::vote(Origin::signed(3), proposal.blake2_256().into(), true)); + assert_eq!(CouncilVoting::tally(&proposal.blake2_256().into()), (3, 0, 0)); + assert_ok!(CouncilVoting::end_block(System::block_number())); + + System::set_block_number(2); + assert_ok!(CouncilVoting::end_block(System::block_number())); + assert_eq!(CouncilVoting::proposals().len(), 0); + assert_eq!(Democracy::active_referendums(), vec![(0, 5, proposal, VoteThreshold::SuperMajorityAgainst)]); + }); + } + + #[test] + fn majority_proposal_should_expire_with_unbiased_referendum() { + with_externalities(&mut new_test_ext(true), || { + System::set_block_number(1); + let proposal = set_balance_proposal(42); + assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone()))); + assert_ok!(CouncilVoting::vote(Origin::signed(2), proposal.blake2_256().into(), true)); + assert_ok!(CouncilVoting::vote(Origin::signed(3), proposal.blake2_256().into(), false)); + assert_eq!(CouncilVoting::tally(&proposal.blake2_256().into()), (2, 1, 0)); + assert_ok!(CouncilVoting::end_block(System::block_number())); + + System::set_block_number(2); + assert_ok!(CouncilVoting::end_block(System::block_number())); + assert_eq!(CouncilVoting::proposals().len(), 0); + assert_eq!(Democracy::active_referendums(), vec![(0, 5, proposal, VoteThreshold::SimpleMajority)]); + }); + } + + #[test] + fn propose_by_public_should_not_work() { + with_externalities(&mut new_test_ext(true), || { + System::set_block_number(1); + let proposal = set_balance_proposal(42); + assert_noop!(CouncilVoting::propose(Origin::signed(4), Box::new(proposal)), "proposer would not be on council"); + }); + } + + #[test] + fn vote_by_public_should_not_work() { + with_externalities(&mut new_test_ext(true), || { + System::set_block_number(1); + let proposal = set_balance_proposal(42); + assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone()))); + assert_noop!(CouncilVoting::vote(Origin::signed(4), proposal.blake2_256().into(), true), "only councillors may vote on council proposals"); + }); + } +} diff --git a/runtime/democracy/Cargo.toml b/runtime/democracy/Cargo.toml new file mode 100644 index 0000000000000..88a94e6ba6ad2 --- /dev/null +++ b/runtime/democracy/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "substrate-runtime-democracy" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +hex-literal = "0.1.0" +serde = { version = "1.0", default_features = false } +serde_derive = { version = "1.0", optional = true } +safe-mix = { version = "1.0", default_features = false} +substrate-codec = { path = "../../core/codec", default_features = false } +substrate-codec-derive = { path = "../../core/codec/derive", default_features = false } +substrate-primitives = { path = "../../core/primitives", default_features = false } +substrate-runtime-std = { path = "../../core/runtime-std", default_features = false } +substrate-runtime-io = { path = "../../core/runtime-io", default_features = false } +substrate-runtime-support = { path = "../support", default_features = false } +substrate-runtime-primitives = { path = "../primitives", default_features = false } +substrate-runtime-balances = { path = "../balances", default_features = false } +substrate-runtime-consensus = { path = "../consensus", default_features = false } +substrate-runtime-system = { path = "../system", default_features = false } + +[features] +default = ["std"] +std = [ + "serde/std", + "serde_derive", + "safe-mix/std", + "substrate-codec/std", + "substrate-primitives/std", + "substrate-runtime-std/std", + "substrate-runtime-io/std", + "substrate-runtime-support/std", + "substrate-runtime-primitives/std", + "substrate-runtime-consensus/std", + "substrate-runtime-balances/std", + "substrate-runtime-system/std", +] diff --git a/runtime/democracy/src/lib.rs b/runtime/democracy/src/lib.rs new file mode 100644 index 0000000000000..12f2e7e7aa402 --- /dev/null +++ b/runtime/democracy/src/lib.rs @@ -0,0 +1,681 @@ +// Copyright 2017 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 . + +//! Democratic system: Handles administration of general stakeholder voting. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(feature = "std")] +extern crate serde; + +#[cfg(feature = "std")] +extern crate substrate_primitives; + +#[cfg(feature = "std")] +#[macro_use] +extern crate serde_derive; + +#[macro_use] +extern crate substrate_codec_derive; +#[macro_use] +extern crate substrate_runtime_std as rstd; +#[macro_use] +extern crate substrate_runtime_support; + +extern crate substrate_codec as codec; +extern crate substrate_runtime_io as runtime_io; +extern crate substrate_runtime_primitives as primitives; +extern crate substrate_runtime_balances as balances; +extern crate substrate_runtime_system as system; + +use rstd::prelude::*; +use rstd::result; +use primitives::traits::{Zero, OnFinalise, As, MaybeSerializeDebug}; +use substrate_runtime_support::{StorageValue, StorageMap, Parameter, Dispatchable, IsSubType}; +use substrate_runtime_support::dispatch::Result; +use system::{ensure_signed, ensure_root}; + +#[cfg(any(feature = "std", test))] +use std::collections::HashMap; + +mod vote_threshold; +pub use vote_threshold::{Approved, VoteThreshold}; + +/// A proposal index. +pub type PropIndex = u32; +/// A referendum index. +pub type ReferendumIndex = u32; + +pub trait Trait: balances::Trait + Sized { + type Proposal: Parameter + Dispatchable + IsSubType> + MaybeSerializeDebug; + + type Event: From> + Into<::Event>; +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + fn propose(origin, proposal: Box, value: T::Balance) -> Result; + fn second(origin, proposal: PropIndex) -> Result; + fn vote(origin, ref_index: ReferendumIndex, approve_proposal: bool) -> Result; + + fn start_referendum(origin, proposal: Box, vote_threshold: VoteThreshold) -> Result; + fn cancel_referendum(origin, ref_index: ReferendumIndex) -> Result; + } +} + +decl_storage! { + trait Store for Module as Democracy { + + /// The number of (public) proposals that have been made so far. + pub PublicPropCount get(public_prop_count): default PropIndex; + /// The public proposals. Unsorted. + pub PublicProps get(public_props): default Vec<(PropIndex, T::Proposal, T::AccountId)>; + /// Those who have locked a deposit. + pub DepositOf get(deposit_of): map [ PropIndex => (T::Balance, Vec) ]; + /// How often (in blocks) new public referenda are launched. + pub LaunchPeriod get(launch_period): required T::BlockNumber; + /// The minimum amount to be used as a deposit for a public referendum proposal. + pub MinimumDeposit get(minimum_deposit): required T::Balance; + + /// How often (in blocks) to check for new votes. + pub VotingPeriod get(voting_period): required T::BlockNumber; + + /// The next free referendum index, aka the number of referendums started so far. + pub ReferendumCount get(referendum_count): required ReferendumIndex; + /// The next referendum index that should be tallied. + pub NextTally get(next_tally): required ReferendumIndex; + /// Information concerning any given referendum. + pub ReferendumInfoOf get(referendum_info): map [ ReferendumIndex => (T::BlockNumber, T::Proposal, VoteThreshold) ]; + + /// Get the voters for the current proposal. + pub VotersFor get(voters_for): default map [ ReferendumIndex => Vec ]; + + /// Get the vote, if Some, of `who`. + pub VoteOf get(vote_of): map [ (ReferendumIndex, T::AccountId) => bool ]; + } +} + +decl_event!( + /// An event in this module. + pub enum Event with RawEvent + where ::Balance, ::AccountId + { + Tabled(PropIndex, Balance, Vec), + Started(ReferendumIndex, VoteThreshold), + Passed(ReferendumIndex), + NotPassed(ReferendumIndex), + Cancelled(ReferendumIndex), + Executed(ReferendumIndex, bool), + } +); + +impl Module { + + /// Deposit one of this module's events. + fn deposit_event(event: Event) { + >::deposit_event(::Event::from(event).into()); + } + + // exposed immutables. + + /// Get the amount locked in support of `proposal`; `None` if proposal isn't a valid proposal + /// index. + pub fn locked_for(proposal: PropIndex) -> Option { + Self::deposit_of(proposal).map(|(d, l)| d * T::Balance::sa(l.len() as u64)) + } + + /// Return true if `ref_index` is an on-going referendum. + pub fn is_active_referendum(ref_index: ReferendumIndex) -> bool { + >::exists(ref_index) + } + + /// Get all referendums currently active. + pub fn active_referendums() -> Vec<(ReferendumIndex, T::BlockNumber, T::Proposal, VoteThreshold)> { + let next = Self::next_tally(); + let last = Self::referendum_count(); + (next..last).into_iter() + .filter_map(|i| Self::referendum_info(i).map(|(n, p, t)| (i, n, p, t))) + .collect() + } + + /// Get all referendums ready for tally at block `n`. + pub fn maturing_referendums_at(n: T::BlockNumber) -> Vec<(ReferendumIndex, T::BlockNumber, T::Proposal, VoteThreshold)> { + let next = Self::next_tally(); + let last = Self::referendum_count(); + (next..last).into_iter() + .filter_map(|i| Self::referendum_info(i).map(|(n, p, t)| (i, n, p, t))) + .take_while(|&(_, block_number, _, _)| block_number == n) + .collect() + } + + /// Get the voters for the current proposal. + pub fn tally(ref_index: ReferendumIndex) -> (T::Balance, T::Balance) { + Self::voters_for(ref_index).iter() + .map(|a| (>::total_balance(a), Self::vote_of((ref_index, a.clone())).unwrap_or(false)/*defensive only: all items come from `voters`; for an item to be in `voters` there must be a vote registered; qed*/)) + .map(|(bal, vote)| if vote { (bal, Zero::zero()) } else { (Zero::zero(), bal) }) + .fold((Zero::zero(), Zero::zero()), |(a, b), (c, d)| (a + c, b + d)) + } + + // dispatching. + + /// Propose a sensitive action to be taken. + fn propose(origin: T::Origin, proposal: Box, value: T::Balance) -> Result { + let who = ensure_signed(origin)?; + ensure!(value >= Self::minimum_deposit(), "value too low"); + >::reserve(&who, value) + .map_err(|_| "proposer's balance too low")?; + + let index = Self::public_prop_count(); + >::put(index + 1); + >::insert(index, (value, vec![who.clone()])); + + let mut props = Self::public_props(); + props.push((index, (*proposal).clone(), who)); + >::put(props); + Ok(()) + } + + /// Propose a sensitive action to be taken. + fn second(origin: T::Origin, proposal: PropIndex) -> Result { + let who = ensure_signed(origin)?; + let mut deposit = Self::deposit_of(proposal) + .ok_or("can only second an existing proposal")?; + >::reserve(&who, deposit.0) + .map_err(|_| "seconder's balance too low")?; + deposit.1.push(who); + >::insert(proposal, deposit); + Ok(()) + } + + /// Vote in a referendum. If `approve_proposal` is true, the vote is to enact the proposal; + /// false would be a vote to keep the status quo. + fn vote(origin: T::Origin, ref_index: ReferendumIndex, approve_proposal: bool) -> Result { + let who = ensure_signed(origin)?; + ensure!(Self::is_active_referendum(ref_index), "vote given for invalid referendum."); + ensure!(!>::total_balance(&who).is_zero(), + "transactor must have balance to signal approval."); + if !>::exists(&(ref_index, who.clone())) { + >::mutate(ref_index, |voters| voters.push(who.clone())); + } + >::insert(&(ref_index, who), approve_proposal); + Ok(()) + } + + /// Start a referendum. + fn start_referendum(origin: T::Origin, proposal: Box, vote_threshold: VoteThreshold) -> Result { + ensure_root(origin)?; + Self::inject_referendum( + >::block_number() + Self::voting_period(), + *proposal, + vote_threshold + ).map(|_| ()) + } + + /// Remove a referendum. + fn cancel_referendum(origin: T::Origin, ref_index: ReferendumIndex) -> Result { + ensure_root(origin)?; + Self::clear_referendum(ref_index); + Ok(()) + } + + // exposed mutables. + + /// Start a referendum. Can be called directly by the council. + pub fn internal_start_referendum(proposal: T::Proposal, vote_threshold: VoteThreshold) -> result::Result { + >::inject_referendum(>::block_number() + >::voting_period(), proposal, vote_threshold) + } + + /// Remove a referendum. Can be called directly by the council. + pub fn internal_cancel_referendum(ref_index: ReferendumIndex) { + Self::deposit_event(RawEvent::Cancelled(ref_index)); + >::clear_referendum(ref_index); + } + + // private. + + /// Start a referendum + fn inject_referendum( + end: T::BlockNumber, + proposal: T::Proposal, + vote_threshold: VoteThreshold + ) -> result::Result { + let ref_index = Self::referendum_count(); + if ref_index > 0 && Self::referendum_info(ref_index - 1).map(|i| i.0 > end).unwrap_or(false) { + Err("Cannot inject a referendum that ends earlier than preceeding referendum")? + } + + >::put(ref_index + 1); + >::insert(ref_index, (end, proposal, vote_threshold)); + Self::deposit_event(RawEvent::Started(ref_index, vote_threshold)); + Ok(ref_index) + } + + /// Remove all info on a referendum. + fn clear_referendum(ref_index: ReferendumIndex) { + >::remove(ref_index); + >::remove(ref_index); + for v in Self::voters_for(ref_index) { + >::remove((ref_index, v)); + } + } + + /// Current era is ending; we should finish up any proposals. + fn end_block(now: T::BlockNumber) -> Result { + // pick out another public referendum if it's time. + if (now % Self::launch_period()).is_zero() { + let mut public_props = Self::public_props(); + if let Some((winner_index, _)) = public_props.iter() + .enumerate() + .max_by_key(|x| Self::locked_for((x.1).0).unwrap_or_else(Zero::zero)/*defensive only: All current public proposals have an amount locked*/) + { + let (prop_index, proposal, _) = public_props.swap_remove(winner_index); + >::put(public_props); + + if let Some((deposit, depositors)) = >::take(prop_index) {//: (T::Balance, Vec) = + // refund depositors + for d in &depositors { + >::unreserve(d, deposit); + } + Self::deposit_event(RawEvent::Tabled(prop_index, deposit, depositors)); + Self::inject_referendum(now + Self::voting_period(), proposal, VoteThreshold::SuperMajorityApprove)?; + } + } + } + + // tally up votes for any expiring referenda. + for (index, _, proposal, vote_threshold) in Self::maturing_referendums_at(now) { + let (approve, against) = Self::tally(index); + let total_issuance = >::total_issuance(); + Self::clear_referendum(index); + if vote_threshold.approved(approve, against, total_issuance) { + Self::deposit_event(RawEvent::Passed(index)); + let ok = proposal.dispatch(system::RawOrigin::Root.into()).is_ok(); + Self::deposit_event(RawEvent::Executed(index, ok)); + } else { + Self::deposit_event(RawEvent::NotPassed(index)); + } + >::put(index + 1); + } + Ok(()) + } +} + +impl OnFinalise for Module { + fn on_finalise(n: T::BlockNumber) { + if let Err(e) = Self::end_block(n) { + runtime_io::print(e); + } + } +} + +#[cfg(any(feature = "std", test))] +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +#[serde(deny_unknown_fields)] +pub struct GenesisConfig { + pub launch_period: T::BlockNumber, + pub voting_period: T::BlockNumber, + pub minimum_deposit: T::Balance, +} + +#[cfg(any(feature = "std", test))] +impl GenesisConfig { + pub fn new() -> Self { + GenesisConfig { + launch_period: T::BlockNumber::sa(1), + voting_period: T::BlockNumber::sa(1), + minimum_deposit: T::Balance::sa(1), + } + } +} + +#[cfg(any(feature = "std", test))] +impl Default for GenesisConfig { + fn default() -> Self { + GenesisConfig { + launch_period: T::BlockNumber::sa(1000), + voting_period: T::BlockNumber::sa(1000), + minimum_deposit: T::Balance::sa(0), + } + } +} + +#[cfg(any(feature = "std", test))] +impl primitives::BuildStorage for GenesisConfig +{ + fn build_storage(self) -> ::std::result::Result, Vec>, String> { + use codec::Encode; + + Ok(map![ + Self::hash(>::key()).to_vec() => self.launch_period.encode(), + Self::hash(>::key()).to_vec() => self.voting_period.encode(), + Self::hash(>::key()).to_vec() => self.minimum_deposit.encode(), + Self::hash(>::key()).to_vec() => (0 as ReferendumIndex).encode(), + Self::hash(>::key()).to_vec() => (0 as ReferendumIndex).encode(), + Self::hash(>::key()).to_vec() => (0 as PropIndex).encode() + ]) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use runtime_io::with_externalities; + use substrate_primitives::{H256, Blake2Hasher}; + use primitives::BuildStorage; + use primitives::traits::{BlakeTwo256}; + use primitives::testing::{Digest, Header}; + + impl_outer_origin! { + pub enum Origin for Test {} + } + + impl_outer_dispatch! { + pub enum Call where origin: Origin { + Balances, + Democracy, + } + } + + // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. + #[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] + pub struct Test; + impl system::Trait for Test { + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type Digest = Digest; + type AccountId = u64; + type Header = Header; + type Event = (); + } + impl balances::Trait for Test { + type Balance = u64; + type AccountIndex = u64; + type OnFreeBalanceZero = (); + type EnsureAccountLiquid = (); + type Event = (); + } + impl Trait for Test { + type Proposal = Call; + type Event = (); + } + + fn new_test_ext() -> runtime_io::TestExternalities { + let mut t = system::GenesisConfig::::default().build_storage().unwrap(); + t.extend(balances::GenesisConfig::{ + balances: vec![(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)], + transaction_base_fee: 0, + transaction_byte_fee: 0, + existential_deposit: 0, + transfer_fee: 0, + creation_fee: 0, + reclaim_rebate: 0, + }.build_storage().unwrap()); + t.extend(GenesisConfig::{ + launch_period: 1, + voting_period: 1, + minimum_deposit: 1, + }.build_storage().unwrap()); + t.into() + } + + type System = system::Module; + type Balances = balances::Module; + type Democracy = Module; + + #[test] + fn params_should_work() { + with_externalities(&mut new_test_ext(), || { + assert_eq!(Democracy::launch_period(), 1); + assert_eq!(Democracy::voting_period(), 1); + assert_eq!(Democracy::minimum_deposit(), 1); + assert_eq!(Democracy::referendum_count(), 0); + assert_eq!(Balances::free_balance(&42), 0); + assert_eq!(Balances::total_issuance(), 210); + }); + } + + fn set_balance_proposal(value: u64) -> Call { + Call::Balances(balances::Call::set_balance(balances::address::Address::Id(42), value, 0)) + } + + fn propose_set_balance(who: u64, value: u64, locked: u64) -> super::Result { + Democracy::propose(Origin::signed(who), Box::new(set_balance_proposal(value)), locked) + } + + #[test] + fn locked_for_should_work() { + with_externalities(&mut new_test_ext(), || { + System::set_block_number(1); + assert_ok!(propose_set_balance(1, 2, 2)); + assert_ok!(propose_set_balance(1, 4, 4)); + assert_ok!(propose_set_balance(1, 3, 3)); + assert_eq!(Democracy::locked_for(0), Some(2)); + assert_eq!(Democracy::locked_for(1), Some(4)); + assert_eq!(Democracy::locked_for(2), Some(3)); + }); + } + + #[test] + fn single_proposal_should_work() { + with_externalities(&mut new_test_ext(), || { + System::set_block_number(1); + assert_ok!(propose_set_balance(1, 2, 1)); + assert_eq!(Democracy::end_block(System::block_number()), Ok(())); + + System::set_block_number(2); + let r = 0; + assert_ok!(Democracy::vote(Origin::signed(1), r, true)); + + assert_eq!(Democracy::referendum_count(), 1); + assert_eq!(Democracy::voters_for(r), vec![1]); + assert_eq!(Democracy::vote_of((r, 1)), Some(true)); + assert_eq!(Democracy::tally(r), (10, 0)); + + assert_eq!(Democracy::end_block(System::block_number()), Ok(())); + + assert_eq!(Balances::free_balance(&42), 2); + }); + } + + #[test] + fn deposit_for_proposals_should_be_taken() { + with_externalities(&mut new_test_ext(), || { + System::set_block_number(1); + assert_ok!(propose_set_balance(1, 2, 5)); + assert_ok!(Democracy::second(Origin::signed(2), 0)); + assert_ok!(Democracy::second(Origin::signed(5), 0)); + assert_ok!(Democracy::second(Origin::signed(5), 0)); + assert_ok!(Democracy::second(Origin::signed(5), 0)); + assert_eq!(Balances::free_balance(&1), 5); + assert_eq!(Balances::free_balance(&2), 15); + assert_eq!(Balances::free_balance(&5), 35); + }); + } + + #[test] + fn deposit_for_proposals_should_be_returned() { + with_externalities(&mut new_test_ext(), || { + System::set_block_number(1); + assert_ok!(propose_set_balance(1, 2, 5)); + assert_ok!(Democracy::second(Origin::signed(2), 0)); + assert_ok!(Democracy::second(Origin::signed(5), 0)); + assert_ok!(Democracy::second(Origin::signed(5), 0)); + assert_ok!(Democracy::second(Origin::signed(5), 0)); + assert_eq!(Democracy::end_block(System::block_number()), Ok(())); + assert_eq!(Balances::free_balance(&1), 10); + assert_eq!(Balances::free_balance(&2), 20); + assert_eq!(Balances::free_balance(&5), 50); + }); + } + + #[test] + fn proposal_with_deposit_below_minimum_should_not_work() { + with_externalities(&mut new_test_ext(), || { + System::set_block_number(1); + assert_noop!(propose_set_balance(1, 2, 0), "value too low"); + }); + } + + #[test] + fn poor_proposer_should_not_work() { + with_externalities(&mut new_test_ext(), || { + System::set_block_number(1); + assert_noop!(propose_set_balance(1, 2, 11), "proposer\'s balance too low"); + }); + } + + #[test] + fn poor_seconder_should_not_work() { + with_externalities(&mut new_test_ext(), || { + System::set_block_number(1); + assert_ok!(propose_set_balance(2, 2, 11)); + assert_noop!(Democracy::second(Origin::signed(1), 0), "seconder\'s balance too low"); + }); + } + + #[test] + fn runners_up_should_come_after() { + with_externalities(&mut new_test_ext(), || { + System::set_block_number(0); + assert_ok!(propose_set_balance(1, 2, 2)); + assert_ok!(propose_set_balance(1, 4, 4)); + assert_ok!(propose_set_balance(1, 3, 3)); + assert_eq!(Democracy::end_block(System::block_number()), Ok(())); + + System::set_block_number(1); + assert_ok!(Democracy::vote(Origin::signed(1), 0, true)); + assert_eq!(Democracy::end_block(System::block_number()), Ok(())); + assert_eq!(Balances::free_balance(&42), 4); + + System::set_block_number(2); + assert_ok!(Democracy::vote(Origin::signed(1), 1, true)); + assert_eq!(Democracy::end_block(System::block_number()), Ok(())); + assert_eq!(Balances::free_balance(&42), 3); + + System::set_block_number(3); + assert_ok!(Democracy::vote(Origin::signed(1), 2, true)); + assert_eq!(Democracy::end_block(System::block_number()), Ok(())); + }); + } + + #[test] + fn simple_passing_should_work() { + with_externalities(&mut new_test_ext(), || { + System::set_block_number(1); + let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap(); + assert_ok!(Democracy::vote(Origin::signed(1), r, true)); + + assert_eq!(Democracy::voters_for(r), vec![1]); + assert_eq!(Democracy::vote_of((r, 1)), Some(true)); + assert_eq!(Democracy::tally(r), (10, 0)); + + assert_eq!(Democracy::end_block(System::block_number()), Ok(())); + + assert_eq!(Balances::free_balance(&42), 2); + }); + } + + #[test] + fn cancel_referendum_should_work() { + with_externalities(&mut new_test_ext(), || { + System::set_block_number(1); + let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap(); + assert_ok!(Democracy::vote(Origin::signed(1), r, true)); + assert_ok!(Democracy::cancel_referendum(Origin::ROOT, r)); + + assert_eq!(Democracy::end_block(System::block_number()), Ok(())); + + assert_eq!(Balances::free_balance(&42), 0); + }); + } + + #[test] + fn simple_failing_should_work() { + with_externalities(&mut new_test_ext(), || { + System::set_block_number(1); + let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap(); + assert_ok!(Democracy::vote(Origin::signed(1), r, false)); + + assert_eq!(Democracy::voters_for(r), vec![1]); + assert_eq!(Democracy::vote_of((r, 1)), Some(false)); + assert_eq!(Democracy::tally(r), (0, 10)); + + assert_eq!(Democracy::end_block(System::block_number()), Ok(())); + + assert_eq!(Balances::free_balance(&42), 0); + }); + } + + #[test] + fn controversial_voting_should_work() { + with_externalities(&mut new_test_ext(), || { + System::set_block_number(1); + let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap(); + assert_ok!(Democracy::vote(Origin::signed(1), r, true)); + assert_ok!(Democracy::vote(Origin::signed(2), r, false)); + assert_ok!(Democracy::vote(Origin::signed(3), r, false)); + assert_ok!(Democracy::vote(Origin::signed(4), r, true)); + assert_ok!(Democracy::vote(Origin::signed(5), r, false)); + assert_ok!(Democracy::vote(Origin::signed(6), r, true)); + + assert_eq!(Democracy::tally(r), (110, 100)); + + assert_eq!(Democracy::end_block(System::block_number()), Ok(())); + + assert_eq!(Balances::free_balance(&42), 2); + }); + } + + #[test] + fn controversial_low_turnout_voting_should_work() { + with_externalities(&mut new_test_ext(), || { + System::set_block_number(1); + let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap(); + assert_ok!(Democracy::vote(Origin::signed(5), r, false)); + assert_ok!(Democracy::vote(Origin::signed(6), r, true)); + + assert_eq!(Democracy::tally(r), (60, 50)); + + assert_eq!(Democracy::end_block(System::block_number()), Ok(())); + + assert_eq!(Balances::free_balance(&42), 0); + }); + } + + #[test] + fn passing_low_turnout_voting_should_work() { + with_externalities(&mut new_test_ext(), || { + assert_eq!(Balances::free_balance(&42), 0); + assert_eq!(Balances::total_issuance(), 210); + + System::set_block_number(1); + let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap(); + assert_ok!(Democracy::vote(Origin::signed(4), r, true)); + assert_ok!(Democracy::vote(Origin::signed(5), r, false)); + assert_ok!(Democracy::vote(Origin::signed(6), r, true)); + + assert_eq!(Democracy::tally(r), (100, 50)); + + assert_eq!(Democracy::end_block(System::block_number()), Ok(())); + + assert_eq!(Balances::free_balance(&42), 2); + }); + } +} diff --git a/runtime/democracy/src/vote_threshold.rs b/runtime/democracy/src/vote_threshold.rs new file mode 100644 index 0000000000000..ce46149222055 --- /dev/null +++ b/runtime/democracy/src/vote_threshold.rs @@ -0,0 +1,97 @@ +// Copyright 2017 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 . + +//! Voting thresholds. + +use primitives::traits::{Zero, IntegerSquareRoot}; +use rstd::ops::{Add, Mul, Div, Rem}; + +/// A means of determining if a vote is past pass threshold. +#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] +pub enum VoteThreshold { + /// A supermajority of approvals is needed to pass this vote. + SuperMajorityApprove, + /// A supermajority of rejects is needed to fail this vote. + SuperMajorityAgainst, + /// A simple majority of approvals is needed to pass this vote. + SimpleMajority, +} + +pub trait Approved { + /// Given `approve` votes for and `against` votes against from a total electorate size of + /// `electorate` (`electorate - (approve + against)` are abstainers), then returns true if the + /// overall outcome is in favour of approval. + fn approved(&self, approve: Balance, against: Balance, electorate: Balance) -> bool; +} + +/// Return `true` iff `n1 / d1 < n2 / d2`. `d1` and `d2` may not be zero. +fn compare_rationals + Div + Rem + Ord + Copy>(mut n1: T, mut d1: T, mut n2: T, mut d2: T) -> bool { + // Uses a continued fractional representation for a non-overflowing compare. + // Detailed at https://janmr.com/blog/2014/05/comparing-rational-numbers-without-overflow/. + loop { + let q1 = n1 / d1; + let q2 = n2 / d2; + if q1 < q2 { + return true; + } + if q2 < q1 { + return false; + } + let r1 = n1 % d1; + let r2 = n2 % d2; + if r2.is_zero() { + return false; + } + if r1.is_zero() { + return true; + } + n1 = d2; + n2 = d1; + d1 = r2; + d2 = r1; + } +} + +impl + Mul + Div + Rem + Copy> Approved for VoteThreshold { + /// Given `approve` votes for and `against` votes against from a total electorate size of + /// `electorate` (`electorate - (approve + against)` are abstainers), then returns true if the + /// overall outcome is in favour of approval. + fn approved(&self, approve: Balance, against: Balance, electorate: Balance) -> bool { + let voters = approve + against; + let sqrt_voters = voters.integer_sqrt(); + let sqrt_electorate = electorate.integer_sqrt(); + if sqrt_voters.is_zero() { return false; } + match *self { + VoteThreshold::SuperMajorityApprove => + compare_rationals(against, sqrt_voters, approve, sqrt_electorate), + VoteThreshold::SuperMajorityAgainst => + compare_rationals(against, sqrt_electorate, approve, sqrt_voters), + VoteThreshold::SimpleMajority => approve > against, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn should_work() { + assert_eq!(VoteThreshold::SuperMajorityApprove.approved(60, 50, 210), false); + assert_eq!(VoteThreshold::SuperMajorityApprove.approved(100, 50, 210), true); + } +} diff --git a/runtime/example/Cargo.toml b/runtime/example/Cargo.toml new file mode 100644 index 0000000000000..66da7e9355ea8 --- /dev/null +++ b/runtime/example/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "substrate-runtime-example" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +hex-literal = "0.1.0" +serde = { version = "1.0", default_features = false } +serde_derive = { version = "1.0", optional = true } +substrate-runtime-std = { path = "../../core/runtime-std", default_features = false } +substrate-runtime-io = { path = "../../core/runtime-io", default_features = false } +substrate-runtime-support = { path = "../support", default_features = false } +substrate-runtime-primitives = { path = "../primitives", default_features = false } +substrate-codec = { path = "../../core/codec", default_features = false } +substrate-codec-derive = { path = "../../core/codec/derive", default_features = false } +substrate-primitives = { path = "../../core/primitives", default_features = false } +substrate-runtime-system = { path = "../system", default_features = false } +substrate-runtime-balances = { path = "../balances", default_features = false } + +[features] +default = ["std"] +std = [ + "substrate-runtime-std/std", + "substrate-runtime-io/std", + "substrate-runtime-support/std", + "substrate-runtime-primitives/std", + "substrate-runtime-balances/std", + "serde/std", + "serde_derive", + "substrate-codec/std", + "substrate-codec-derive/std", + "substrate-primitives/std", + "substrate-runtime-system/std", +] diff --git a/runtime/example/src/lib.rs b/runtime/example/src/lib.rs new file mode 100644 index 0000000000000..8247106e38466 --- /dev/null +++ b/runtime/example/src/lib.rs @@ -0,0 +1,415 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! The Example: A simple example of a runtime module demonstrating +//! concepts, APIs and structures common to most runtime modules. + +// Ensure we're `no_std` when compiling for Wasm. +#![cfg_attr(not(feature = "std"), no_std)] + +// Assert macros used in tests. +#[cfg_attr(feature = "std", macro_use)] +extern crate substrate_runtime_std; + +// Needed for tests (`with_externalities`). +#[cfg(test)] +extern crate substrate_runtime_io as runtime_io; + +// Needed for the set of mock primitives used in our tests. +#[cfg(test)] +extern crate substrate_primitives; + +// Needed for deriving `Serialize` and `Deserialize` for various types. +// We only implement the serde traits for std builds - they're unneeded +// in the wasm runtime. +#[cfg(feature = "std")] +#[macro_use] +extern crate serde_derive; + +// Needed for deriving `Encode` and `Decode` for `RawEvent`. +#[macro_use] +extern crate substrate_codec_derive; +extern crate substrate_codec as codec; + +// Needed for type-safe access to storage DB. +#[macro_use] +extern crate substrate_runtime_support as runtime_support; + +// Needed for various traits. In our case, `OnFinalise`. +extern crate substrate_runtime_primitives as runtime_primitives; +// `system` module provides us with all sorts of useful stuff and macros +// depend on it being around. +extern crate substrate_runtime_system as system; +// `balances` module is needed for our little example. It's not required in +// general (though if you want your module to be able to work with tokens, then you +// might find it useful). +extern crate substrate_runtime_balances as balances; + +use runtime_primitives::traits::OnFinalise; +use runtime_support::{StorageValue, dispatch::Result}; +use system::{ensure_signed, ensure_root}; + +/// 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 +/// should be added to our implied traits list. +/// +/// `system::Trait` should always be included in our implied traits. +pub trait Trait: balances::Trait { + /// The overarching event type. + type Event: From> + Into<::Event>; +} + +// The module declaration. This states the entry points that we handle. The +// macro takes care of the marshalling of arguments and dispatch. +// +// Anyone can have these functions execute by signing and submitting +// an extrinsic. Ensure that calls into each of these execute in a time, memory and +// using storage space proportional to any costs paid for by the caller or otherwise the +// difficulty of forcing the call to happen. +// +// Generally you'll want to split these into three groups: +// - Public calls that are signed by an external account. +// - Root calls that are allowed to be made only by the governance system. +// - Inherent calls that are allowed to be made only by the block authors and validators. +// +// Information about where this dispatch initiated from is provided as the first argument +// "origin". As such functions must always look like: +// +// `fn foo(origin, bar: Bar, baz: Baz) -> Result = 0;` +// +// The `Result` is required as part of the syntax (and expands to the conventional dispatch +// result of `Result<(), &'static str>`). +// +// When you come to `impl` them later in the module, you must specify the full type for `origin`: +// +// `fn foo(origin: T::Origin, bar: Bar, baz: Baz) { ... }` +// +// There are three entries in the `system::Origin` enum that correspond +// to the above bullets: `::Signed(AccountId)`, `::Root` and `::Inherent`. You should always match +// against them as the first thing you do in your function. There are three convenience calls +// in system that do the matching for you and return a convenient result: `ensure_signed`, +// `ensure_root` and `ensure_inherent`. +decl_module! { + // Simple declaration of the `Module` type. Lets the macro know what its working on. + pub struct Module for enum Call where origin: T::Origin { + /// This is your public interface. Be extremely careful. + /// This is just a simple example of how to interact with the module from the external + /// world. + fn accumulate_dummy(origin, increase_by: T::Balance) -> Result; + + fn accumulate_foo(origin, increase_by: T::Balance) -> Result; + + /// A privileged call; in this case it resets our dummy value to something new. + fn set_dummy(origin, new_dummy: T::Balance) -> Result; + } +} + +/// An event in this module. Events are simple means of reporting specific conditions and +/// circumstances that have happened that users, Dapps and/or chain explorers would find +/// interesting and otherwise difficult to detect. +decl_event!( + pub enum Event with RawEvent + where ::Balance + { + // Just a normal `enum`, here's a dummy event to ensure it compiles. + /// Dummy event, just here so there's a generic type that's used. + Dummy(B), + } +); + +decl_storage! { + // A macro for the Storage trait, and its implementation, for this module. + // This allows for type-safe usage of the Substrate storage database, so you can + // keep things around between blocks. + trait Store for Module as Example { + // Any storage declarations of the form: + // `pub? Name get(getter_name)? : [required | default]? ;` + // where `` is either: + // - `Type` (a basic value item); or + // - `map [ KeyType => ValueType ]` (a map item). + // + // Note that there are two optional modifiers for the storage type declaration. + // - `Foo: u32`: + // - `Foo::put(1); Foo::get()` returns `Some(1)`; + // - `Foo::kill(); Foo::get()` returns `None`. + // - `Foo: required u32`: + // - `Foo::put(1); Foo::get()` returns `1`; + // - `Foo::kill(); Foo::get()` panics. + // - `Foo: default u32`: + // - `Foo::put(1); Foo::get()` returns `1`; + // - `Foo::kill(); Foo::get()` returns `0` (u32::default()). + // e.g. Foo: u32; + // e.g. pub Bar get(bar): default map [ T::AccountId => Vec<(T::Balance, u64)> ]; + // + // For basic value items, you'll get a type which implements + // `runtime_support::StorageValue`. For map items, you'll get a type which + // implements `runtime_support::StorageMap`. + // + // If they have a getter (`get(getter_name)`), then your module will come + // equipped with `fn getter_name() -> Type` for basic value items or + // `fn getter_name(key: KeyType) -> ValueType` for map items. + Dummy get(dummy): T::Balance; + + // this one uses the default, we'll demonstrate the usage of 'mutate' API. + Foo get(foo): default T::Balance; + } +} + +// The main implementation block for the module. Functions here fall into three broad +// categories: +// - Implementations of dispatch functions. The dispatch code generated by the module macro +// expects each of its functions to be implemented. +// - Public interface. These are functions that are `pub` and generally fall into inspector +// functions that do not write to storage and operation functions that do. +// - Private functions. These are your usual private utilities unavailable to other modules. +impl Module { + /// Deposit one of this module's events. + // TODO: move into `decl_module` macro. + fn deposit_event(event: Event) { + >::deposit_event(::Event::from(event).into()); + } + + // Implement Calls and add public immutables and private mutables. + + // Implement dispatched function `accumulate_dummy`. This just increases the value + // of `Dummy` by `increase_by`. + // + // Since this is a dispatched function there are two extremely important things to + // remember: + // + // - MUST NOT PANIC: Under no circumstances (save, perhaps, storage getting into an + // irreparably damaged state) must this function panic. + // - NO SIDE-EFFECTS ON ERROR: This function must either complete totally (and return + // `Ok(())` or it must have no side-effects on storage and return `Err('Some reason')`. + // + // The first is relatively easy to audit for - just ensure all panickers are removed from + // logic that executes in production (which you do anyway, right?!). To ensure the second + // is followed, you should do all tests for validity at the top of your function. This + // is stuff like checking the sender (`origin`) or that state is such that the operation + // makes sense. + // + // Once you've determined that it's all good, then enact the operation and change storage. + // If you can't be certain that the operation will succeed without substantial computation + // then you have a classic blockchain attack scenario. The normal way of managing this is + // to attach a bond to the operation. As the first major alteration of storage, reserve + // some value from the sender's account (`Balances` module has a `reserve` function for + // exactly this scenario). This amount should be enough to cover any costs of the + // substantial execution in case it turns out that you can't proceed with the operation. + // + // If it eventually transpires that the operation is fine and, therefore, that the + // expense of the checks should be borne by the network, then you can refund the reserved + // deposit. If, however, the operation turns out to be invalid and the computation is + // wasted, then you can burn it or repatriate elsewhere. + // + // Security bonds ensure that attackers can't game it by ensuring that anyone interacting + // with the system either progresses it or pays for the trouble of faffing around with + // no progress. + // + // If you don't respect these rules, it is likely that your chain will be attackable. + fn accumulate_dummy(origin: T::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)?; + + // Read the value of dummy from storage. + // let dummy = Self::dummy(); + // Will also work using the `::get` on the storage item type itself: + // let dummy = >::get(); + + // Calculate the new value. + // let new_dummy = dummy.map_or(increase_by, |dummy| dummy + increase_by); + + // Put the new value into storage. + // >::put(new_dummy); + // Will also work with a reference: + // >::put(&new_dummy); + + // Here's the new one of read and then modify the value. + >::mutate(|dummy| { + let new_dummy = dummy.map_or(increase_by, |dummy| dummy + increase_by); + *dummy = Some(new_dummy); + }); + + // Let's deposit an event to let the outside world know this happened. + Self::deposit_event(RawEvent::Dummy(increase_by)); + + // All good. + Ok(()) + } + + fn accumulate_foo(origin: T::Origin, increase_by: T::Balance) -> Result { + let _sender = ensure_signed(origin)?; + + // Because Foo has 'default', the type of 'foo' in closure is the raw type instead of an Option<> type. + >::mutate(|foo| *foo = *foo + increase_by); + + Ok(()) + } + + // Implementation of a privileged call. This doesn't have an `origin` parameter 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 + // assume it's a one-off operation and substantial processing/storage/memory can be used + // without worrying about gameability or attack scenarios. + fn set_dummy(origin: T::Origin, new_value: T::Balance) -> Result { + // This is a privileged call, so we ensure that the origin is "Root". + ensure_root(origin)?; + + // Put the new value into storage. + >::put(new_value); + + // All good. + Ok(()) + } +} + +// This trait expresses what should happen when the block is finalised. +impl OnFinalise for Module { + fn on_finalise(_: T::BlockNumber) { + // Anything that needs to be done at the end of the block. + // We just kill our dummy storage item. + >::kill(); + } +} + +#[cfg(feature = "std")] +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +#[serde(deny_unknown_fields)] +/// The genesis block configuration type. This is a simple default-capable struct that +/// contains any fields with which this module can be configured at genesis time. +pub struct GenesisConfig { + /// A value with which to initialise the Dummy storage item. + pub dummy: T::Balance, + pub foo: T::Balance, +} + +#[cfg(feature = "std")] +impl Default for GenesisConfig { + fn default() -> Self { + GenesisConfig { + dummy: Default::default(), + foo: Default::default(), + } + } +} + +// This expresses the specific key/value pairs that must be placed in storage in order +// to initialise the module and properly reflect the configuration. +// +// Ideally this would re-use the `::put` logic in the storage item type for introducing +// the values into the `StorageMap` (which is just a `HashMap, Vec>`). That +// is not yet in place, though, so for now we do everything "manually", using `hash`, +// `::key()` and `.to_vec()` for the key and `.encode()` for the value. +#[cfg(feature = "std")] +impl runtime_primitives::BuildStorage for GenesisConfig +{ + fn build_storage(self) -> ::std::result::Result { + use codec::Encode; + Ok(map![ + Self::hash(>::key()).to_vec() => self.dummy.encode(), + Self::hash(>::key()).to_vec() => self.foo.encode() + ]) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use runtime_io::with_externalities; + use substrate_primitives::{H256, Blake2Hasher}; + use runtime_primitives::BuildStorage; + use runtime_primitives::traits::{BlakeTwo256}; + + // 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 runtime_primitives::testing::{Digest, Header}; + + 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; + impl system::Trait for Test { + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type Digest = Digest; + type AccountId = u64; + type Header = Header; + type Event = (); + } + impl balances::Trait for Test { + type Balance = u64; + type AccountIndex = u64; + type OnFreeBalanceZero = (); + type EnsureAccountLiquid = (); + type Event = (); + } + impl Trait for Test { + type Event = (); + } + type Example = Module; + + // This function basically just builds a genesis storage key/value store according to + // our desired mockup. + fn new_test_ext() -> runtime_io::TestExternalities { + let mut t = system::GenesisConfig::::default().build_storage().unwrap(); + // We use default for brevity, but you can configure as desired if needed. + t.extend(balances::GenesisConfig::::default().build_storage().unwrap()); + t.extend(GenesisConfig::{ + dummy: 42, + foo: 24, + }.build_storage().unwrap()); + t.into() + } + + #[test] + fn it_works_for_optional_value() { + with_externalities(&mut new_test_ext(), || { + // Check that GenesisBuilder works properly. + assert_eq!(Example::dummy(), Some(42)); + + // Check that accumulate works when we have Some value in Dummy already. + assert_ok!(Example::accumulate_dummy(Origin::signed(1), 27)); + assert_eq!(Example::dummy(), Some(69)); + + // Check that finalising the block removes Dummy from storage. + >::on_finalise(1); + assert_eq!(Example::dummy(), None); + + // Check that accumulate works when we Dummy has None in it. + assert_ok!(Example::accumulate_dummy(Origin::signed(1), 42)); + assert_eq!(Example::dummy(), Some(42)); + }); + } + + #[test] + fn it_works_for_default_value() { + with_externalities(&mut new_test_ext(), || { + assert_eq!(Example::foo(), 24); + assert_ok!(Example::accumulate_foo(Origin::signed(1), 1)); + assert_eq!(Example::foo(), 25); + }); + } +} diff --git a/runtime/executive/Cargo.toml b/runtime/executive/Cargo.toml new file mode 100644 index 0000000000000..2cebd50692e38 --- /dev/null +++ b/runtime/executive/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "substrate-runtime-executive" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +hex-literal = "0.1.0" +serde = { version = "1.0", default_features = false } +serde_derive = { version = "1.0", optional = true } +substrate-codec = { path = "../../core/codec", default_features = false } +substrate-runtime-std = { path = "../../core/runtime-std", default_features = false } +substrate-runtime-io = { path = "../../core/runtime-io", default_features = false } +substrate-runtime-support = { path = "../support", default_features = false } +substrate-runtime-primitives = { path = "../primitives", default_features = false } +substrate-runtime-system = { path = "../system", default_features = false } + +[dev-dependencies] +substrate-primitives = { path = "../../core/primitives" } +substrate-runtime-balances = { path = "../balances" } +substrate-codec-derive = { path = "../../core/codec/derive" } + +[features] +default = ["std"] +std = [ + "substrate-runtime-std/std", + "substrate-runtime-support/std", + "serde/std", + "serde_derive", + "substrate-codec/std", + "substrate-runtime-primitives/std", + "substrate-runtime-io/std", + "substrate-runtime-system/std", +] diff --git a/runtime/executive/src/lib.rs b/runtime/executive/src/lib.rs new file mode 100644 index 0000000000000..9b7e736f6d934 --- /dev/null +++ b/runtime/executive/src/lib.rs @@ -0,0 +1,359 @@ +// Copyright 2017 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 . + +//! Executive: Handles all of the top-level stuff; essentially just executing blocks/extrinsics. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(feature = "std")] +extern crate serde; +#[cfg(test)] +#[macro_use] +extern crate serde_derive; + +#[cfg(test)] +#[macro_use] +extern crate substrate_codec_derive; + +#[cfg_attr(test, macro_use)] +extern crate substrate_runtime_support as runtime_support; + +extern crate substrate_runtime_std as rstd; +extern crate substrate_runtime_io as runtime_io; +extern crate substrate_codec as codec; +extern crate substrate_runtime_primitives as primitives; +extern crate substrate_runtime_system as system; + +#[cfg(test)] +#[macro_use] +extern crate hex_literal; + +#[cfg(test)] +extern crate substrate_primitives; + +#[cfg(test)] +extern crate substrate_runtime_balances as balances; + +use rstd::prelude::*; +use rstd::marker::PhantomData; +use rstd::result; +use primitives::traits::{self, Header, Zero, One, Checkable, Applyable, CheckEqual, OnFinalise, + MakePayment, Hash}; +use codec::{Codec, Encode}; +use system::extrinsics_root; +use primitives::{ApplyOutcome, ApplyError}; + +mod internal { + pub enum ApplyError { + BadSignature(&'static str), + Stale, + Future, + CantPay, + } + + pub enum ApplyOutcome { + Success, + Fail(&'static str), + } +} + +pub struct Executive< + System, + Block, + Lookup, + Payment, + Finalisation, +>(PhantomData<(System, Block, Lookup, Payment, Finalisation)>); + +impl< + Address, + System: system::Trait, + Block: traits::Block, + Lookup: traits::Lookup, + Payment: MakePayment, + Finalisation: OnFinalise, +> Executive where + Block::Extrinsic: Checkable Result> + Codec, + Result>>::Checked: Applyable +{ + /// Start the execution of a particular block. + pub fn initialise_block(header: &System::Header) { + >::initialise(header.number(), header.parent_hash(), header.extrinsics_root()); + } + + fn initial_checks(block: &Block) { + let header = block.header(); + + // check parent_hash is correct. + let n = header.number().clone(); + assert!( + n > System::BlockNumber::zero() && >::block_hash(n - System::BlockNumber::one()) == *header.parent_hash(), + "Parent hash should be valid." + ); + + // check transaction trie root represents the transactions. + let xts_root = extrinsics_root::(&block.extrinsics()); + header.extrinsics_root().check_equal(&xts_root); + assert!(header.extrinsics_root() == &xts_root, "Transaction trie root must be valid."); + } + + /// Actually execute all transitioning for `block`. + pub fn execute_block(block: Block) { + Self::initialise_block(block.header()); + + // any initial checks + Self::initial_checks(&block); + + // execute transactions + let (header, extrinsics) = block.deconstruct(); + extrinsics.into_iter().for_each(Self::apply_extrinsic_no_note); + + // post-transactional book-keeping. + >::note_finished_extrinsics(); + Finalisation::on_finalise(*header.number()); + + // any final checks + Self::final_checks(&header); + } + + /// Finalise the block - it is up the caller to ensure that all header fields are valid + /// except state-root. + pub fn finalise_block() -> System::Header { + >::note_finished_extrinsics(); + Finalisation::on_finalise(>::block_number()); + + // setup extrinsics + >::derive_extrinsics(); + >::finalise() + } + + /// Apply extrinsic outside of the block execution function. + /// This doesn't attempt to validate anything regarding the block, but it builds a list of uxt + /// hashes. + pub fn apply_extrinsic(uxt: Block::Extrinsic) -> result::Result { + let encoded = uxt.encode(); + let encoded_len = encoded.len(); + >::note_extrinsic(encoded); + match Self::apply_extrinsic_no_note_with_len(uxt, encoded_len) { + Ok(internal::ApplyOutcome::Success) => Ok(ApplyOutcome::Success), + Ok(internal::ApplyOutcome::Fail(_)) => Ok(ApplyOutcome::Fail), + Err(internal::ApplyError::CantPay) => Err(ApplyError::CantPay), + Err(internal::ApplyError::BadSignature(_)) => Err(ApplyError::BadSignature), + Err(internal::ApplyError::Stale) => Err(ApplyError::Stale), + Err(internal::ApplyError::Future) => Err(ApplyError::Future), + } + } + + /// Apply an extrinsic inside the block execution function. + fn apply_extrinsic_no_note(uxt: Block::Extrinsic) { + let l = uxt.encode().len(); + match Self::apply_extrinsic_no_note_with_len(uxt, l) { + Ok(internal::ApplyOutcome::Success) => (), + Ok(internal::ApplyOutcome::Fail(e)) => runtime_io::print(e), + Err(internal::ApplyError::CantPay) => panic!("All extrinsics should have sender able to pay their fees"), + Err(internal::ApplyError::BadSignature(_)) => panic!("All extrinsics should be properly signed"), + Err(internal::ApplyError::Stale) | Err(internal::ApplyError::Future) => panic!("All extrinsics should have the correct nonce"), + } + } + + /// Actually apply an extrinsic given its `encoded_len`; this doesn't note its hash. + fn apply_extrinsic_no_note_with_len(uxt: Block::Extrinsic, encoded_len: usize) -> result::Result { + // Verify the signature is good. + let xt = uxt.check_with(Lookup::lookup).map_err(internal::ApplyError::BadSignature)?; + + if let Some(sender) = xt.sender() { + // check index + let expected_index = >::account_nonce(sender); + if xt.index() != &expected_index { return Err( + if xt.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. + + // increment nonce in storage + >::inc_account_nonce(sender); + } + + // decode parameters and dispatch + let r = xt.apply(); + + >::note_applied_extrinsic(&r); + + r.map(|_| internal::ApplyOutcome::Success).or_else(|e| Ok(internal::ApplyOutcome::Fail(e))) + } + + fn final_checks(header: &System::Header) { + // check digest + assert!(header.digest() == &>::digest()); + + // remove temporaries. + >::finalise(); + + // check storage root. + let storage_root = System::Hashing::storage_root(); + header.state_root().check_equal(&storage_root); + assert!(header.state_root() == &storage_root, "Storage root must match that calculated."); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use balances::Call; + use runtime_io::with_externalities; + use substrate_primitives::{H256, Blake2Hasher}; + use primitives::BuildStorage; + use primitives::traits::{Header as HeaderT, BlakeTwo256, Lookup}; + use primitives::testing::{Digest, Header, Block}; + use system; + + struct NullLookup; + impl Lookup for NullLookup { + type Source = u64; + type Target = u64; + fn lookup(s: Self::Source) -> Result { + Ok(s) + } + } + + impl_outer_origin! { + pub enum Origin for Runtime { + } + } + + impl_outer_event!{ + pub enum MetaEvent for Runtime { + balances + } + } + + // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. + #[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] + pub struct Runtime; + impl system::Trait for Runtime { + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Hash = substrate_primitives::H256; + type Hashing = BlakeTwo256; + type Digest = Digest; + type AccountId = u64; + type Header = Header; + type Event = MetaEvent; + } + impl balances::Trait for Runtime { + type Balance = u64; + type AccountIndex = u64; + type OnFreeBalanceZero = (); + type EnsureAccountLiquid = (); + type Event = MetaEvent; + } + + type TestXt = primitives::testing::TestXt>; + type Executive = super::Executive, NullLookup, balances::Module, ()>; + + #[test] + fn balance_transfer_dispatch_works() { + let mut t = system::GenesisConfig::::default().build_storage().unwrap(); + t.extend(balances::GenesisConfig:: { + balances: vec![(1, 111)], + transaction_base_fee: 10, + transaction_byte_fee: 0, + existential_deposit: 0, + transfer_fee: 0, + creation_fee: 0, + reclaim_rebate: 0, + }.build_storage().unwrap()); + let xt = primitives::testing::TestXt(Some(1), 0, Call::transfer(2.into(), 69)); + let mut t = runtime_io::TestExternalities::from(t); + with_externalities(&mut t, || { + Executive::initialise_block(&Header::new(1, H256::default(), H256::default(), [69u8; 32].into(), Digest::default())); + Executive::apply_extrinsic(xt).unwrap(); + assert_eq!(>::total_balance(&1), 32); + assert_eq!(>::total_balance(&2), 69); + }); + } + + fn new_test_ext() -> runtime_io::TestExternalities { + let mut t = system::GenesisConfig::::default().build_storage().unwrap(); + t.extend(balances::GenesisConfig::::default().build_storage().unwrap()); + t.into() + } + + #[test] + fn block_import_works() { + with_externalities(&mut new_test_ext(), || { + Executive::execute_block(Block { + header: Header { + parent_hash: [69u8; 32].into(), + number: 1, + state_root: hex!("d1d3da2b1efb1a6ef740b8cdef52e4cf3c6dade6f8a360969fd7ef0034c53b54").into(), + extrinsics_root: hex!("45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0").into(), + digest: Digest { logs: vec![], }, + }, + extrinsics: vec![], + }); + }); + } + + #[test] + #[should_panic] + fn block_import_of_bad_state_root_fails() { + with_externalities(&mut new_test_ext(), || { + Executive::execute_block(Block { + header: Header { + parent_hash: [69u8; 32].into(), + number: 1, + state_root: [0u8; 32].into(), + extrinsics_root: hex!("45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0").into(), + digest: Digest { logs: vec![], }, + }, + extrinsics: vec![], + }); + }); + } + + #[test] + #[should_panic] + fn block_import_of_bad_extrinsic_root_fails() { + with_externalities(&mut new_test_ext(), || { + Executive::execute_block(Block { + header: Header { + parent_hash: [69u8; 32].into(), + number: 1, + state_root: hex!("d1d3da2b1efb1a6ef740b8cdef52e4cf3c6dade6f8a360969fd7ef0034c53b54").into(), + extrinsics_root: [0u8; 32].into(), + digest: Digest { logs: vec![], }, + }, + extrinsics: vec![], + }); + }); + } + + #[test] + fn bad_extrinsic_not_inserted() { + let mut t = new_test_ext(); + let xt = primitives::testing::TestXt(Some(1), 42, Call::transfer(33.into(), 69)); + with_externalities(&mut t, || { + Executive::initialise_block(&Header::new(1, H256::default(), H256::default(), [69u8; 32].into(), Digest::default())); + assert!(Executive::apply_extrinsic(xt).is_err()); + assert_eq!(>::extrinsic_index(), Some(0)); + }); + } +} diff --git a/runtime/primitives/Cargo.toml b/runtime/primitives/Cargo.toml new file mode 100644 index 0000000000000..f67577664c5e3 --- /dev/null +++ b/runtime/primitives/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "substrate-runtime-primitives" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +num-traits = { version = "0.2", default_features = false } +integer-sqrt = { git = "https://github.com/paritytech/integer-sqrt-rs.git", branch = "master" } +serde = { version = "1.0", optional = true } +serde_derive = { version = "1.0", optional = true } +substrate-codec = { path = "../../core/codec", default_features = false } +substrate-codec-derive = { path = "../../core/codec/derive", default_features = false } +substrate-primitives = { path = "../../core/primitives", default_features = false } +substrate-runtime-std = { path = "../../core/runtime-std", default_features = false } +substrate-runtime-io = { path = "../../core/runtime-io", default_features = false } +substrate-runtime-support = { path = "../support", default_features = false } +log = {version = "0.3", optional = true } + +[dev-dependencies] +serde_json = "1.0" + +[features] +default = ["std"] +std = [ + "num-traits/std", + "serde", + "serde_derive", + "log", + "substrate-runtime-std/std", + "substrate-runtime-io/std", + "substrate-runtime-support/std", + "substrate-codec/std", + "substrate-primitives/std", +] diff --git a/runtime/primitives/src/bft.rs b/runtime/primitives/src/bft.rs new file mode 100644 index 0000000000000..17eb54ab10531 --- /dev/null +++ b/runtime/primitives/src/bft.rs @@ -0,0 +1,195 @@ +// Copyright 2017 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 . + +//! Message formats for the BFT consensus layer. + +use rstd::prelude::*; +use codec::{Decode, Encode, Input, Output}; +use substrate_primitives::{AuthorityId, Signature}; + +/// Type alias for extracting message type from block. +pub type ActionFor = Action::Hash>; + +/// Actions which can be taken during the BFT process. +#[derive(Clone, PartialEq, Eq, Encode, Decode)] +#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] +pub enum Action { + /// Proposal of a block candidate. + #[codec(index = "1")] + Propose(u32, Block), + /// Proposal header of a block candidate. Accompanies any proposal, + /// but is used for misbehavior reporting since blocks themselves are big. + #[codec(index = "2")] + ProposeHeader(u32, H), + /// Preparation to commit for a candidate. + #[codec(index = "3")] + Prepare(u32, H), + /// Vote to commit to a candidate. + #[codec(index = "4")] + Commit(u32, H), + /// Vote to advance round after inactive primary. + #[codec(index = "5")] + AdvanceRound(u32), +} + +/// Type alias for extracting message type from block. +pub type MessageFor = Message::Hash>; + +/// Messages exchanged between participants in the BFT consensus. +#[derive(Clone, PartialEq, Eq, Encode, Decode)] +#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] +pub struct Message { + /// The parent header hash this action is relative to. + pub parent: Hash, + /// The action being broadcasted. + pub action: Action, +} + +/// Justification of a block. +#[derive(Clone, PartialEq, Eq, Encode, Decode)] +#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] +pub struct Justification { + /// The round consensus was reached in. + pub round_number: u32, + /// The hash of the header justified. + pub hash: H, + /// The signatures and signers of the hash. + pub signatures: Vec<(AuthorityId, Signature)> +} + +// single-byte code to represent misbehavior kind. +#[repr(i8)] +enum MisbehaviorCode { + /// BFT: double prepare. + BftDoublePrepare = 0x11, + /// BFT: double commit. + BftDoubleCommit = 0x12, +} + +impl MisbehaviorCode { + fn from_i8(x: i8) -> Option { + match x { + 0x11 => Some(MisbehaviorCode::BftDoublePrepare), + 0x12 => Some(MisbehaviorCode::BftDoubleCommit), + _ => None, + } + } +} + +/// Misbehavior kinds. +#[derive(Clone, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] +pub enum MisbehaviorKind { + /// BFT: double prepare. + BftDoublePrepare(u32, (Hash, Signature), (Hash, Signature)), + /// BFT: double commit. + BftDoubleCommit(u32, (Hash, Signature), (Hash, Signature)), +} + +impl Encode for MisbehaviorKind { + fn encode_to(&self, dest: &mut T) { + match *self { + MisbehaviorKind::BftDoublePrepare(ref round, (ref h_a, ref s_a), (ref h_b, ref s_b)) => { + dest.push(&(MisbehaviorCode::BftDoublePrepare as i8)); + dest.push(round); + dest.push(h_a); + dest.push(s_a); + dest.push(h_b); + dest.push(s_b); + } + MisbehaviorKind::BftDoubleCommit(ref round, (ref h_a, ref s_a), (ref h_b, ref s_b)) => { + dest.push(&(MisbehaviorCode::BftDoubleCommit as i8)); + dest.push(round); + dest.push(h_a); + dest.push(s_a); + dest.push(h_b); + dest.push(s_b); + } + } + } +} +impl Decode for MisbehaviorKind { + fn decode(input: &mut I) -> Option { + Some(match i8::decode(input).and_then(MisbehaviorCode::from_i8)? { + MisbehaviorCode::BftDoublePrepare => { + MisbehaviorKind::BftDoublePrepare( + u32::decode(input)?, + (Hash::decode(input)?, Signature::decode(input)?), + (Hash::decode(input)?, Signature::decode(input)?), + ) + } + MisbehaviorCode::BftDoubleCommit => { + MisbehaviorKind::BftDoubleCommit( + u32::decode(input)?, + (Hash::decode(input)?, Signature::decode(input)?), + (Hash::decode(input)?, Signature::decode(input)?), + ) + } + }) + } +} + + +/// A report of misbehavior by an authority. +#[derive(Clone, PartialEq, Eq, Encode, Decode)] +#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] +pub struct MisbehaviorReport { + /// The parent hash of the block where the misbehavior occurred. + pub parent_hash: Hash, + /// The parent number of the block where the misbehavior occurred. + pub parent_number: Number, + /// The authority who misbehavior. + pub target: AuthorityId, + /// The misbehavior kind. + pub misbehavior: MisbehaviorKind, +} + +#[cfg(test)] +mod test { + use super::*; + use substrate_primitives::H256; + + #[test] + fn misbehavior_report_roundtrip() { + let report = MisbehaviorReport:: { + parent_hash: [0; 32].into(), + parent_number: 999, + target: [1; 32].into(), + misbehavior: MisbehaviorKind::BftDoubleCommit( + 511, + ([2; 32].into(), [3; 64].into()), + ([4; 32].into(), [5; 64].into()), + ), + }; + + let encoded = report.encode(); + assert_eq!(MisbehaviorReport::::decode(&mut &encoded[..]).unwrap(), report); + + let report = MisbehaviorReport:: { + parent_hash: [0; 32].into(), + parent_number: 999, + target: [1; 32].into(), + misbehavior: MisbehaviorKind::BftDoublePrepare( + 511, + ([2; 32].into(), [3; 64].into()), + ([4; 32].into(), [5; 64].into()), + ), + }; + + let encoded = report.encode(); + assert_eq!(MisbehaviorReport::::decode(&mut &encoded[..]).unwrap(), report); + } +} diff --git a/runtime/primitives/src/generic/block.rs b/runtime/primitives/src/generic/block.rs new file mode 100644 index 0000000000000..40538a392c3d0 --- /dev/null +++ b/runtime/primitives/src/generic/block.rs @@ -0,0 +1,105 @@ +// Copyright 2017 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 a block and associated items. + +#[cfg(feature = "std")] +use std::fmt; + +use rstd::prelude::*; +use codec::Codec; +use traits::{self, Member, Block as BlockT, Header as HeaderT}; +use bft::Justification; + +/// Something to identify a block. +#[derive(PartialEq, Eq, Clone)] +#[cfg_attr(feature = "std", derive(Debug, Serialize))] +#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] +#[cfg_attr(feature = "std", serde(deny_unknown_fields))] +pub enum BlockId { + /// Identify by block header hash. + Hash(<::Header as HeaderT>::Hash), + /// Identify by block number. + Number(<::Header as HeaderT>::Number), +} + +impl BlockId { + /// Create a block ID from a hash. + pub fn hash(hash: Block::Hash) -> Self { + BlockId::Hash(hash) + } + + /// Create a block ID from a number. + pub fn number(number: ::Number) -> Self { + BlockId::Number(number) + } +} + +impl Copy for BlockId {} + +#[cfg(feature = "std")] +impl fmt::Display for BlockId { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "{:?}", self) + } +} + +/// Abstraction over a substrate block. +#[derive(PartialEq, Eq, Clone, Encode, Decode)] +#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] +#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] +#[cfg_attr(feature = "std", serde(deny_unknown_fields))] +pub struct Block { + /// The block header. + pub header: Header, + /// The accompanying extrinsics. + pub extrinsics: Vec, +} + +impl traits::Block for Block +where + Header: HeaderT, + Extrinsic: Member + Codec, +{ + type Extrinsic = Extrinsic; + type Header = Header; + type Hash = ::Hash; + + fn header(&self) -> &Self::Header { + &self.header + } + fn extrinsics(&self) -> &[Self::Extrinsic] { + &self.extrinsics[..] + } + fn deconstruct(self) -> (Self::Header, Vec) { + (self.header, self.extrinsics) + } + fn new(header: Self::Header, extrinsics: Vec) -> Self { + Block { header, extrinsics } + } +} + +/// Abstraction over a substrate block and justification. +#[derive(PartialEq, Eq, Clone, Encode, Decode)] +#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] +#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] +#[cfg_attr(feature = "std", serde(deny_unknown_fields))] +pub struct SignedBlock { + /// Full block. + pub block: Block, + /// Block header justification. + pub justification: Justification, +} diff --git a/runtime/primitives/src/generic/checked_extrinsic.rs b/runtime/primitives/src/generic/checked_extrinsic.rs new file mode 100644 index 0000000000000..e5aee32a02c58 --- /dev/null +++ b/runtime/primitives/src/generic/checked_extrinsic.rs @@ -0,0 +1,59 @@ +// Copyright 2017 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 extrinsic that has passed the verification +//! stage. + +use runtime_support::Dispatchable; +use traits::{self, Member, SimpleArithmetic, MaybeDisplay}; + +/// 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(Serialize, Deserialize, Debug))] +pub struct CheckedExtrinsic { + /// Who this purports to be from, if anyone (note this is not a signature). + pub signed: Option, + /// The number of extrinsics have come before from the same signer. + pub index: Index, + /// The function that should be called. + pub function: Call, +} + +impl traits::Applyable + for CheckedExtrinsic +where + AccountId: Member + MaybeDisplay, + Index: Member + MaybeDisplay + SimpleArithmetic, + Call: Member + Dispatchable, + ::Origin: From> +{ + type Index = Index; + type AccountId = AccountId; + + fn index(&self) -> &Self::Index { + &self.index + } + + fn sender(&self) -> Option<&Self::AccountId> { + self.signed.as_ref() + } + + fn apply(self) -> Result<(), &'static str> { + self.function.dispatch(self.signed.into()) + } +} diff --git a/runtime/primitives/src/generic/digest.rs b/runtime/primitives/src/generic/digest.rs new file mode 100644 index 0000000000000..0616ec8914e39 --- /dev/null +++ b/runtime/primitives/src/generic/digest.rs @@ -0,0 +1,150 @@ +// Copyright 2017 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 a digest. + +use rstd::prelude::*; + +use codec::{Decode, Encode, Codec, Input}; +use traits::{self, Member, DigestItem as DigestItemT}; + +#[derive(PartialEq, Eq, Clone, Encode, Decode)] +#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] +pub struct Digest { + pub logs: Vec, +} + +impl Default for Digest { + fn default() -> Self { + Digest { logs: Vec::new(), } + } +} + +impl traits::Digest for Digest where + Item: DigestItemT + Codec +{ + type Item = Item; + + fn logs(&self) -> &[Self::Item] { + &self.logs + } + + fn push(&mut self, item: Self::Item) { + self.logs.push(item); + } +} + +/// Digest item that is able to encode/decode 'system' digest items and +/// provide opaque access to other items. +#[derive(PartialEq, Eq, Clone)] +#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] +pub enum DigestItem { + /// System digest item announcing that authorities set has been changed + /// in the block. Contains the new set of authorities. + AuthoritiesChange(Vec), + /// Any 'non-system' digest item, opaque to the native code. + Other(Vec), +} + +/// A 'referencing view' for digest item. Does not own its contents. Used by +/// final runtime implementations for encoding/decoding its log items. +#[derive(PartialEq, Eq, Clone)] +#[cfg_attr(feature = "std", derive(Debug))] +pub enum DigestItemRef<'a, AuthorityId: 'a> { + /// Reference to `DigestItem::AuthoritiesChange`. + AuthoritiesChange(&'a [AuthorityId]), + /// Reference to `DigestItem::Other`. + Other(&'a Vec), +} + +/// Type of the digest item. Used to gain explicit control over `DigestItem` encoding +/// process. We need an explicit control, because final runtimes are encoding their own +/// digest items using `DigestItemRef` type and we can't auto-derive `Decode` +/// trait for `DigestItemRef`. +#[repr(u32)] +#[derive(Encode, Decode)] +enum DigestItemType { + Other = 0, + AuthoritiesChange, +} + +impl DigestItem { + /// Returns Some if `self` is a `DigestItem::Other`. + pub fn as_other(&self) -> Option<&Vec> { + match *self { + DigestItem::Other(ref v) => Some(v), + _ => None, + } + } + + /// Returns a 'referencing view' for this digest item. + fn dref<'a>(&'a self) -> DigestItemRef<'a, AuthorityId> { + match *self { + DigestItem::AuthoritiesChange(ref v) => DigestItemRef::AuthoritiesChange(v), + DigestItem::Other(ref v) => DigestItemRef::Other(v), + } + } +} + +impl traits::DigestItem for DigestItem { + type AuthorityId = AuthorityId; + + fn as_authorities_change(&self) -> Option<&[Self::AuthorityId]> { + match *self { + DigestItem::AuthoritiesChange(ref authorities) => Some(authorities), + _ => None, + } + } +} + +impl Encode for DigestItem { + fn encode(&self) -> Vec { + self.dref().encode() + } +} + +impl Decode for DigestItem { + fn decode(input: &mut I) -> Option { + let item_type: DigestItemType = Decode::decode(input)?; + match item_type { + DigestItemType::AuthoritiesChange => Some(DigestItem::AuthoritiesChange( + Decode::decode(input)?, + )), + DigestItemType::Other => Some(DigestItem::Other( + Decode::decode(input)?, + )), + } + } +} + +impl<'a, AuthorityId: Encode> Encode for DigestItemRef<'a, AuthorityId> { + fn encode(&self) -> Vec { + let mut v = Vec::new(); + + match *self { + DigestItemRef::AuthoritiesChange(authorities) => { + DigestItemType::AuthoritiesChange.encode_to(&mut v); + authorities.encode_to(&mut v); + }, + DigestItemRef::Other(val) => { + DigestItemType::Other.encode_to(&mut v); + val.encode_to(&mut v); + }, + } + + v + } +} diff --git a/runtime/primitives/src/generic/header.rs b/runtime/primitives/src/generic/header.rs new file mode 100644 index 0000000000000..8425a17bb7b26 --- /dev/null +++ b/runtime/primitives/src/generic/header.rs @@ -0,0 +1,167 @@ +// Copyright 2017 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 a block header. + +#[cfg(feature = "std")] +use serde::{Deserialize, Deserializer}; + +use codec::{Decode, Encode, Codec, Input, Output}; +use traits::{self, Member, SimpleArithmetic, SimpleBitOps, MaybeDisplay, + Hash as HashT, DigestItem as DigestItemT}; +use generic::Digest; + +/// Abstraction over a block header for a substrate chain. +#[derive(PartialEq, Eq, Clone)] +#[cfg_attr(feature = "std", derive(Debug, Serialize))] +#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] +#[cfg_attr(feature = "std", serde(deny_unknown_fields))] +pub struct Header { + /// The parent hash. + pub parent_hash: ::Output, + /// The block number. + pub number: Number, + /// The state trie merkle root + pub state_root: ::Output, + /// The merkle root of the extrinsics. + pub extrinsics_root: ::Output, + /// A chain-specific digest of data useful for light clients or referencing auxiliary data. + pub digest: Digest, +} + +// Hack to work around the fact that deriving deserialize doesn't work nicely with +// the `hashing` trait used as a parameter. +// dummy struct that uses the hash type directly. +// https://github.com/serde-rs/serde/issues/1296 +#[cfg(feature = "std")] +#[serde(rename_all = "camelCase")] +#[derive(Deserialize)] +struct DeserializeHeader { + parent_hash: H, + number: N, + state_root: H, + extrinsics_root: H, + digest: Digest, +} + +#[cfg(feature = "std")] +impl From> for Header { + fn from(other: DeserializeHeader) -> Self { + Header { + parent_hash: other.parent_hash, + number: other.number, + state_root: other.state_root, + extrinsics_root: other.extrinsics_root, + digest: other.digest, + } + } +} + +#[cfg(feature = "std")] +impl<'a, Number: 'a, Hash: 'a + HashT, DigestItem: 'a> Deserialize<'a> for Header where + Number: Deserialize<'a>, + Hash::Output: Deserialize<'a>, + DigestItem: Deserialize<'a>, +{ + fn deserialize>(de: D) -> Result { + DeserializeHeader::::deserialize(de).map(Into::into) + } +} + +// TODO [ToDr] Issue with bounds +impl Decode for Header where + Number: Decode, + Hash: HashT, + Hash::Output: Decode, + DigestItem: DigestItemT + Decode, +{ + fn decode(input: &mut I) -> Option { + Some(Header { + parent_hash: Decode::decode(input)?, + number: Decode::decode(input)?, + state_root: Decode::decode(input)?, + extrinsics_root: Decode::decode(input)?, + digest: Decode::decode(input)?, + }) + } +} + +impl Encode for Header where + Number: Encode, + Hash: HashT, + Hash::Output: Encode, + DigestItem: DigestItemT + Encode, +{ + fn encode_to(&self, dest: &mut T) { + dest.push(&self.parent_hash); + dest.push(&self.number); + dest.push(&self.state_root); + dest.push(&self.extrinsics_root); + dest.push(&self.digest); + } +} + +impl traits::Header for Header where + Number: Member + ::rstd::hash::Hash + Copy + Codec + MaybeDisplay + SimpleArithmetic + Codec, + Hash: HashT, + DigestItem: DigestItemT + Codec, + Hash::Output: Default + ::rstd::hash::Hash + Copy + Member + MaybeDisplay + SimpleBitOps + Codec, + { + type Number = Number; + type Hash = ::Output; + type Hashing = Hash; + type Digest = Digest; + + fn number(&self) -> &Self::Number { &self.number } + fn set_number(&mut self, num: Self::Number) { self.number = num } + + fn extrinsics_root(&self) -> &Self::Hash { &self.extrinsics_root } + fn set_extrinsics_root(&mut self, root: Self::Hash) { self.extrinsics_root = root } + + fn state_root(&self) -> &Self::Hash { &self.state_root } + fn set_state_root(&mut self, root: Self::Hash) { self.state_root = root } + + fn parent_hash(&self) -> &Self::Hash { &self.parent_hash } + fn set_parent_hash(&mut self, hash: Self::Hash) { self.parent_hash = hash } + + fn digest(&self) -> &Self::Digest { &self.digest } + fn set_digest(&mut self, digest: Self::Digest) { self.digest = digest } + + fn new( + number: Self::Number, + extrinsics_root: Self::Hash, + state_root: Self::Hash, + parent_hash: Self::Hash, + digest: Self::Digest + ) -> Self { + Header { + number, extrinsics_root: extrinsics_root, state_root, parent_hash, digest + } + } +} + +impl Header where + Number: Member + ::rstd::hash::Hash + Copy + Codec + MaybeDisplay + SimpleArithmetic + Codec, + Hash: HashT, + DigestItem: DigestItemT + Codec, + Hash::Output: Default + ::rstd::hash::Hash + Copy + Member + MaybeDisplay + SimpleBitOps + Codec, + { + /// Convenience helper for computing the hash of the header without having + /// to import the trait. + pub fn hash(&self) -> Hash::Output { + Hash::hash_of(self) + } +} diff --git a/runtime/primitives/src/generic/mod.rs b/runtime/primitives/src/generic/mod.rs new file mode 100644 index 0000000000000..23907d9f6cb7f --- /dev/null +++ b/runtime/primitives/src/generic/mod.rs @@ -0,0 +1,33 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +// tag::description[] +//! Generic implementations of Extrinsic/Header/Block. +// end::description[] + +mod unchecked_extrinsic; +mod checked_extrinsic; +mod header; +mod block; +mod digest; +#[cfg(test)] +mod tests; + +pub use self::unchecked_extrinsic::UncheckedExtrinsic; +pub use self::checked_extrinsic::CheckedExtrinsic; +pub use self::header::Header; +pub use self::block::{Block, SignedBlock, BlockId}; +pub use self::digest::{Digest, DigestItem, DigestItemRef}; diff --git a/runtime/primitives/src/generic/tests.rs b/runtime/primitives/src/generic/tests.rs new file mode 100644 index 0000000000000..6d22357d1902c --- /dev/null +++ b/runtime/primitives/src/generic/tests.rs @@ -0,0 +1,105 @@ +// Copyright 2017 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 generic implementations of Extrinsic/Header/Block. + +use codec::{Decode, Encode}; +use substrate_primitives::{H256, H512}; +use super::{Digest, Header, DigestItem, UncheckedExtrinsic}; + +type Block = super::Block< + Header>, + UncheckedExtrinsic, +>; + +#[test] +fn block_roundtrip_serialization() { + let block: Block = Block { + header: Header { + parent_hash: [0u8; 32].into(), + number: 100_000, + state_root: [1u8; 32].into(), + extrinsics_root: [2u8; 32].into(), + digest: Digest { logs: vec![ + DigestItem::Other::(vec![1, 2, 3]), + DigestItem::Other::(vec![4, 5, 6]), + ] }, + }, + extrinsics: vec![ + UncheckedExtrinsic::new_signed( + 0, + 100, + [255u8; 32].into(), + H512::from([0u8; 64]).into() + ), + UncheckedExtrinsic::new_signed( + 100, + 99, + [128u8; 32].into(), + H512::from([255u8; 64]).into() + ) + ] + }; + + { + let encoded = ::serde_json::to_vec(&block).unwrap(); + let decoded: Block = ::serde_json::from_slice(&encoded).unwrap(); + + assert_eq!(block, decoded); + } + { + let encoded = block.encode(); + let decoded = Block::decode(&mut &encoded[..]).unwrap(); + + assert_eq!(block, decoded); + } +} + +#[test] +fn system_digest_item_encoding() { + let item = DigestItem::AuthoritiesChange::(vec![10, 20, 30]); + let encoded = item.encode(); + assert_eq!(encoded, vec![ + // type = DigestItemType::AuthoritiesChange + 1, + // number of items in athorities set + 3, 0, 0, 0, + // authorities + 10, 0, 0, 0, + 20, 0, 0, 0, + 30, 0, 0, 0, + ]); + + let decoded: DigestItem = Decode::decode(&mut &encoded[..]).unwrap(); + assert_eq!(item, decoded); +} + +#[test] +fn non_system_digest_item_encoding() { + let item = DigestItem::Other::(vec![10, 20, 30]); + let encoded = item.encode(); + assert_eq!(encoded, vec![ + // type = DigestItemType::Other + 0, + // length of other data + 3, 0, 0, 0, + // authorities + 10, 20, 30, + ]); + + let decoded: DigestItem = Decode::decode(&mut &encoded[..]).unwrap(); + assert_eq!(item, decoded); +} \ No newline at end of file diff --git a/runtime/primitives/src/generic/unchecked_extrinsic.rs b/runtime/primitives/src/generic/unchecked_extrinsic.rs new file mode 100644 index 0000000000000..c7e3693fbf187 --- /dev/null +++ b/runtime/primitives/src/generic/unchecked_extrinsic.rs @@ -0,0 +1,159 @@ +// Copyright 2017 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 codec::{Decode, Encode, Input}; +use traits::{self, Member, SimpleArithmetic, MaybeDisplay}; +use super::CheckedExtrinsic; + +/// A extrinsic right from the external world. This is unchecked and so +/// can contain a signature. +#[derive(PartialEq, Eq, Clone)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub struct UncheckedExtrinsic { + /// The signature and address, if this is a signed extrinsic. + pub signature: Option<(Address, Signature)>, + /// The number of extrinsics have come before from the same signer. + pub index: Index, + /// The function that should be called. + pub function: Call, +} + +impl UncheckedExtrinsic { + /// New instance of a signed extrinsic aka "transaction". + pub fn new_signed(index: Index, function: Call, signed: Address, signature: Signature) -> Self { + UncheckedExtrinsic { + signature: Some((signed, signature)), + index, + function, + } + } + + /// New instance of an unsigned extrinsic aka "inherent". + pub fn new_unsigned(index: Index, function: Call) -> Self { + UncheckedExtrinsic { + signature: None, + index, + function, + } + } + + /// `true` if there is a signature. + pub fn is_signed(&self) -> bool { + self.signature.is_some() + } +} + +impl traits::Checkable + for UncheckedExtrinsic +where + Address: Member + MaybeDisplay, + Index: Encode + Member + MaybeDisplay + SimpleArithmetic, + Call: Encode + Member, + Signature: Member + traits::Verify, + AccountId: Member + MaybeDisplay, + ThisLookup: FnOnce(Address) -> Result, +{ + type Checked = CheckedExtrinsic; + + fn check_with(self, lookup: ThisLookup) -> Result { + Ok(match self.signature { + Some((signed, signature)) => { + let payload = (self.index, self.function); + let signed = lookup(signed)?; + if !::verify_encoded_lazy(&signature, &payload, &signed) { + return Err("bad signature in extrinsic") + } + CheckedExtrinsic { + signed: Some(signed), + index: payload.0, + function: payload.1, + } + } + None => CheckedExtrinsic { + signed: None, + index: self.index, + function: self.function, + }, + }) + } +} + +impl Decode + for UncheckedExtrinsic +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 u32, which has the total number of bytes following (we don't need + // to use this). + let _length_do_not_remove_me_see_above: u32 = Decode::decode(input)?; + + Some(UncheckedExtrinsic { + signature: Decode::decode(input)?, + index: Decode::decode(input)?, + function: Decode::decode(input)?, + }) + } +} + +impl Encode + for UncheckedExtrinsic +where + Address: Encode, + Signature: Encode, + Index: Encode, + Call: Encode, +{ + fn encode(&self) -> Vec { + let mut v = Vec::new(); + + // need to prefix with the total length as u32 to ensure it's binary comptible with + // Vec. we'll make room for it here, then overwrite once we know the length. + v.extend(&[0u8; 4]); + + self.signature.encode_to(&mut v); + self.index.encode_to(&mut v); + self.function.encode_to(&mut v); + + let length = (v.len() - 4) as u32; + length.using_encoded(|s| v[0..4].copy_from_slice(s)); + + v + } +} + +/// TODO: use derive when possible. +#[cfg(feature = "std")] +impl fmt::Debug for UncheckedExtrinsic where + Address: fmt::Debug, + Index: fmt::Debug, + Call: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "UncheckedExtrinsic({:?}, {:?}, {:?})", self.signature.as_ref().map(|x| &x.0), self.function, self.index) + } +} diff --git a/runtime/primitives/src/lib.rs b/runtime/primitives/src/lib.rs new file mode 100644 index 0000000000000..e9bddc3286d87 --- /dev/null +++ b/runtime/primitives/src/lib.rs @@ -0,0 +1,434 @@ +// Copyright 2017 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 . + +//! System manager: Handles all of the top-level stuff; executing block/transaction, setting code +//! and depositing logs. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(feature = "std")] +extern crate serde; + +#[cfg(feature = "std")] +#[macro_use] +extern crate serde_derive; + +#[cfg(feature = "std")] +#[macro_use] +extern crate log; + +#[macro_use] +extern crate substrate_codec_derive; + +extern crate num_traits; +extern crate integer_sqrt; +extern crate substrate_runtime_std as rstd; +extern crate substrate_runtime_io as runtime_io; +extern crate substrate_runtime_support as runtime_support; +extern crate substrate_codec as codec; +extern crate substrate_primitives; + +#[cfg(test)] +extern crate serde_json; + +#[cfg(feature = "std")] +use std::collections::HashMap; + +use rstd::prelude::*; +use substrate_primitives::hash::{H256, H512}; + +#[cfg(feature = "std")] +use substrate_primitives::hexdisplay::ascii_format; + +#[cfg(feature = "std")] +pub mod testing; + +pub mod traits; +pub mod generic; +pub mod bft; + +use traits::{Verify, Lazy}; + +#[cfg(feature = "std")] +pub use serde::{Serialize, de::DeserializeOwned}; + +/// A set of key value pairs for storage. +#[cfg(feature = "std")] +pub type StorageMap = HashMap, Vec>; + +/// Complex storage builder stuff. +#[cfg(feature = "std")] +pub trait BuildStorage { + fn hash(data: &[u8]) -> [u8; 16] { + let r = runtime_io::twox_128(data); + trace!(target: "build_storage", "{} <= {}", substrate_primitives::hexdisplay::HexDisplay::from(&r), ascii_format(data)); + r + } + fn build_storage(self) -> Result; +} + +#[cfg(feature = "std")] +impl BuildStorage for StorageMap { + fn build_storage(self) -> Result { + Ok(self) + } +} + +/// Permill is parts-per-million (i.e. after multiplying by this, divide by 1000000). +#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] +#[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq)] +pub struct Permill(u32); + +// TODO: impl Mul for N where N: As +impl Permill { + pub fn times + ::rstd::ops::Mul + ::rstd::ops::Div>(self, b: N) -> N { + // TODO: handle overflows + b * >::sa(self.0 as usize) / >::sa(1000000) + } + + pub fn from_millionths(x: u32) -> Permill { Permill(x) } + + pub fn from_percent(x: u32) -> Permill { Permill(x * 10_000) } + + #[cfg(feature = "std")] + pub fn from_fraction(x: f64) -> Permill { Permill((x * 1_000_000.0) as u32) } +} + +#[cfg(feature = "std")] +impl From for Permill { + fn from(x: f64) -> Permill { + Permill::from_fraction(x) + } +} + +#[cfg(feature = "std")] +impl From for Permill { + fn from(x: f32) -> Permill { + Permill::from_fraction(x as f64) + } +} + +/// Ed25519 signature verify. +#[derive(Eq, PartialEq, Clone, Default, Encode, Decode)] +#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] +pub struct Ed25519Signature(pub H512); + +impl Verify for Ed25519Signature { + type Signer = H256; + fn verify>(&self, mut msg: L, signer: &Self::Signer) -> bool { + runtime_io::ed25519_verify(&(self.0).0, msg.get(), &signer.0[..]) + } +} + +impl From for Ed25519Signature { + fn from(h: H512) -> Ed25519Signature { + Ed25519Signature(h) + } +} + +#[derive(Eq, PartialEq, Clone, Copy, Decode)] +#[cfg_attr(feature = "std", derive(Debug, Serialize))] +#[repr(u8)] +/// Outcome of a valid extrinsic application. Capable of being sliced. +pub enum ApplyOutcome { + /// Successful application (extrinsic reported no issue). + Success = 0, + /// Failed application (extrinsic was probably a no-op other than fees). + Fail = 1, +} + +impl codec::Encode for ApplyOutcome { + fn using_encoded R>(&self, f: F) -> R { + f(&[*self as u8]) + } +} + +#[derive(Eq, PartialEq, Clone, Copy, Decode)] +#[cfg_attr(feature = "std", derive(Debug, Serialize))] +#[repr(u8)] +/// Reason why an extrinsic couldn't be applied (i.e. invalid extrinsic). +pub enum ApplyError { + /// Bad signature. + BadSignature = 0, + /// Nonce too low. + Stale = 1, + /// Nonce too high. + Future = 2, + /// Sending account had too low a balance. + CantPay = 3, +} + +impl codec::Encode for ApplyError { + fn using_encoded R>(&self, f: F) -> R { + f(&[*self as u8]) + } +} + +/// Result from attempt to apply an extrinsic. +pub type ApplyResult = Result; + +/// Verify a signature on an encoded value in a lazy manner. This can be +/// an optimization if the signature scheme has an "unsigned" escape hash. +pub fn verify_encoded_lazy(sig: &V, item: &T, signer: &V::Signer) -> bool { + // The `Lazy` trait expresses something like `X: FnMut &'a T>`. + // unfortunately this is a lifetime relationship that can't + // be expressed without generic associated types, better unification of HRTBs in type position, + // and some kind of integration into the Fn* traits. + struct LazyEncode { + inner: F, + encoded: Option>, + } + + impl Vec> traits::Lazy<[u8]> for LazyEncode { + fn get(&mut self) -> &[u8] { + self.encoded.get_or_insert_with(&self.inner).as_slice() + } + } + + sig.verify( + LazyEncode { inner: || item.encode(), encoded: None }, + signer, + ) +} + +#[macro_export] +macro_rules! __impl_outer_config_types { + ($concrete:ident $config:ident $snake:ident $($rest:ident)*) => { + #[cfg(any(feature = "std", test))] + pub type $config = $snake::GenesisConfig<$concrete>; + __impl_outer_config_types! {$concrete $($rest)*} + }; + ($concrete:ident) => () +} + +#[macro_export] +/// Implement the output "meta" module configuration struct. +macro_rules! impl_outer_config { + ( pub struct $main:ident for $concrete:ident { $( $config:ident => $snake:ident, )* } ) => { + __impl_outer_config_types! { $concrete $( $config $snake )* } + #[cfg(any(feature = "std", test))] + #[derive(Serialize, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(deny_unknown_fields)] + pub struct $main { + $( + pub $snake: Option<$config>, + )* + } + #[cfg(any(feature = "std", test))] + impl $crate::BuildStorage for $main { + fn build_storage(self) -> ::std::result::Result<$crate::StorageMap, String> { + let mut s = $crate::StorageMap::new(); + $( + if let Some(extra) = self.$snake { + s.extend(extra.build_storage()?); + } + )* + Ok(s) + } + } + } +} + +/// Generates enum that contains all possible log entries for the runtime. +/// Every individual module of the runtime that is mentioned, must +/// expose a `Log` and `RawLog` enums. +/// +/// Generated enum is binary-compatible with and could be interpreted +/// as `generic::DigestItem`. +/// +/// Requires `use runtime_primitives::generic;` to be used. +/// +/// Runtime requirements: +/// 1) binary representation of all supported 'system' log items should stay +/// the same. Otherwise, the native code will be unable to read log items +/// generated by previous runtime versions +/// 2) the support of 'system' log items should never be dropped by runtime. +/// Otherwise, native code will lost its ability to read items of this type +/// even if they were generated by the versions which have supported these +/// items. +#[macro_export] +macro_rules! impl_outer_log { + ( + $(#[$attr:meta])* + pub enum $name:ident ($internal:ident: DigestItem<$( $genarg:ty ),*>) for $trait:ident { + $( $module:ident($( $item:ident ),*) ),* + } + ) => { + /// Wrapper for all possible log entries for the `$trait` runtime. Provides binary-compatible + /// `Encode`/`Decode` implementations with the corresponding `generic::DigestItem`. + #[derive(Clone, PartialEq, Eq)] + #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] + $(#[$attr])* + #[allow(non_camel_case_types)] + pub struct $name($internal); + + /// All possible log entries for the `$trait` runtime. `Encode`/`Decode` implementations + /// are auto-generated => it is not binary-compatible with `generic::DigestItem`. + #[derive(Clone, PartialEq, Eq, Encode, Decode)] + #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] + $(#[$attr])* + #[allow(non_camel_case_types)] + enum $internal { + $( + $module($module::Log<$trait>), + )* + } + + impl $name { + /// Try to convert `$name` into `generic::DigestItemRef`. Returns Some when + /// `self` is a 'system' log && it has been marked as 'system' in macro call. + /// Otherwise, None is returned. + #[allow(unreachable_patterns)] + fn dref<'a>(&'a self) -> Option> { + match self.0 { + $($( + $internal::$module($module::RawLog::$item(ref v)) => + Some(generic::DigestItemRef::$item(v)), + )*)* + _ => None, + } + } + } + + impl From> for $name { + /// Converts `generic::DigestItem` into `$name`. If `generic::DigestItem` represents + /// a system item which is supported by the runtime, it is returned. + /// Otherwise we expect a `Other` log item. Trying to convert from anything other + /// will lead to panic in runtime, since the runtime does not supports this 'system' + /// log item. + #[allow(unreachable_patterns)] + fn from(gen: generic::DigestItem<$($genarg),*>) -> Self { + match gen { + $($( + generic::DigestItem::$item(value) => + $name($internal::$module($module::RawLog::$item(value))), + )*)* + _ => gen.as_other() + .and_then(|value| Decode::decode(&mut &value[..])) + .map($name) + .expect("not allowed to fail in runtime"), + } + } + } + + impl Decode for $name { + /// `generic::DigestItem` binray compatible decode. + fn decode(input: &mut I) -> Option { + let gen: generic::DigestItem<$($genarg),*> = Decode::decode(input)?; + Some($name::from(gen)) + } + } + + impl Encode for $name { + /// `generic::DigestItem` binray compatible encode. + fn encode(&self) -> Vec { + match self.dref() { + Some(dref) => dref.encode(), + None => { + let gen: generic::DigestItem<$($genarg),*> = generic::DigestItem::Other(self.0.encode()); + gen.encode() + }, + } + } + } + + $( + impl From<$module::Log<$trait>> for $name { + /// Converts single module log item into `$name`. + fn from(x: $module::Log<$trait>) -> Self { + $name(x.into()) + } + } + + impl From<$module::Log<$trait>> for $internal { + /// Converts single module log item into `$internal`. + fn from(x: $module::Log<$trait>) -> Self { + $internal::$module(x) + } + } + )* + }; +} + +#[cfg(test)] +mod tests { + use codec::{Encode, Decode, Input}; + + pub trait RuntimeT { + type AuthorityId; + } + + pub struct Runtime; + + impl RuntimeT for Runtime { + type AuthorityId = u64; + } + + #[test] + fn impl_outer_log_works() { + mod a { + use super::RuntimeT; + pub type Log = RawLog<::AuthorityId>; + + #[derive(Serialize, Deserialize, Debug, Encode, Decode, PartialEq, Eq, Clone)] + pub enum RawLog { A1(AuthorityId), AuthoritiesChange(Vec), A3(AuthorityId) } + } + + mod b { + use super::RuntimeT; + pub type Log = RawLog<::AuthorityId>; + + #[derive(Serialize, Deserialize, Debug, Encode, Decode, PartialEq, Eq, Clone)] + pub enum RawLog { B1(AuthorityId), B2(AuthorityId) } + } + + use super::generic; // required before macro invocation + + // TODO try to avoid redundant brackets: a(AuthoritiesChange), b + impl_outer_log! { + pub enum Log(InternalLog: DigestItem) for Runtime { + a(AuthoritiesChange), b() + } + } + + // encode/decode regular item + let b1: Log = b::RawLog::B1::(777).into(); + let encoded_b1 = b1.encode(); + let decoded_b1: Log = Decode::decode(&mut &encoded_b1[..]).unwrap(); + assert_eq!(b1, decoded_b1); + + // encode/decode system item + let auth_change: Log = a::RawLog::AuthoritiesChange::(vec![100, 200, 300]).into(); + let encoded_auth_change = auth_change.encode(); + let decoded_auth_change: Log = Decode::decode(&mut &encoded_auth_change[..]).unwrap(); + assert_eq!(auth_change, decoded_auth_change); + + // interpret regular item using `generic::DigestItem` + let generic_b1: generic::DigestItem = Decode::decode(&mut &encoded_b1[..]).unwrap(); + match generic_b1 { + generic::DigestItem::Other(_) => (), + _ => panic!("unexpected generic_b1: {:?}", generic_b1), + } + + // interpret system item using `generic::DigestItem` + let generic_auth_change: generic::DigestItem = Decode::decode(&mut &encoded_auth_change[..]).unwrap(); + match generic_auth_change { + generic::DigestItem::AuthoritiesChange(authorities) => assert_eq!(authorities, vec![100, 200, 300]), + _ => panic!("unexpected generic_auth_change: {:?}", generic_auth_change), + } + } +} diff --git a/runtime/primitives/src/testing.rs b/runtime/primitives/src/testing.rs new file mode 100644 index 0000000000000..764828bfe5662 --- /dev/null +++ b/runtime/primitives/src/testing.rs @@ -0,0 +1,138 @@ +// Copyright 2017 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 . + +//! Testing utilities. + +use serde::{Serialize, de::DeserializeOwned}; +use std::fmt::Debug; +use codec::Codec; +use runtime_support::Dispatchable; +use traits::{self, Checkable, Applyable, BlakeTwo256}; + +pub use substrate_primitives::H256; + +#[derive(Default, PartialEq, Eq, Clone, Serialize, Deserialize, Debug, Encode, Decode)] +pub struct Digest { + pub logs: Vec, +} + +impl traits::Digest for Digest { + type Item = u64; + + fn logs(&self) -> &[Self::Item] { + &self.logs + } + + fn push(&mut self, item: Self::Item) { + self.logs.push(item); + } +} + +impl traits::DigestItem for () { + type AuthorityId = (); +} + +impl traits::DigestItem for u64 { + type AuthorityId = (); +} + +#[derive(PartialEq, Eq, Clone, Serialize, Deserialize, Debug, Encode, Decode)] +#[serde(rename_all = "camelCase")] +#[serde(deny_unknown_fields)] +pub struct Header { + pub parent_hash: H256, + pub number: u64, + pub state_root: H256, + pub extrinsics_root: H256, + pub digest: Digest, +} + +impl traits::Header for Header { + type Number = u64; + type Hashing = BlakeTwo256; + type Hash = H256; + type Digest = Digest; + + fn number(&self) -> &Self::Number { &self.number } + fn set_number(&mut self, num: Self::Number) { self.number = num } + + fn extrinsics_root(&self) -> &Self::Hash { &self.extrinsics_root } + fn set_extrinsics_root(&mut self, root: Self::Hash) { self.extrinsics_root = root } + + fn state_root(&self) -> &Self::Hash { &self.state_root } + fn set_state_root(&mut self, root: Self::Hash) { self.state_root = root } + + fn parent_hash(&self) -> &Self::Hash { &self.parent_hash } + fn set_parent_hash(&mut self, hash: Self::Hash) { self.parent_hash = hash } + + fn digest(&self) -> &Self::Digest { &self.digest } + fn set_digest(&mut self, digest: Self::Digest) { self.digest = digest } + + fn new( + number: Self::Number, + extrinsics_root: Self::Hash, + state_root: Self::Hash, + parent_hash: Self::Hash, + digest: Self::Digest + ) -> Self { + Header { + number, extrinsics_root: extrinsics_root, state_root, parent_hash, digest + } + } +} + +#[derive(PartialEq, Eq, Clone, Serialize, Deserialize, Debug, Encode, Decode)] +pub struct Block { + pub header: Header, + pub extrinsics: Vec, +} + +impl traits::Block for Block { + type Extrinsic = Xt; + type Header = Header; + type Hash =

::Hash; + + fn header(&self) -> &Self::Header { + &self.header + } + fn extrinsics(&self) -> &[Self::Extrinsic] { + &self.extrinsics[..] + } + fn deconstruct(self) -> (Self::Header, Vec) { + (self.header, self.extrinsics) + } + fn new(header: Self::Header, extrinsics: Vec) -> Self { + Block { header, extrinsics } + } +} + +#[derive(PartialEq, Eq, Clone, Serialize, Deserialize, Debug, Encode, Decode)] +pub struct TestXt(pub Option, pub u64, pub Call); + +impl Checkable for TestXt { + type Checked = Self; + fn check_with(self, _: Context) -> Result { Ok(self) } +} +impl Applyable for TestXt where + Call: Sized + Send + Sync + Clone + Eq + Dispatchable + Codec + Debug + Serialize + DeserializeOwned, + ::Origin: From> +{ + type AccountId = u64; + type Index = u64; + fn sender(&self) -> Option<&u64> { self.0.as_ref() } + fn index(&self) -> &u64 { &self.1 } + fn apply(self) -> Result<(), &'static str> { self.2.dispatch(self.0.into()) } +} diff --git a/runtime/primitives/src/traits.rs b/runtime/primitives/src/traits.rs new file mode 100644 index 0000000000000..32aa47a16f95a --- /dev/null +++ b/runtime/primitives/src/traits.rs @@ -0,0 +1,462 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Primitives for the runtime modules. + +use rstd::prelude::*; +use rstd::{self, result}; +use runtime_io; +#[cfg(feature = "std")] use std::fmt::{Debug, Display}; +#[cfg(feature = "std")] use serde::{Serialize, de::DeserializeOwned}; +use substrate_primitives; +use substrate_primitives::Blake2Hasher; +use codec::{Codec, Encode}; +pub use integer_sqrt::IntegerSquareRoot; +pub use num_traits::{Zero, One, Bounded}; +pub use num_traits::ops::checked::{CheckedAdd, CheckedSub, CheckedMul, CheckedDiv}; +use rstd::ops::{Add, Sub, Mul, Div, Rem, AddAssign, SubAssign, MulAssign, DivAssign, + RemAssign, Shl, Shr}; + +/// A lazy value. +pub trait Lazy { + fn get(&mut self) -> &T; +} + +impl<'a> Lazy<[u8]> for &'a [u8] { + fn get(&mut self) -> &[u8] { &**self } +} + +/// Means of signature verification. +pub trait Verify { + /// Type of the signer. + type Signer; + /// Verify a signature. Return `true` if signature is valid for the value. + fn verify>(&self, msg: L, signer: &Self::Signer) -> bool; +} + +/// Some sort of check on the origin is performed by this object. +pub trait EnsureOrigin { + type Success; + fn ensure_origin(o: OuterOrigin) -> Result; +} + +/// Means of changing one type into another in a manner dependent on the source type. +pub trait Lookup { + /// Type to lookup from. + type Source; + /// Type to lookup into. + type Target; + /// Attempt a lookup. + fn lookup(s: Self::Source) -> result::Result; +} + +/// Simple payment making trait, operating on a single generic `AccountId` type. +pub trait MakePayment { + /// Make some sort of payment concerning `who` for an extrinsic (transaction) of encoded length + /// `encoded_len` bytes. Return true iff the payment was successful. + fn make_payment(who: &AccountId, encoded_len: usize) -> Result<(), &'static str>; +} + +impl MakePayment for () { + fn make_payment(_: &T, _: usize) -> Result<(), &'static str> { Ok(()) } +} + +/// Extensible conversion trait. Generic over both source and destination types. +pub trait Convert { + /// Make conversion. + fn convert(a: A) -> B; +} + +/// Simple trait similar to `Into`, except that it can be used to convert numerics between each +/// other. +pub trait As { + /// Convert forward (ala `Into::into`). + fn as_(self) -> T; + /// Convert backward (ala `From::from`). + fn sa(T) -> Self; +} + +macro_rules! impl_numerics { + ( $( $t:ty ),* ) => { + $( + impl_numerics!($t: u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize,); + )* + }; + ( $f:ty : $t:ty, $( $rest:ty, )* ) => { + impl As<$t> for $f { + fn as_(self) -> $t { self as $t } + fn sa(t: $t) -> Self { t as Self } + } + impl_numerics!($f: $( $rest, )*); + }; + ( $f:ty : ) => {} +} + +impl_numerics!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize); + +pub struct Identity; +impl Convert for Identity { + fn convert(a: T) -> T { a } +} +impl Convert for () { + fn convert(_: T) -> () { () } +} + +pub trait RefInto { + fn ref_into(&self) -> &T; +} +impl RefInto for T { + fn ref_into(&self) -> &T { &self } +} + +pub trait SimpleArithmetic: + Zero + One + IntegerSquareRoot + As + + Add + AddAssign + + Sub + SubAssign + + Mul + MulAssign + + Div + DivAssign + + Rem + RemAssign + + Shl + Shr + + CheckedAdd + + CheckedSub + + CheckedMul + + CheckedDiv + + PartialOrd + Ord +{} +impl + + Add + AddAssign + + Sub + SubAssign + + Mul + MulAssign + + Div + DivAssign + + Rem + RemAssign + + Shl + Shr + + CheckedAdd + + CheckedSub + + CheckedMul + + CheckedDiv + + PartialOrd + Ord +> SimpleArithmetic for T {} + +/// Trait for things that can be clear (have no bits set). For numeric types, essentially the same +/// as `Zero`. +pub trait Clear { + /// True iff no bits are set. + fn is_clear(&self) -> bool; + + /// Return the value of Self that is clear. + fn clear() -> Self; +} + +impl Clear for T { + fn is_clear(&self) -> bool { *self == Self::clear() } + fn clear() -> Self { Default::default() } +} + +pub trait SimpleBitOps: + Sized + Clear + + rstd::ops::BitOr + + rstd::ops::BitAnd +{} +impl + + rstd::ops::BitAnd +> SimpleBitOps for T {} + +/// The block finalisation trait. Implementing this lets you express what should happen +/// for your module when the block is ending. +pub trait OnFinalise { + /// The block is being finalised. Implement to have something happen. + fn on_finalise(_n: BlockNumber) {} +} + +impl OnFinalise for () {} + +macro_rules! tuple_impl { + ($one:ident,) => { + impl> OnFinalise for ($one,) { + fn on_finalise(n: Number) { + $one::on_finalise(n); + } + } + }; + ($first:ident, $($rest:ident,)+) => { + impl< + Number: Copy, + $first: OnFinalise, + $($rest: OnFinalise),+ + > OnFinalise for ($first, $($rest),+) { + fn on_finalise(n: Number) { + $first::on_finalise(n); + $($rest::on_finalise(n);)+ + } + } + tuple_impl!($($rest,)+); + } +} + +#[allow(non_snake_case)] +tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z,); + +/// Abstraction around hashing +pub trait Hash: 'static + MaybeSerializeDebug + Clone + Eq + PartialEq { // Stupid bug in the Rust compiler believes derived + // traits must be fulfilled by all type parameters. + /// The hash type produced. + type Output: Member + AsRef<[u8]>; + + /// Produce the hash of some byte-slice. + fn hash(s: &[u8]) -> Self::Output; + + /// Produce the hash of some codec-encodable value. + fn hash_of(s: &S) -> Self::Output { + Encode::using_encoded(s, Self::hash) + } + + /// Produce the patricia-trie root of a mapping from indices to byte slices. + fn enumerated_trie_root(items: &[&[u8]]) -> Self::Output; + + /// Iterator-based version of `enumerated_trie_root`. + fn ordered_trie_root< + I: IntoIterator, + A: AsRef<[u8]> + >(input: I) -> Self::Output; + + /// The Patricia tree root of the given mapping as an iterator. + fn trie_root< + I: IntoIterator, + A: AsRef<[u8]> + Ord, + B: AsRef<[u8]> + >(input: I) -> Self::Output; + + /// Acquire the global storage root. + fn storage_root() -> Self::Output; +} + +/// Blake2-256 Hash implementation. +#[derive(PartialEq, Eq, Clone)] +#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] +pub struct BlakeTwo256; + +impl Hash for BlakeTwo256 { + type Output = substrate_primitives::H256; + fn hash(s: &[u8]) -> Self::Output { + runtime_io::blake2_256(s).into() + } + fn enumerated_trie_root(items: &[&[u8]]) -> Self::Output { + runtime_io::enumerated_trie_root::(items).into() + } + fn trie_root< + I: IntoIterator, + A: AsRef<[u8]> + Ord, + B: AsRef<[u8]> + >(input: I) -> Self::Output { + runtime_io::trie_root::(input).into() + } + fn ordered_trie_root< + I: IntoIterator, + A: AsRef<[u8]> + >(input: I) -> Self::Output { + runtime_io::ordered_trie_root::(input).into() + } + fn storage_root() -> Self::Output { + runtime_io::storage_root().into() + } +} + +/// Something that can be checked for equality and printed out to a debug channel if bad. +pub trait CheckEqual { + fn check_equal(&self, other: &Self); +} + +impl CheckEqual for substrate_primitives::H256 { + #[cfg(feature = "std")] + fn check_equal(&self, other: &Self) { + use substrate_primitives::hexdisplay::HexDisplay; + if &self.0 != &other.0 { + println!("Hash: given={}, expected={}", HexDisplay::from(&self.0), HexDisplay::from(&other.0)); + } + } + + #[cfg(not(feature = "std"))] + fn check_equal(&self, other: &Self) { + if self != other { + runtime_io::print("Hash not equal"); + runtime_io::print(&self.0[..]); + runtime_io::print(&other.0[..]); + } + } +} + +#[cfg(feature = "std")] +pub trait MaybeSerializeDebugButNotDeserialize: Serialize + Debug {} +#[cfg(feature = "std")] +impl MaybeSerializeDebugButNotDeserialize for T {} + +#[cfg(not(feature = "std"))] +pub trait MaybeSerializeDebugButNotDeserialize {} +#[cfg(not(feature = "std"))] +impl MaybeSerializeDebugButNotDeserialize for T {} + +#[cfg(feature = "std")] +pub trait MaybeSerializeDebug: Serialize + DeserializeOwned + Debug {} +#[cfg(feature = "std")] +impl MaybeSerializeDebug for T {} + +#[cfg(not(feature = "std"))] +pub trait MaybeSerializeDebug {} +#[cfg(not(feature = "std"))] +impl MaybeSerializeDebug for T {} + +#[cfg(feature = "std")] +pub trait MaybeDisplay: Display {} +#[cfg(feature = "std")] +impl MaybeDisplay for T {} + +#[cfg(not(feature = "std"))] +pub trait MaybeDisplay {} +#[cfg(not(feature = "std"))] +impl MaybeDisplay for T {} + +pub trait Member: Send + Sync + Sized + MaybeSerializeDebug + Eq + PartialEq + Clone + 'static {} +impl Member for T {} + +/// 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`. +/// +/// You can also create a `new` one from those fields. +pub trait Header: Clone + Send + Sync + Codec + Eq + MaybeSerializeDebug + 'static { + type Number: Member + ::rstd::hash::Hash + Copy + MaybeDisplay + SimpleArithmetic + Codec; + type Hash: Member + ::rstd::hash::Hash + Copy + MaybeDisplay + Default + SimpleBitOps + Codec + AsRef<[u8]>; + type Hashing: Hash; + type Digest: Digest; + + fn new( + number: Self::Number, + extrinsics_root: Self::Hash, + state_root: Self::Hash, + parent_hash: Self::Hash, + digest: Self::Digest + ) -> Self; + + fn number(&self) -> &Self::Number; + fn set_number(&mut self, Self::Number); + + fn extrinsics_root(&self) -> &Self::Hash; + fn set_extrinsics_root(&mut self, Self::Hash); + + fn state_root(&self) -> &Self::Hash; + fn set_state_root(&mut self, Self::Hash); + + fn parent_hash(&self) -> &Self::Hash; + fn set_parent_hash(&mut self, Self::Hash); + + fn digest(&self) -> &Self::Digest; + fn set_digest(&mut self, Self::Digest); + + fn hash(&self) -> Self::Hash { + ::hash_of(self) + } +} + +/// Something which fulfills the abstract idea of a Substrate block. It has types for an +/// `Extrinsic` piece of information as well as a `Header`. +/// +/// You can get an iterator over each of the `extrinsics` and retrieve the `header`. +pub trait Block: Clone + Send + Sync + Codec + Eq + MaybeSerializeDebug + 'static { + type Extrinsic: Member + Codec; + type Header: Header; + type Hash: Member + ::rstd::hash::Hash + Copy + MaybeDisplay + Default + SimpleBitOps + Codec + AsRef<[u8]>; + + fn header(&self) -> &Self::Header; + fn extrinsics(&self) -> &[Self::Extrinsic]; + fn deconstruct(self) -> (Self::Header, Vec); + fn new(header: Self::Header, extrinsics: Vec) -> Self; + fn hash(&self) -> Self::Hash { + <::Hashing as Hash>::hash_of(self.header()) + } +} + +/// Extract the hashing type for a block. +pub type HashFor = <::Header as Header>::Hashing; +/// Extract the number type for a block. +pub type NumberFor = <::Header as Header>::Number; + +/// A "checkable" piece of information, used by the standard Substrate Executive in order to +/// check the validity of a piece of extrinsic information, usually by verifying the signature. +/// Implement for pieces of information that require some additional context `Context` in order to be +/// checked. +pub trait Checkable: Sized { + /// Returned if `check_with` succeeds. + type Checked; + + fn check_with(self, context: Context) -> Result; +} + +/// A "checkable" piece of information, used by the standard Substrate Executive in order to +/// check the validity of a piece of extrinsic information, usually by verifying the signature. +/// Implement for pieces of information that don't require additional context in order to be +/// checked. +pub trait BlindCheckable: Sized { + /// Returned if `check` succeeds. + type Checked; + + fn check(self) -> Result; +} + +// Every `BlindCheckable` is also a `Checkable` for arbitrary `Context`. +impl Checkable for T { + type Checked = ::Checked; + fn check_with(self, _: Context) -> Result { + BlindCheckable::check(self) + } +} + +/// 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 functioon +/// call. +/// +/// Also provides information on to whom this information is attributable and an index that allows +/// each piece of attributable information to be disambiguated. +pub trait Applyable: Sized + Send + Sync { + type AccountId: Member + MaybeDisplay; + type Index: Member + MaybeDisplay + SimpleArithmetic; + fn index(&self) -> &Self::Index; + fn sender(&self) -> Option<&Self::AccountId>; + fn apply(self) -> Result<(), &'static str>; +} + +/// Something that acts like a `Digest` - it can have `Log`s `push`ed onto it and these `Log`s are +/// each `Codec`. +pub trait Digest: Member + Default { + type Item: DigestItem; + fn logs(&self) -> &[Self::Item]; + fn push(&mut self, item: Self::Item); +} + +/// Single digest item. Could be any type that implements `Member` and provides methods +/// for casting member to 'system' log items, known to substrate. +/// +/// If the runtime does not supports some 'system' items, use `()` as a stub. +pub trait DigestItem: Member { + type AuthorityId; + + /// Returns Some if the entry is the `AuthoritiesChange` entry. + fn as_authorities_change(&self) -> Option<&[Self::AuthorityId]> { + None + } +} diff --git a/runtime/session/Cargo.toml b/runtime/session/Cargo.toml new file mode 100644 index 0000000000000..9cffec07b8ddd --- /dev/null +++ b/runtime/session/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "substrate-runtime-session" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +hex-literal = "0.1.0" +serde = { version = "1.0", default_features = false } +serde_derive = { version = "1.0", optional = true } +safe-mix = { version = "1.0", default_features = false} +substrate-keyring = { path = "../../core/keyring", optional = true } +substrate-codec = { path = "../../core/codec", default_features = false } +substrate-codec-derive = { path = "../../core/codec/derive", default_features = false } +substrate-primitives = { path = "../../core/primitives", default_features = false } +substrate-runtime-std = { path = "../../core/runtime-std", default_features = false } +substrate-runtime-io = { path = "../../core/runtime-io", default_features = false } +substrate-runtime-support = { path = "../support", default_features = false } +substrate-runtime-primitives = { path = "../primitives", default_features = false } +substrate-runtime-consensus = { path = "../consensus", default_features = false } +substrate-runtime-system = { path = "../system", default_features = false } +substrate-runtime-timestamp = { path = "../timestamp", default_features = false } + +[features] +default = ["std"] +std = [ + "serde/std", + "serde_derive", + "safe-mix/std", + "substrate-keyring", + "substrate-codec/std", + "substrate-codec-derive/std", + "substrate-primitives/std", + "substrate-runtime-std/std", + "substrate-runtime-io/std", + "substrate-runtime-support/std", + "substrate-runtime-primitives/std", + "substrate-runtime-consensus/std", + "substrate-runtime-system/std", + "substrate-runtime-timestamp/std" +] diff --git a/runtime/session/src/lib.rs b/runtime/session/src/lib.rs new file mode 100644 index 0000000000000..007dd43729f7c --- /dev/null +++ b/runtime/session/src/lib.rs @@ -0,0 +1,467 @@ +// Copyright 2017 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 . + +//! Session manager: is told the validators and allows them to manage their session keys for the +//! consensus module. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(feature = "std")] +extern crate serde; + +#[cfg(feature = "std")] +#[macro_use] +extern crate serde_derive; + +#[cfg(any(feature = "std", test))] +extern crate substrate_keyring as keyring; + +#[cfg(any(feature = "std", test))] +extern crate substrate_primitives; + +#[cfg_attr(feature = "std", macro_use)] +extern crate substrate_runtime_std as rstd; + +#[macro_use] +extern crate substrate_runtime_support as runtime_support; + +#[macro_use] +extern crate substrate_codec_derive; + +extern crate substrate_runtime_io as runtime_io; +extern crate substrate_codec as codec; +extern crate substrate_runtime_primitives as primitives; +extern crate substrate_runtime_consensus as consensus; +extern crate substrate_runtime_system as system; +extern crate substrate_runtime_timestamp as timestamp; + +use rstd::prelude::*; +use primitives::traits::{Zero, One, OnFinalise, Convert, As}; +use runtime_support::{StorageValue, StorageMap}; +use runtime_support::dispatch::Result; +use system::{ensure_signed, ensure_root}; + +#[cfg(any(feature = "std", test))] +use std::collections::HashMap; + +/// A session has changed. +pub trait OnSessionChange { + /// Session has changed. + fn on_session_change(time_elapsed: T, should_reward: bool); +} + +impl OnSessionChange for () { + fn on_session_change(_: T, _: bool) {} +} + +pub trait Trait: timestamp::Trait { + type ConvertAccountIdToSessionKey: Convert; + type OnSessionChange: OnSessionChange; + type Event: From> + Into<::Event>; +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + fn set_key(origin, key: T::SessionKey) -> Result; + + fn set_length(origin, new: T::BlockNumber) -> Result; + fn force_new_session(origin, apply_rewards: bool) -> Result; + } +} + +/// An event in this module. +decl_event!( + pub enum Event with RawEvent + where ::BlockNumber + { + /// New session has happened. Note that the argument is the session index, not the block + /// number as the type might suggest. + NewSession(BlockNumber), + } +); + +decl_storage! { + trait Store for Module as Session { + + /// The current set of validators. + pub Validators get(validators): required Vec; + /// Current length of the session. + pub SessionLength get(length): required T::BlockNumber; + /// Current index of the session. + pub CurrentIndex get(current_index): required T::BlockNumber; + /// Timestamp when current session started. + pub CurrentStart get(current_start): required T::Moment; + + /// New session is being forced is this entry exists; in which case, the boolean value is whether + /// the new session should be considered a normal rotation (rewardable) or exceptional (slashable). + pub ForcingNewSession get(forcing_new_session): bool; + /// Block at which the session length last changed. + LastLengthChange: T::BlockNumber; + /// The next key for a given validator. + NextKeyFor: map [ T::AccountId => T::SessionKey ]; + /// The next session length. + NextSessionLength: T::BlockNumber; + } +} + +impl Module { + + /// Deposit one of this module's events. + fn deposit_event(event: Event) { + >::deposit_event(::Event::from(event).into()); + } + + /// The number of validators currently. + pub fn validator_count() -> u32 { + >::get().len() as u32 // TODO: can probably optimised + } + + /// The last length change, if there was one, zero if not. + pub fn last_length_change() -> T::BlockNumber { + >::get().unwrap_or_else(T::BlockNumber::zero) + } + + /// Sets the session key of `_validator` to `_key`. This doesn't take effect until the next + /// session. + fn set_key(origin: T::Origin, key: T::SessionKey) -> Result { + let who = ensure_signed(origin)?; + // set new value for next session + >::insert(who, key); + Ok(()) + } + + /// Set a new era length. Won't kick in until the next era change (at current length). + fn set_length(origin: T::Origin, new: T::BlockNumber) -> Result { + ensure_root(origin)?; + >::put(new); + Ok(()) + } + + /// Forces a new session. + pub fn force_new_session(origin: T::Origin, apply_rewards: bool) -> Result { + ensure_root(origin)?; + Self::apply_force_new_session(apply_rewards) + } + + // INTERNAL API (available to other runtime modules) + + /// Forces a new session, no origin. + pub fn apply_force_new_session(apply_rewards: bool) -> Result { + >::put(apply_rewards); + Ok(()) + } + + /// Set the current set of validators. + /// + /// Called by `staking::next_era()` only. `next_session` should be called after this in order to + /// update the session keys to the next validator set. + pub fn set_validators(new: &[T::AccountId]) { + >::put(&new.to_vec()); // TODO: optimise. + >::set_authorities( + &new.iter().cloned().map(T::ConvertAccountIdToSessionKey::convert).collect::>() + ); + } + + /// Hook to be called after transaction processing. + pub fn check_rotate_session(block_number: T::BlockNumber) { + // do this last, after the staking system has had chance to switch out the authorities for the + // new set. + // check block number and call next_session if necessary. + let is_final_block = ((block_number - Self::last_length_change()) % Self::length()).is_zero(); + let (should_end_session, apply_rewards) = >::take() + .map_or((is_final_block, is_final_block), |apply_rewards| (true, apply_rewards)); + if should_end_session { + Self::rotate_session(is_final_block, apply_rewards); + } + } + + /// Move onto next session: register the new authority set. + pub fn rotate_session(is_final_block: bool, apply_rewards: bool) { + let now = >::get(); + let time_elapsed = now.clone() - Self::current_start(); + let session_index = >::get() + One::one(); + + Self::deposit_event(RawEvent::NewSession(session_index)); + + // Increment current session index. + >::put(session_index); + >::put(now); + + // Enact era length change. + let len_changed = if let Some(next_len) = >::take() { + >::put(next_len); + true + } else { + false + }; + if len_changed || !is_final_block { + let block_number = >::block_number(); + >::put(block_number); + } + + T::OnSessionChange::on_session_change(time_elapsed, apply_rewards); + + // Update any changes in session keys. + Self::validators().iter().enumerate().for_each(|(i, v)| { + if let Some(n) = >::take(v) { + >::set_authority(i as u32, &n); + } + }); + } + + /// Get the time that should have elapsed over a session if everything was working perfectly. + pub fn ideal_session_duration() -> T::Moment { + let block_period = >::block_period(); + let session_length = >::sa(Self::length()); + session_length * block_period + } + + /// Number of blocks remaining in this session, not counting this one. If the session is + /// due to rotate at the end of this block, then it will return 0. If the just began, then + /// it will return `Self::length() - 1`. + pub fn blocks_remaining() -> T::BlockNumber { + let length = Self::length(); + let length_minus_1 = length - One::one(); + let block_number = >::block_number(); + length_minus_1 - (block_number - Self::last_length_change() + length_minus_1) % length + } +} + +impl OnFinalise for Module { + fn on_finalise(n: T::BlockNumber) { + Self::check_rotate_session(n); + } +} + +#[cfg(any(feature = "std", test))] +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +#[serde(deny_unknown_fields)] +pub struct GenesisConfig { + pub session_length: T::BlockNumber, + pub validators: Vec, +} + +#[cfg(any(feature = "std", test))] +impl Default for GenesisConfig { + fn default() -> Self { + use primitives::traits::As; + GenesisConfig { + session_length: T::BlockNumber::sa(1000), + validators: vec![], + } + } +} + +#[cfg(any(feature = "std", test))] +impl primitives::BuildStorage for GenesisConfig +{ + fn build_storage(self) -> ::std::result::Result, Vec>, String> { + + use codec::Encode; + use primitives::traits::As; + Ok(map![ + Self::hash(>::key()).to_vec() => self.session_length.encode(), + Self::hash(>::key()).to_vec() => T::BlockNumber::sa(0).encode(), + Self::hash(>::key()).to_vec() => T::Moment::zero().encode(), + Self::hash(>::key()).to_vec() => self.validators.encode() + ]) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use runtime_io::with_externalities; + use substrate_primitives::{H256, Blake2Hasher}; + use primitives::BuildStorage; + use primitives::traits::{Identity, BlakeTwo256}; + use primitives::testing::{Digest, Header}; + + impl_outer_origin!{ + pub enum Origin for Test {} + } + + #[derive(Clone, Eq, PartialEq)] + pub struct Test; + impl consensus::Trait for Test { + const NOTE_OFFLINE_POSITION: u32 = 1; + type Log = u64; + type SessionKey = u64; + type OnOfflineValidator = (); + } + impl system::Trait for Test { + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type Digest = Digest; + type AccountId = u64; + type Header = Header; + type Event = (); + } + impl timestamp::Trait for Test { + const TIMESTAMP_SET_POSITION: u32 = 0; + type Moment = u64; + } + impl Trait for Test { + type ConvertAccountIdToSessionKey = Identity; + type OnSessionChange = (); + type Event = (); + } + + type System = system::Module; + type Consensus = consensus::Module; + type Session = Module; + + fn new_test_ext() -> runtime_io::TestExternalities { + let mut t = system::GenesisConfig::::default().build_storage().unwrap(); + t.extend(consensus::GenesisConfig::{ + code: vec![], + authorities: vec![1, 2, 3], + }.build_storage().unwrap()); + t.extend(timestamp::GenesisConfig::{ + period: 5, + }.build_storage().unwrap()); + t.extend(GenesisConfig::{ + session_length: 2, + validators: vec![1, 2, 3], + }.build_storage().unwrap()); + t.into() + } + + #[test] + fn simple_setup_should_work() { + with_externalities(&mut new_test_ext(), || { + assert_eq!(Consensus::authorities(), vec![1, 2, 3]); + assert_eq!(Session::length(), 2); + assert_eq!(Session::validators(), vec![1, 2, 3]); + }); + } + + #[test] + fn should_work_with_early_exit() { + with_externalities(&mut new_test_ext(), || { + System::set_block_number(1); + assert_ok!(Session::set_length(Origin::ROOT, 10)); + assert_eq!(Session::blocks_remaining(), 1); + Session::check_rotate_session(1); + + System::set_block_number(2); + assert_eq!(Session::blocks_remaining(), 0); + Session::check_rotate_session(2); + assert_eq!(Session::length(), 10); + + System::set_block_number(7); + assert_eq!(Session::current_index(), 1); + assert_eq!(Session::blocks_remaining(), 5); + assert_ok!(Session::force_new_session(Origin::ROOT, false)); + Session::check_rotate_session(7); + + System::set_block_number(8); + assert_eq!(Session::current_index(), 2); + assert_eq!(Session::blocks_remaining(), 9); + Session::check_rotate_session(8); + + System::set_block_number(17); + assert_eq!(Session::current_index(), 2); + assert_eq!(Session::blocks_remaining(), 0); + Session::check_rotate_session(17); + + System::set_block_number(18); + assert_eq!(Session::current_index(), 3); + }); + } + + #[test] + fn session_length_change_should_work() { + with_externalities(&mut new_test_ext(), || { + // Block 1: Change to length 3; no visible change. + System::set_block_number(1); + assert_ok!(Session::set_length(Origin::ROOT, 3)); + Session::check_rotate_session(1); + assert_eq!(Session::length(), 2); + assert_eq!(Session::current_index(), 0); + + // Block 2: Length now changed to 3. Index incremented. + System::set_block_number(2); + assert_ok!(Session::set_length(Origin::ROOT, 3)); + Session::check_rotate_session(2); + assert_eq!(Session::length(), 3); + assert_eq!(Session::current_index(), 1); + + // Block 3: Length now changed to 3. Index incremented. + System::set_block_number(3); + Session::check_rotate_session(3); + assert_eq!(Session::length(), 3); + assert_eq!(Session::current_index(), 1); + + // Block 4: Change to length 2; no visible change. + System::set_block_number(4); + assert_ok!(Session::set_length(Origin::ROOT, 2)); + Session::check_rotate_session(4); + assert_eq!(Session::length(), 3); + assert_eq!(Session::current_index(), 1); + + // Block 5: Length now changed to 2. Index incremented. + System::set_block_number(5); + Session::check_rotate_session(5); + assert_eq!(Session::length(), 2); + assert_eq!(Session::current_index(), 2); + + // Block 6: No change. + System::set_block_number(6); + Session::check_rotate_session(6); + assert_eq!(Session::length(), 2); + assert_eq!(Session::current_index(), 2); + + // Block 7: Next index. + System::set_block_number(7); + Session::check_rotate_session(7); + assert_eq!(Session::length(), 2); + assert_eq!(Session::current_index(), 3); + }); + } + + #[test] + fn session_change_should_work() { + with_externalities(&mut new_test_ext(), || { + // Block 1: No change + System::set_block_number(1); + Session::check_rotate_session(1); + assert_eq!(Consensus::authorities(), vec![1, 2, 3]); + + // Block 2: Session rollover, but no change. + System::set_block_number(2); + Session::check_rotate_session(2); + assert_eq!(Consensus::authorities(), vec![1, 2, 3]); + + // Block 3: Set new key for validator 2; no visible change. + System::set_block_number(3); + assert_ok!(Session::set_key(Origin::signed(2), 5)); + assert_eq!(Consensus::authorities(), vec![1, 2, 3]); + + Session::check_rotate_session(3); + assert_eq!(Consensus::authorities(), vec![1, 2, 3]); + + // Block 4: Session rollover, authority 2 changes. + System::set_block_number(4); + Session::check_rotate_session(4); + assert_eq!(Consensus::authorities(), vec![1, 5, 3]); + }); + } +} diff --git a/runtime/staking/Cargo.toml b/runtime/staking/Cargo.toml new file mode 100644 index 0000000000000..84a14febae386 --- /dev/null +++ b/runtime/staking/Cargo.toml @@ -0,0 +1,45 @@ +[package] +name = "substrate-runtime-staking" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +hex-literal = "0.1.0" +serde = { version = "1.0", default_features = false } +serde_derive = { version = "1.0", optional = true } +safe-mix = { version = "1.0", default_features = false} +substrate-keyring = { path = "../../core/keyring", optional = true } +substrate-codec = { path = "../../core/codec", default_features = false } +substrate-codec-derive = { path = "../../core/codec/derive", default_features = false } +substrate-primitives = { path = "../../core/primitives", default_features = false } +substrate-runtime-std = { path = "../../core/runtime-std", default_features = false } +substrate-runtime-io = { path = "../../core/runtime-io", default_features = false } +substrate-runtime-sandbox = { path = "../../core/runtime-sandbox", default_features = false } +substrate-runtime-support = { path = "../support", default_features = false } +substrate-runtime-primitives = { path = "../primitives", default_features = false } +substrate-runtime-balances = { path = "../balances", default_features = false } +substrate-runtime-consensus = { path = "../consensus", default_features = false } +substrate-runtime-system = { path = "../system", default_features = false } +substrate-runtime-session = { path = "../session", default_features = false } +substrate-runtime-timestamp = { path = "../timestamp", default_features = false } + +[features] +default = ["std"] +std = [ + "serde/std", + "serde_derive", + "safe-mix/std", + "substrate-keyring", + "substrate-codec/std", + "substrate-codec-derive/std", + "substrate-primitives/std", + "substrate-runtime-std/std", + "substrate-runtime-io/std", + "substrate-runtime-sandbox/std", + "substrate-runtime-support/std", + "substrate-runtime-primitives/std", + "substrate-runtime-balances/std", + "substrate-runtime-session/std", + "substrate-runtime-system/std", + "substrate-runtime-timestamp/std" +] diff --git a/runtime/staking/src/genesis_config.rs b/runtime/staking/src/genesis_config.rs new file mode 100644 index 0000000000000..e2b50ca1229ad --- /dev/null +++ b/runtime/staking/src/genesis_config.rs @@ -0,0 +1,77 @@ +// Copyright 2017 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 . + +//! Build a staking genesis block. + +#![cfg(feature = "std")] + +use std::collections::HashMap; +use rstd::prelude::*; +use codec::Encode; +use runtime_support::StorageValue; +use primitives::traits::As; +use substrate_primitives::Blake2Hasher; +use {runtime_io, primitives}; +use super::{Trait, Intentions, CurrentEra, OfflineSlashGrace, MinimumValidatorCount, + BondingDuration, SessionsPerEra, ValidatorCount, SessionReward, OfflineSlash}; + +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +#[serde(deny_unknown_fields)] +pub struct GenesisConfig { + pub sessions_per_era: T::BlockNumber, + pub current_era: T::BlockNumber, + pub intentions: Vec, + pub validator_count: u32, + pub minimum_validator_count: u32, + pub bonding_duration: T::BlockNumber, + pub session_reward: T::Balance, + pub offline_slash: T::Balance, + pub offline_slash_grace: u32, +} + +impl Default for GenesisConfig { + fn default() -> Self { + GenesisConfig { + sessions_per_era: T::BlockNumber::sa(1000), + current_era: T::BlockNumber::sa(0), + intentions: vec![], + validator_count: 0, + minimum_validator_count: 0, + bonding_duration: T::BlockNumber::sa(1000), + session_reward: T::Balance::sa(0), + offline_slash: T::Balance::sa(0), + offline_slash_grace: 0, + } + } +} + +impl primitives::BuildStorage for GenesisConfig { + fn build_storage(self) -> ::std::result::Result, Vec>, String> { + let r: runtime_io::TestExternalities = map![ + Self::hash(>::key()).to_vec() => self.intentions.encode(), + Self::hash(>::key()).to_vec() => self.sessions_per_era.encode(), + Self::hash(>::key()).to_vec() => self.validator_count.encode(), + Self::hash(>::key()).to_vec() => self.minimum_validator_count.encode(), + Self::hash(>::key()).to_vec() => self.bonding_duration.encode(), + Self::hash(>::key()).to_vec() => self.current_era.encode(), + Self::hash(>::key()).to_vec() => self.session_reward.encode(), + Self::hash(>::key()).to_vec() => self.offline_slash.encode(), + Self::hash(>::key()).to_vec() => self.offline_slash_grace.encode() + ]; + Ok(r.into()) + } +} diff --git a/runtime/staking/src/lib.rs b/runtime/staking/src/lib.rs new file mode 100644 index 0000000000000..f9737d6a3c26a --- /dev/null +++ b/runtime/staking/src/lib.rs @@ -0,0 +1,576 @@ +// Copyright 2017 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 . + +//! Staking manager: Periodically determines the best set of validators. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(feature = "std")] +extern crate serde; + +#[cfg(feature = "std")] +#[macro_use] +extern crate serde_derive; + +#[macro_use] +extern crate substrate_runtime_support as runtime_support; + +#[cfg_attr(feature = "std", macro_use)] +extern crate substrate_runtime_std as rstd; + +#[macro_use] +extern crate substrate_codec_derive; + +extern crate substrate_codec as codec; +extern crate substrate_primitives; +extern crate substrate_runtime_io as runtime_io; +extern crate substrate_runtime_primitives as primitives; +extern crate substrate_runtime_balances as balances; +extern crate substrate_runtime_consensus as consensus; +extern crate substrate_runtime_sandbox as sandbox; +extern crate substrate_runtime_session as session; +extern crate substrate_runtime_system as system; +extern crate substrate_runtime_timestamp as timestamp; + +use rstd::prelude::*; +use runtime_support::{Parameter, StorageValue, StorageMap}; +use runtime_support::dispatch::Result; +use session::OnSessionChange; +use primitives::traits::{Zero, One, Bounded, OnFinalise, + As, Lookup}; +use balances::{address::Address, OnDilution}; +use system::{ensure_root, ensure_signed}; + +mod mock; + +mod tests; +mod genesis_config; + +#[cfg(feature = "std")] +pub use genesis_config::GenesisConfig; + +const DEFAULT_MINIMUM_VALIDATOR_COUNT: usize = 4; + +#[derive(PartialEq, Clone)] +#[cfg_attr(test, derive(Debug))] +pub enum LockStatus { + Liquid, + LockedUntil(BlockNumber), + Bonded, +} + +/// Preference of what happens on a slash event. +#[derive(PartialEq, Eq, Clone, Encode, Decode)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] +pub struct ValidatorPrefs { + /// Validator should ensure this many more slashes than is necessary before being unstaked. + pub unstake_threshold: u32, + // Reward that validator takes up-front; only the rest is split between themself and nominators. + pub validator_payment: Balance, +} + +impl Default for ValidatorPrefs { + fn default() -> Self { + ValidatorPrefs { + unstake_threshold: 3, + validator_payment: Default::default(), + } + } +} + +pub trait Trait: balances::Trait + session::Trait { + /// Some tokens minted. + type OnRewardMinted: OnDilution<::Balance>; + + /// The overarching event type. + type Event: From> + Into<::Event>; +} + +decl_module! { + #[cfg_attr(feature = "std", serde(bound(deserialize = "T::Balance: ::serde::de::DeserializeOwned")))] + pub struct Module for enum Call where origin: T::Origin { + fn stake(origin) -> Result; + fn unstake(origin, intentions_index: u32) -> Result; + fn nominate(origin, target: Address) -> Result; + fn unnominate(origin, target_index: u32) -> Result; + fn register_preferences(origin, intentions_index: u32, prefs: ValidatorPrefs) -> Result; + + fn set_sessions_per_era(origin, new: T::BlockNumber) -> Result; + fn set_bonding_duration(origin, new: T::BlockNumber) -> Result; + fn set_validator_count(origin, new: u32) -> Result; + fn force_new_era(origin, apply_rewards: bool) -> Result; + fn set_offline_slash_grace(origin, new: u32) -> Result; + } +} + +/// An event in this module. +decl_event!( + pub enum Event with RawEvent + where ::Balance, ::AccountId + { + /// All validators have been rewarded by the given balance. + Reward(Balance), + /// One validator (and their nominators) has been given a offline-warning (they're still + /// within their grace). The accrued number of slashes is recorded, too. + OfflineWarning(AccountId, u32), + /// One validator (and their nominators) has been slashed by the given amount. + OfflineSlash(AccountId, Balance), + } +); + +pub type PairOf = (T, T); + +decl_storage! { + trait Store for Module as Staking { + + /// The ideal number of staking participants. + pub ValidatorCount get(validator_count): required u32; + /// Minimum number of staking participants before emergency conditions are imposed. + pub MinimumValidatorCount: u32; + /// The length of a staking era in sessions. + pub SessionsPerEra get(sessions_per_era): required T::BlockNumber; + /// Maximum reward, per validator, that is provided per acceptable session. + pub SessionReward get(session_reward): required T::Balance; + /// Slash, per validator that is taken for the first time they are found to be offline. + pub OfflineSlash get(offline_slash): required T::Balance; + /// Number of instances of offline reports before slashing begins for validators. + pub OfflineSlashGrace get(offline_slash_grace): default u32; + /// The length of the bonding duration in blocks. + pub BondingDuration get(bonding_duration): required T::BlockNumber; + + /// The current era index. + pub CurrentEra get(current_era): required T::BlockNumber; + /// Preferences that a validator has. + pub ValidatorPreferences get(validator_preferences): default map [ T::AccountId => ValidatorPrefs ]; + /// All the accounts with a desire to stake. + pub Intentions get(intentions): default Vec; + /// All nominator -> nominee relationships. + pub Nominating get(nominating): map [ T::AccountId => T::AccountId ]; + /// Nominators for a particular account. + pub NominatorsFor get(nominators_for): default map [ T::AccountId => Vec ]; + /// Nominators for a particular account that is in action right now. + pub CurrentNominatorsFor get(current_nominators_for): default map [ T::AccountId => Vec ]; + /// The next value of sessions per era. + pub NextSessionsPerEra get(next_sessions_per_era): T::BlockNumber; + /// The session index at which the era length last changed. + pub LastEraLengthChange get(last_era_length_change): default T::BlockNumber; + + /// The highest and lowest staked validator slashable balances. + pub StakeRange get(stake_range): default PairOf; + + /// The block at which the `who`'s funds become entirely liquid. + pub Bondage get(bondage): default map [ T::AccountId => T::BlockNumber ]; + /// The number of times a given validator has been reported offline. This gets decremented by one each era that passes. + pub SlashCount get(slash_count): default map [ T::AccountId => u32 ]; + + /// We are forcing a new era. + pub ForcingNewEra get(forcing_new_era): (); + } +} + +impl Module { + + /// Deposit one of this module's events. + fn deposit_event(event: Event) { + >::deposit_event(::Event::from(event).into()); + } + + // PUBLIC IMMUTABLES + + /// MinimumValidatorCount getter, introduces a default. + pub fn minimum_validator_count() -> usize { + >::get().map(|v| v as usize).unwrap_or(DEFAULT_MINIMUM_VALIDATOR_COUNT) + } + + /// The length of a staking era in blocks. + pub fn era_length() -> T::BlockNumber { + Self::sessions_per_era() * >::length() + } + + /// Balance of a (potential) validator that includes all nominators. + pub fn nomination_balance(who: &T::AccountId) -> T::Balance { + Self::nominators_for(who).iter() + .map(>::total_balance) + .fold(Zero::zero(), |acc, x| acc + x) + } + + /// The total balance that can be slashed from an account. + pub fn slashable_balance(who: &T::AccountId) -> T::Balance { + Self::nominators_for(who).iter() + .map(>::total_balance) + .fold(>::total_balance(who), |acc, x| acc + x) + } + + /// The block at which the `who`'s funds become entirely liquid. + pub fn unlock_block(who: &T::AccountId) -> LockStatus { + match Self::bondage(who) { + i if i == T::BlockNumber::max_value() => LockStatus::Bonded, + i if i <= >::block_number() => LockStatus::Liquid, + i => LockStatus::LockedUntil(i), + } + } + + // PUBLIC DISPATCH + + /// Declare the desire to stake for the transactor. + /// + /// Effects will be felt at the beginning of the next era. + fn stake(origin: T::Origin) -> Result { + let who = ensure_signed(origin)?; + ensure!(Self::nominating(&who).is_none(), "Cannot stake if already nominating."); + let mut intentions = >::get(); + // can't be in the list twice. + ensure!(intentions.iter().find(|&t| t == &who).is_none(), "Cannot stake if already staked."); + + >::insert(&who, T::BlockNumber::max_value()); + intentions.push(who); + >::put(intentions); + Ok(()) + } + + /// Retract the desire to stake for the transactor. + /// + /// Effects will be felt at the beginning of the next era. + fn unstake(origin: T::Origin, intentions_index: u32) -> Result { + let who = ensure_signed(origin)?; + // unstake fails in degenerate case of having too few existing staked parties + if Self::intentions().len() <= Self::minimum_validator_count() { + return Err("cannot unstake when there are too few staked participants") + } + Self::apply_unstake(&who, intentions_index as usize) + } + + fn nominate(origin: T::Origin, target: Address) -> Result { + let who = ensure_signed(origin)?; + let target = >::lookup(target)?; + + ensure!(Self::nominating(&who).is_none(), "Cannot nominate if already nominating."); + ensure!(Self::intentions().iter().find(|&t| t == &who).is_none(), "Cannot nominate if already staked."); + + // update nominators_for + let mut t = Self::nominators_for(&target); + t.push(who.clone()); + >::insert(&target, t); + + // update nominating + >::insert(&who, &target); + + // Update bondage + >::insert(&who, T::BlockNumber::max_value()); + + Ok(()) + } + + /// Will panic if called when source isn't currently nominating target. + /// Updates Nominating, NominatorsFor and NominationBalance. + fn unnominate(origin: T::Origin, target_index: u32) -> Result { + let source = ensure_signed(origin)?; + let target_index = target_index as usize; + + let target = >::get(&source).ok_or("Account must be nominating")?; + + let mut t = Self::nominators_for(&target); + if t.get(target_index) != Some(&source) { + return Err("Invalid target index") + } + + // Ok - all valid. + + // update nominators_for + t.swap_remove(target_index); + >::insert(&target, t); + + // update nominating + >::remove(&source); + + // update bondage + >::insert(source, >::block_number() + Self::bonding_duration()); + Ok(()) + } + + /// Set the given account's preference for slashing behaviour should they be a validator. + /// + /// An error (no-op) if `Self::intentions()[intentions_index] != origin`. + fn register_preferences( + origin: T::Origin, + intentions_index: u32, + prefs: ValidatorPrefs + ) -> Result { + let who = ensure_signed(origin)?; + + if Self::intentions().get(intentions_index as usize) != Some(&who) { + return Err("Invalid index") + } + + >::insert(who, prefs); + + Ok(()) + } + + // PRIV DISPATCH + + /// Set the number of sessions in an era. + fn set_sessions_per_era(origin: T::Origin, new: T::BlockNumber) -> Result { + ensure_root(origin)?; + >::put(&new); + Ok(()) + } + + /// The length of the bonding duration in eras. + fn set_bonding_duration(origin: T::Origin, new: T::BlockNumber) -> Result { + ensure_root(origin)?; + >::put(&new); + Ok(()) + } + + /// The length of a staking era in sessions. + fn set_validator_count(origin: T::Origin, new: u32) -> Result { + ensure_root(origin)?; + >::put(&new); + Ok(()) + } + + /// Force there to be a new era. This also forces a new session immediately after. + /// `apply_rewards` should be true for validators to get the session reward. + fn force_new_era(origin: T::Origin, apply_rewards: bool) -> Result { + ensure_root(origin)?; + Self::apply_force_new_era(apply_rewards) + } + + // Just force_new_era without origin check. + fn apply_force_new_era(apply_rewards: bool) -> Result { + >::put(()); + >::apply_force_new_session(apply_rewards) + } + + + /// Set the offline slash grace period. + fn set_offline_slash_grace(origin: T::Origin, new: u32) -> Result { + ensure_root(origin)?; + >::put(&new); + Ok(()) + } + + // PUBLIC MUTABLES (DANGEROUS) + + /// Slash a given validator by a specific amount. Removes the slash from their balance by preference, + /// and reduces the nominators' balance if needed. + fn slash_validator(v: &T::AccountId, slash: T::Balance) { + // skip the slash in degenerate case of having only 4 staking participants despite having a larger + // desired number of validators (validator_count). + if Self::intentions().len() <= Self::minimum_validator_count() { + return + } + + if let Some(rem) = >::slash(v, slash) { + let noms = Self::current_nominators_for(v); + let total = noms.iter().map(>::total_balance).fold(T::Balance::zero(), |acc, x| acc + x); + if !total.is_zero() { + let safe_mul_rational = |b| b * rem / total;// TODO: avoid overflow + for n in noms.iter() { + let _ = >::slash(n, safe_mul_rational(>::total_balance(n))); // best effort - not much that can be done on fail. + } + } + } + } + + /// Reward a given validator by a specific amount. Add the reward to their, and their nominators' + /// balance, pro-rata. + fn reward_validator(who: &T::AccountId, reward: T::Balance) { + let off_the_table = reward.min(Self::validator_preferences(who).validator_payment); + let reward = reward - off_the_table; + let validator_cut = if reward.is_zero() { + Zero::zero() + } else { + let noms = Self::current_nominators_for(who); + let total = noms.iter() + .map(>::total_balance) + .fold(>::total_balance(who), |acc, x| acc + x) + .max(One::one()); + let safe_mul_rational = |b| b * reward / total;// TODO: avoid overflow + for n in noms.iter() { + let _ = >::reward(n, safe_mul_rational(>::total_balance(n))); + } + safe_mul_rational(>::total_balance(who)) + }; + let _ = >::reward(who, validator_cut + off_the_table); + } + + /// Actually carry out the unstake operation. + /// Assumes `intentions()[intentions_index] == who`. + fn apply_unstake(who: &T::AccountId, intentions_index: usize) -> Result { + let mut intentions = Self::intentions(); + if intentions.get(intentions_index) != Some(who) { + return Err("Invalid index"); + } + intentions.swap_remove(intentions_index); + >::put(intentions); + >::remove(who); + >::remove(who); + >::insert(who, >::block_number() + Self::bonding_duration()); + Ok(()) + } + + /// Get the reward for the session, assuming it ends with this block. + fn this_session_reward(actual_elapsed: T::Moment) -> T::Balance { + let ideal_elapsed = >::ideal_session_duration(); + let per65536: u64 = (T::Moment::sa(65536u64) * ideal_elapsed.clone() / actual_elapsed.max(ideal_elapsed)).as_(); + Self::session_reward() * T::Balance::sa(per65536) / T::Balance::sa(65536u64) + } + + /// Session has just changed. We need to determine whether we pay a reward, slash and/or + /// move to a new era. + fn new_session(actual_elapsed: T::Moment, should_reward: bool) { + if should_reward { + // apply good session reward + let reward = Self::this_session_reward(actual_elapsed); + let validators = >::validators(); + for v in validators.iter() { + Self::reward_validator(v, reward); + } + Self::deposit_event(RawEvent::Reward(reward)); + let total_minted = reward * >::sa(validators.len()); + let total_rewarded_stake = Self::stake_range().0 * >::sa(validators.len()); + T::OnRewardMinted::on_dilution(total_minted, total_rewarded_stake); + } + + let session_index = >::current_index(); + if >::take().is_some() + || ((session_index - Self::last_era_length_change()) % Self::sessions_per_era()).is_zero() + { + Self::new_era(); + } + } + + /// The era has changed - enact new staking set. + /// + /// 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() { + // Increment current era. + >::put(&(>::get() + One::one())); + + // Enact era length change. + if let Some(next_spe) = Self::next_sessions_per_era() { + if next_spe != Self::sessions_per_era() { + >::put(&next_spe); + >::put(&>::current_index()); + } + } + + // evaluate desired staking amounts and nominations and optimise to find the best + // combination of validators, then use session::internal::set_validators(). + // for now, this just orders would-be stakers by their balances and chooses the top-most + // >::get() of them. + // TODO: this is not sound. this should be moved to an off-chain solution mechanism. + let mut intentions = Self::intentions() + .into_iter() + .map(|v| (Self::slashable_balance(&v), v)) + .collect::>(); + + // Avoid reevaluate validator set if it would leave us with fewer than the minimum + // needed validators + if intentions.len() < Self::minimum_validator_count() { + return + } + + intentions.sort_unstable_by(|&(ref b1, _), &(ref b2, _)| b2.cmp(&b1)); + + >::put( + if !intentions.is_empty() { + let n = >::get() as usize; + (intentions[0].0, intentions[n - 1].0) + } else { + (Zero::zero(), Zero::zero()) + } + ); + let vals = &intentions.into_iter() + .map(|(_, v)| v) + .take(>::get() as usize) + .collect::>(); + for v in >::validators().iter() { + >::remove(v); + let slash_count = >::take(v); + if slash_count > 1 { + >::insert(v, slash_count - 1); + } + } + for v in vals.iter() { + >::insert(v, Self::nominators_for(v)); + } + >::set_validators(vals); + } +} + +impl OnFinalise for Module { + fn on_finalise(_n: T::BlockNumber) { + } +} + +impl OnSessionChange for Module { + fn on_session_change(elapsed: T::Moment, should_reward: bool) { + Self::new_session(elapsed, should_reward); + } +} + +impl balances::EnsureAccountLiquid for Module { + fn ensure_account_liquid(who: &T::AccountId) -> Result { + if Self::bondage(who) <= >::block_number() { + Ok(()) + } else { + Err("cannot transfer illiquid funds") + } + } +} + +impl balances::OnFreeBalanceZero for Module { + fn on_free_balance_zero(who: &T::AccountId) { + >::remove(who); + } +} + +impl consensus::OnOfflineValidator for Module { + fn on_offline_validator(validator_index: usize) { + let v = >::validators()[validator_index].clone(); + let slash_count = Self::slash_count(&v); + >::insert(v.clone(), slash_count + 1); + let grace = Self::offline_slash_grace(); + + let event = if slash_count >= grace { + let instances = slash_count - grace; + let slash = Self::offline_slash() << instances; + let next_slash = slash << 1u32; + let _ = Self::slash_validator(&v, slash); + if instances >= Self::validator_preferences(&v).unstake_threshold + || Self::slashable_balance(&v) < next_slash + { + if let Some(pos) = Self::intentions().into_iter().position(|x| &x == &v) { + Self::apply_unstake(&v, pos) + .expect("pos derived correctly from Self::intentions(); \ + apply_unstake can only fail if pos wrong; \ + Self::intentions() doesn't change; qed"); + } + let _ = Self::apply_force_new_era(false); + } + RawEvent::OfflineSlash(v, slash) + } else { + RawEvent::OfflineWarning(v, slash_count) + }; + Self::deposit_event(event); + } +} diff --git a/runtime/staking/src/mock.rs b/runtime/staking/src/mock.rs new file mode 100644 index 0000000000000..7dda4fe7ebec4 --- /dev/null +++ b/runtime/staking/src/mock.rs @@ -0,0 +1,126 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Test utilities + +#![cfg(test)] + +use primitives::BuildStorage; +use primitives::traits::{Identity}; +use primitives::testing::{Digest, Header}; +use substrate_primitives::{H256, Blake2Hasher}; +use runtime_io; +use {GenesisConfig, Module, Trait, consensus, session, system, timestamp, balances}; + +impl_outer_origin!{ + pub enum Origin for Test {} +} + +// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. +#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] +pub struct Test; +impl consensus::Trait for Test { + const NOTE_OFFLINE_POSITION: u32 = 1; + type Log = u64; + type SessionKey = u64; + type OnOfflineValidator = (); +} +impl system::Trait for Test { + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = ::primitives::traits::BlakeTwo256; + type Digest = Digest; + type AccountId = u64; + type Header = Header; + type Event = (); +} +impl balances::Trait for Test { + type Balance = u64; + type AccountIndex = u64; + type OnFreeBalanceZero = Staking; + type EnsureAccountLiquid = Staking; + type Event = (); +} +impl session::Trait for Test { + type ConvertAccountIdToSessionKey = Identity; + type OnSessionChange = Staking; + type Event = (); +} +impl timestamp::Trait for Test { + const TIMESTAMP_SET_POSITION: u32 = 0; + type Moment = u64; +} +impl Trait for Test { + type OnRewardMinted = (); + type Event = (); +} + +pub fn new_test_ext(ext_deposit: u64, session_length: u64, sessions_per_era: u64, current_era: u64, monied: bool, reward: u64) -> runtime_io::TestExternalities { + let mut t = system::GenesisConfig::::default().build_storage().unwrap(); + let balance_factor = if ext_deposit > 0 { + 256 + } else { + 1 + }; + t.extend(consensus::GenesisConfig::{ + code: vec![], + authorities: vec![], + }.build_storage().unwrap()); + t.extend(session::GenesisConfig::{ + session_length, + validators: vec![10, 20], + }.build_storage().unwrap()); + t.extend(balances::GenesisConfig::{ + balances: if monied { + if reward > 0 { + vec![(1, 10 * balance_factor), (2, 20 * balance_factor), (3, 30 * balance_factor), (4, 40 * balance_factor), (10, balance_factor), (20, balance_factor)] + } else { + vec![(1, 10 * balance_factor), (2, 20 * balance_factor), (3, 30 * balance_factor), (4, 40 * balance_factor)] + } + } else { + vec![(10, balance_factor), (20, balance_factor)] + }, + transaction_base_fee: 0, + transaction_byte_fee: 0, + existential_deposit: ext_deposit, + transfer_fee: 0, + creation_fee: 0, + reclaim_rebate: 0, + }.build_storage().unwrap()); + t.extend(GenesisConfig::{ + sessions_per_era, + current_era, + intentions: vec![10, 20], + validator_count: 2, + minimum_validator_count: 0, + bonding_duration: sessions_per_era * session_length * 3, + session_reward: reward, + offline_slash: if monied { 20 } else { 0 }, + offline_slash_grace: 0, + }.build_storage().unwrap()); + t.extend(timestamp::GenesisConfig::{ + period: 5 + }.build_storage().unwrap()); + t.into() +} + +pub type System = system::Module; +pub type Balances = balances::Module; +pub type Session = session::Module; +pub type Timestamp = timestamp::Module; +pub type Staking = Module; diff --git a/runtime/staking/src/tests.rs b/runtime/staking/src/tests.rs new file mode 100644 index 0000000000000..66da1ed1737be --- /dev/null +++ b/runtime/staking/src/tests.rs @@ -0,0 +1,502 @@ +// Copyright 2017 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 consensus::OnOfflineValidator; +use runtime_io::with_externalities; +use mock::{Balances, Session, Staking, System, Timestamp, Test, new_test_ext, Origin}; + +#[test] +fn note_null_offline_should_work() { + with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || { + assert_eq!(Staking::offline_slash_grace(), 0); + assert_eq!(Staking::slash_count(&10), 0); + assert_eq!(Balances::free_balance(&10), 1); + ::system::ExtrinsicIndex::::put(1); + assert_eq!(Staking::slash_count(&10), 0); + assert_eq!(Balances::free_balance(&10), 1); + assert!(Staking::forcing_new_era().is_none()); + }); +} + +#[test] +fn note_offline_should_work() { + with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || { + Balances::set_free_balance(&10, 70); + assert_eq!(Staking::offline_slash_grace(), 0); + assert_eq!(Staking::slash_count(&10), 0); + assert_eq!(Balances::free_balance(&10), 70); + ::system::ExtrinsicIndex::::put(1); + Staking::on_offline_validator(0); + assert_eq!(Staking::slash_count(&10), 1); + assert_eq!(Balances::free_balance(&10), 50); + assert!(Staking::forcing_new_era().is_none()); + }); +} + +#[test] +fn note_offline_exponent_should_work() { + with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || { + Balances::set_free_balance(&10, 150); + assert_eq!(Staking::offline_slash_grace(), 0); + assert_eq!(Staking::slash_count(&10), 0); + assert_eq!(Balances::free_balance(&10), 150); + ::system::ExtrinsicIndex::::put(1); + Staking::on_offline_validator(0); + assert_eq!(Staking::slash_count(&10), 1); + assert_eq!(Balances::free_balance(&10), 130); + ::system::ExtrinsicIndex::::put(1); + Staking::on_offline_validator(0); + assert_eq!(Staking::slash_count(&10), 2); + assert_eq!(Balances::free_balance(&10), 90); + assert!(Staking::forcing_new_era().is_none()); + }); +} + +#[test] +fn note_offline_grace_should_work() { + with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || { + Balances::set_free_balance(&10, 70); + Balances::set_free_balance(&20, 70); + assert_ok!(Staking::set_offline_slash_grace(Origin::ROOT, 1)); + assert_eq!(Staking::offline_slash_grace(), 1); + + assert_eq!(Staking::slash_count(&10), 0); + assert_eq!(Balances::free_balance(&10), 70); + + ::system::ExtrinsicIndex::::put(1); + Staking::on_offline_validator(0); + assert_eq!(Staking::slash_count(&10), 1); + assert_eq!(Balances::free_balance(&10), 70); + assert_eq!(Staking::slash_count(&20), 0); + assert_eq!(Balances::free_balance(&20), 70); + + ::system::ExtrinsicIndex::::put(1); + Staking::on_offline_validator(0); + Staking::on_offline_validator(1); + assert_eq!(Staking::slash_count(&10), 2); + assert_eq!(Balances::free_balance(&10), 50); + assert_eq!(Staking::slash_count(&20), 1); + assert_eq!(Balances::free_balance(&20), 70); + assert!(Staking::forcing_new_era().is_none()); + }); +} + +#[test] +fn note_offline_force_unstake_session_change_should_work() { + with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || { + Balances::set_free_balance(&10, 70); + Balances::set_free_balance(&20, 70); + assert_ok!(Staking::stake(Origin::signed(1))); + + assert_eq!(Staking::slash_count(&10), 0); + assert_eq!(Balances::free_balance(&10), 70); + assert_eq!(Staking::intentions(), vec![10, 20, 1]); + assert_eq!(Session::validators(), vec![10, 20]); + + ::system::ExtrinsicIndex::::put(1); + Staking::on_offline_validator(0); + assert_eq!(Balances::free_balance(&10), 50); + assert_eq!(Staking::slash_count(&10), 1); + assert_eq!(Staking::intentions(), vec![10, 20, 1]); + + ::system::ExtrinsicIndex::::put(1); + Staking::on_offline_validator(0); + assert_eq!(Staking::intentions(), vec![1, 20]); + assert_eq!(Balances::free_balance(&10), 10); + assert!(Staking::forcing_new_era().is_some()); + }); +} + +#[test] +fn note_offline_auto_unstake_session_change_should_work() { + with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || { + Balances::set_free_balance(&10, 7000); + Balances::set_free_balance(&20, 7000); + assert_ok!(Staking::register_preferences(Origin::signed(10), 0, ValidatorPrefs { unstake_threshold: 1, validator_payment: 0 })); + + assert_eq!(Staking::intentions(), vec![10, 20]); + + ::system::ExtrinsicIndex::::put(1); + Staking::on_offline_validator(0); + Staking::on_offline_validator(1); + assert_eq!(Balances::free_balance(&10), 6980); + assert_eq!(Balances::free_balance(&20), 6980); + assert_eq!(Staking::intentions(), vec![10, 20]); + assert!(Staking::forcing_new_era().is_none()); + + ::system::ExtrinsicIndex::::put(1); + Staking::on_offline_validator(0); + Staking::on_offline_validator(1); + assert_eq!(Balances::free_balance(&10), 6940); + assert_eq!(Balances::free_balance(&20), 6940); + assert_eq!(Staking::intentions(), vec![20]); + assert!(Staking::forcing_new_era().is_some()); + + ::system::ExtrinsicIndex::::put(1); + Staking::on_offline_validator(1); + assert_eq!(Balances::free_balance(&10), 6940); + assert_eq!(Balances::free_balance(&20), 6860); + assert_eq!(Staking::intentions(), vec![20]); + + ::system::ExtrinsicIndex::::put(1); + Staking::on_offline_validator(1); + assert_eq!(Balances::free_balance(&10), 6940); + assert_eq!(Balances::free_balance(&20), 6700); + assert_eq!(Staking::intentions(), vec![0u64; 0]); + }); +} + + +#[test] +fn rewards_should_work() { + with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || { + assert_eq!(Staking::era_length(), 9); + assert_eq!(Staking::sessions_per_era(), 3); + assert_eq!(Staking::last_era_length_change(), 0); + assert_eq!(Staking::current_era(), 0); + assert_eq!(Session::current_index(), 0); + assert_eq!(Balances::total_balance(&10), 1); + + System::set_block_number(3); + Timestamp::set_timestamp(15); // on time. + Session::check_rotate_session(System::block_number()); + assert_eq!(Staking::current_era(), 0); + assert_eq!(Session::current_index(), 1); + assert_eq!(Balances::total_balance(&10), 11); + System::set_block_number(6); + Timestamp::set_timestamp(31); // a little late + Session::check_rotate_session(System::block_number()); + assert_eq!(Staking::current_era(), 0); + assert_eq!(Session::current_index(), 2); + assert_eq!(Balances::total_balance(&10), 20); // less reward + System::set_block_number(9); + Timestamp::set_timestamp(50); // very late + Session::check_rotate_session(System::block_number()); + assert_eq!(Staking::current_era(), 1); + assert_eq!(Session::current_index(), 3); + assert_eq!(Balances::total_balance(&10), 27); // much less reward + }); +} + +#[test] +fn slashing_should_work() { + with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || { + assert_eq!(Staking::era_length(), 9); + assert_eq!(Staking::sessions_per_era(), 3); + assert_eq!(Staking::last_era_length_change(), 0); + assert_eq!(Staking::current_era(), 0); + assert_eq!(Session::current_index(), 0); + assert_eq!(Balances::total_balance(&10), 1); + + System::set_block_number(3); + Session::check_rotate_session(System::block_number()); + assert_eq!(Staking::current_era(), 0); + assert_eq!(Session::current_index(), 1); + assert_eq!(Balances::total_balance(&10), 11); + + System::set_block_number(6); + Session::check_rotate_session(System::block_number()); + assert_eq!(Staking::current_era(), 0); + assert_eq!(Session::current_index(), 2); + assert_eq!(Balances::total_balance(&10), 21); + + System::set_block_number(7); + ::system::ExtrinsicIndex::::put(1); + Staking::on_offline_validator(0); + Staking::on_offline_validator(1); + assert_eq!(Balances::total_balance(&10), 1); + }); +} + + + +#[test] +fn staking_should_work() { + with_externalities(&mut new_test_ext(0, 1, 2, 0, true, 0), || { + + assert_eq!(Staking::era_length(), 2); + assert_eq!(Staking::validator_count(), 2); + assert_eq!(Session::validators(), vec![10, 20]); + + assert_ok!(Staking::set_bonding_duration(Origin::ROOT, 2)); + assert_eq!(Staking::bonding_duration(), 2); + + // Block 1: Add three validators. No obvious change. + System::set_block_number(1); + assert_ok!(Staking::stake(Origin::signed(1))); + assert_ok!(Staking::stake(Origin::signed(2))); + assert_ok!(Staking::stake(Origin::signed(4))); + Session::check_rotate_session(System::block_number()); + assert_eq!(Staking::current_era(), 0); + assert_eq!(Session::validators(), vec![10, 20]); + + // Block 2: New validator set now. + System::set_block_number(2); + Session::check_rotate_session(System::block_number()); + assert_eq!(Staking::current_era(), 1); + assert_eq!(Session::validators(), vec![4, 2]); + + // Block 3: Unstake highest, introduce another staker. No change yet. + System::set_block_number(3); + assert_ok!(Staking::stake(Origin::signed(3))); + assert_ok!(Staking::unstake(Origin::signed(4), Staking::intentions().iter().position(|&x| x == 4).unwrap() as u32)); + assert_eq!(Staking::current_era(), 1); + Session::check_rotate_session(System::block_number()); + + // Block 4: New era - validators change. + System::set_block_number(4); + Session::check_rotate_session(System::block_number()); + assert_eq!(Staking::current_era(), 2); + assert_eq!(Session::validators(), vec![3, 2]); + + // Block 5: Transfer stake from highest to lowest. No change yet. + System::set_block_number(5); + assert_ok!(Balances::transfer(Origin::signed(4), 1.into(), 40)); + Session::check_rotate_session(System::block_number()); + + // Block 6: Lowest now validator. + System::set_block_number(6); + Session::check_rotate_session(System::block_number()); + assert_eq!(Session::validators(), vec![1, 3]); + + // Block 7: Unstake three. No change yet. + System::set_block_number(7); + assert_ok!(Staking::unstake(Origin::signed(3), Staking::intentions().iter().position(|&x| x == 3).unwrap() as u32)); + Session::check_rotate_session(System::block_number()); + assert_eq!(Session::validators(), vec![1, 3]); + + // Block 8: Back to one and two. + System::set_block_number(8); + Session::check_rotate_session(System::block_number()); + assert_eq!(Session::validators(), vec![1, 2]); + }); +} + +#[test] +fn nominating_and_rewards_should_work() { + with_externalities(&mut new_test_ext(0, 1, 1, 0, true, 10), || { + assert_eq!(Staking::era_length(), 1); + assert_eq!(Staking::validator_count(), 2); + assert_eq!(Staking::bonding_duration(), 3); + assert_eq!(Session::validators(), vec![10, 20]); + + System::set_block_number(1); + assert_ok!(Staking::stake(Origin::signed(1))); + assert_ok!(Staking::stake(Origin::signed(2))); + assert_ok!(Staking::stake(Origin::signed(3))); + assert_ok!(Staking::nominate(Origin::signed(4), 1.into())); + Session::check_rotate_session(System::block_number()); + assert_eq!(Staking::current_era(), 1); + assert_eq!(Session::validators(), vec![1, 3]); // 4 + 1, 3 + assert_eq!(Balances::total_balance(&1), 10); + assert_eq!(Balances::total_balance(&2), 20); + assert_eq!(Balances::total_balance(&3), 30); + assert_eq!(Balances::total_balance(&4), 40); + + System::set_block_number(2); + assert_ok!(Staking::unnominate(Origin::signed(4), 0)); + Session::check_rotate_session(System::block_number()); + assert_eq!(Staking::current_era(), 2); + assert_eq!(Session::validators(), vec![3, 2]); + assert_eq!(Balances::total_balance(&1), 12); + assert_eq!(Balances::total_balance(&2), 20); + assert_eq!(Balances::total_balance(&3), 40); + assert_eq!(Balances::total_balance(&4), 48); + + System::set_block_number(3); + assert_ok!(Staking::stake(Origin::signed(4))); + assert_ok!(Staking::unstake(Origin::signed(3), Staking::intentions().iter().position(|&x| x == 3).unwrap() as u32)); + assert_ok!(Staking::nominate(Origin::signed(3), 1.into())); + Session::check_rotate_session(System::block_number()); + assert_eq!(Session::validators(), vec![1, 4]); + assert_eq!(Balances::total_balance(&1), 12); + assert_eq!(Balances::total_balance(&2), 30); + assert_eq!(Balances::total_balance(&3), 50); + assert_eq!(Balances::total_balance(&4), 48); + + System::set_block_number(4); + Session::check_rotate_session(System::block_number()); + assert_eq!(Balances::total_balance(&1), 13); + assert_eq!(Balances::total_balance(&2), 30); + assert_eq!(Balances::total_balance(&3), 58); + assert_eq!(Balances::total_balance(&4), 58); + }); +} + +#[test] +fn rewards_with_off_the_table_should_work() { + with_externalities(&mut new_test_ext(0, 1, 1, 0, true, 10), || { + System::set_block_number(1); + assert_ok!(Staking::stake(Origin::signed(1))); + assert_ok!(Staking::nominate(Origin::signed(2), 1.into())); + assert_ok!(Staking::stake(Origin::signed(3))); + Session::check_rotate_session(System::block_number()); + assert_eq!(Session::validators(), vec![1, 3]); // 1 + 2, 3 + assert_eq!(Balances::total_balance(&1), 10); + assert_eq!(Balances::total_balance(&2), 20); + assert_eq!(Balances::total_balance(&3), 30); + + System::set_block_number(2); + assert_ok!(Staking::register_preferences(Origin::signed(1), Staking::intentions().into_iter().position(|i| i == 1).unwrap() as u32, ValidatorPrefs { unstake_threshold: 3, validator_payment: 4 })); + Session::check_rotate_session(System::block_number()); + assert_eq!(Balances::total_balance(&1), 16); + assert_eq!(Balances::total_balance(&2), 24); + assert_eq!(Balances::total_balance(&3), 40); + }); +} + +#[test] +fn nominating_slashes_should_work() { + with_externalities(&mut new_test_ext(0, 2, 2, 0, true, 10), || { + assert_eq!(Staking::era_length(), 4); + assert_eq!(Staking::validator_count(), 2); + assert_eq!(Staking::bonding_duration(), 12); + assert_eq!(Session::validators(), vec![10, 20]); + + System::set_block_number(2); + Session::check_rotate_session(System::block_number()); + + Timestamp::set_timestamp(15); + System::set_block_number(4); + assert_ok!(Staking::stake(Origin::signed(1))); + assert_ok!(Staking::stake(Origin::signed(3))); + assert_ok!(Staking::nominate(Origin::signed(2), 3.into())); + assert_ok!(Staking::nominate(Origin::signed(4), 1.into())); + Session::check_rotate_session(System::block_number()); + + assert_eq!(Staking::current_era(), 1); + assert_eq!(Session::validators(), vec![1, 3]); // 1 + 4, 3 + 2 + assert_eq!(Balances::total_balance(&1), 10); + assert_eq!(Balances::total_balance(&2), 20); + assert_eq!(Balances::total_balance(&3), 30); + assert_eq!(Balances::total_balance(&4), 40); + + System::set_block_number(5); + ::system::ExtrinsicIndex::::put(1); + Staking::on_offline_validator(0); + Staking::on_offline_validator(1); + assert_eq!(Balances::total_balance(&1), 0); + assert_eq!(Balances::total_balance(&2), 20); + assert_eq!(Balances::total_balance(&3), 10); + assert_eq!(Balances::total_balance(&4), 30); + }); +} + +#[test] +fn double_staking_should_fail() { + with_externalities(&mut new_test_ext(0, 1, 2, 0, true, 0), || { + System::set_block_number(1); + assert_ok!(Staking::stake(Origin::signed(1))); + assert_noop!(Staking::stake(Origin::signed(1)), "Cannot stake if already staked."); + assert_noop!(Staking::nominate(Origin::signed(1), 1.into()), "Cannot nominate if already staked."); + assert_ok!(Staking::nominate(Origin::signed(2), 1.into())); + assert_noop!(Staking::stake(Origin::signed(2)), "Cannot stake if already nominating."); + assert_noop!(Staking::nominate(Origin::signed(2), 1.into()), "Cannot nominate if already nominating."); + }); +} + +#[test] +fn staking_eras_work() { + with_externalities(&mut new_test_ext(0, 1, 2, 0, true, 0), || { + assert_eq!(Staking::era_length(), 2); + assert_eq!(Staking::sessions_per_era(), 2); + assert_eq!(Staking::last_era_length_change(), 0); + assert_eq!(Staking::current_era(), 0); + assert_eq!(Session::current_index(), 0); + + // Block 1: No change. + System::set_block_number(1); + Session::check_rotate_session(System::block_number()); + assert_eq!(Session::current_index(), 1); + assert_eq!(Staking::sessions_per_era(), 2); + assert_eq!(Staking::last_era_length_change(), 0); + assert_eq!(Staking::current_era(), 0); + + // Block 2: Simple era change. + System::set_block_number(2); + Session::check_rotate_session(System::block_number()); + assert_eq!(Session::current_index(), 2); + assert_eq!(Staking::sessions_per_era(), 2); + assert_eq!(Staking::last_era_length_change(), 0); + assert_eq!(Staking::current_era(), 1); + + // Block 3: Schedule an era length change; no visible changes. + System::set_block_number(3); + assert_ok!(Staking::set_sessions_per_era(Origin::ROOT, 3)); + Session::check_rotate_session(System::block_number()); + assert_eq!(Session::current_index(), 3); + assert_eq!(Staking::sessions_per_era(), 2); + assert_eq!(Staking::last_era_length_change(), 0); + assert_eq!(Staking::current_era(), 1); + + // Block 4: Era change kicks in. + System::set_block_number(4); + Session::check_rotate_session(System::block_number()); + assert_eq!(Session::current_index(), 4); + assert_eq!(Staking::sessions_per_era(), 3); + assert_eq!(Staking::last_era_length_change(), 4); + assert_eq!(Staking::current_era(), 2); + + // Block 5: No change. + System::set_block_number(5); + Session::check_rotate_session(System::block_number()); + assert_eq!(Session::current_index(), 5); + assert_eq!(Staking::sessions_per_era(), 3); + assert_eq!(Staking::last_era_length_change(), 4); + assert_eq!(Staking::current_era(), 2); + + // Block 6: No change. + System::set_block_number(6); + Session::check_rotate_session(System::block_number()); + assert_eq!(Session::current_index(), 6); + assert_eq!(Staking::sessions_per_era(), 3); + assert_eq!(Staking::last_era_length_change(), 4); + assert_eq!(Staking::current_era(), 2); + + // Block 7: Era increment. + System::set_block_number(7); + Session::check_rotate_session(System::block_number()); + assert_eq!(Session::current_index(), 7); + assert_eq!(Staking::sessions_per_era(), 3); + assert_eq!(Staking::last_era_length_change(), 4); + assert_eq!(Staking::current_era(), 3); + }); +} + +#[test] +fn staking_balance_transfer_when_bonded_should_not_work() { + with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || { + Balances::set_free_balance(&1, 111); + assert_ok!(Staking::stake(Origin::signed(1))); + assert_noop!(Balances::transfer(Origin::signed(1), 2.into(), 69), "cannot transfer illiquid funds"); + }); +} + +#[test] +fn deducting_balance_when_bonded_should_not_work() { + with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || { + Balances::set_free_balance(&1, 111); + >::insert(1, 2); + System::set_block_number(1); + assert_eq!(Staking::unlock_block(&1), LockStatus::LockedUntil(2)); + assert_noop!(Balances::reserve(&1, 69), "cannot transfer illiquid funds"); + }); +} diff --git a/runtime/support/Cargo.toml b/runtime/support/Cargo.toml new file mode 100644 index 0000000000000..a718a24573f4f --- /dev/null +++ b/runtime/support/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "substrate-runtime-support" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +hex-literal = { version = "0.1.0", optional = true } +serde = { version = "1.0", default_features = false } +serde_derive = { version = "1.0", optional = true } +substrate-runtime-std = { path = "../../core/runtime-std", default_features = false } +substrate-runtime-io = { path = "../../core/runtime-io", default_features = false } +substrate-primitives = { path = "../../core/primitives", default_features = false } +substrate-codec = { path = "../../core/codec", default_features = false } + +[dev-dependencies] +pretty_assertions = "0.5.1" +serde_json = { version = "1.0" } +substrate-codec-derive = { path = "../../core/codec/derive" } + +[features] +default = ["std"] +std = [ + "hex-literal", + "serde/std", + "serde_derive", + "substrate-primitives/std", + "substrate-runtime-io/std", + "substrate-codec/std", + "substrate-runtime-std/std", +] +nightly = [] +strict = [] diff --git a/runtime/support/README.adoc b/runtime/support/README.adoc new file mode 100644 index 0000000000000..699235e5ae650 --- /dev/null +++ b/runtime/support/README.adoc @@ -0,0 +1,14 @@ + += Runtime Support + +.Summary +[source, toml] +---- +include::Cargo.toml[lines=2..5] +---- + +.Description +---- +include::src/lib.rs[tag=description] +---- + diff --git a/runtime/support/src/dispatch.rs b/runtime/support/src/dispatch.rs new file mode 100644 index 0000000000000..90f6fd7a6d4ff --- /dev/null +++ b/runtime/support/src/dispatch.rs @@ -0,0 +1,584 @@ +// Copyright 2017 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 . + +//! Dispatch system. Just dispatches calls. + +pub use rstd::prelude::{Vec, Clone, Eq, PartialEq}; +#[cfg(feature = "std")] +pub use std::fmt; +pub use rstd::result; +#[cfg(feature = "std")] +use serde; +pub use codec::{Codec, Decode, Encode, Input, Output}; + +pub type Result = result::Result<(), &'static str>; + +pub trait Dispatchable { + type Origin; + type Trait; + fn dispatch(self, origin: Self::Origin) -> Result; +} + +#[cfg(feature = "std")] +pub trait Callable { + type Call: Dispatchable + Codec + ::serde::Serialize + Clone + PartialEq + Eq; +} +#[cfg(not(feature = "std"))] +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; + +#[cfg(feature = "std")] +pub trait Parameter: Codec + serde::Serialize + Clone + Eq + fmt::Debug {} + +#[cfg(feature = "std")] +impl Parameter for T where T: Codec + serde::Serialize + Clone + Eq + fmt::Debug {} + +#[cfg(not(feature = "std"))] +pub trait Parameter: Codec + Clone + Eq {} + +#[cfg(not(feature = "std"))] +impl Parameter for T where T: Codec + Clone + Eq {} + +/// Declare a struct for this module, then implement dispatch logic to create a pairing of several +/// dispatch traits and enums. +#[macro_export] +macro_rules! decl_module { + ( + $(#[$attr:meta])* + pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident> + for enum $call_type:ident where origin: $origin_type:ty {$( + $(#[doc = $doc_attr:tt])* + fn $fn_name:ident(origin + $( + , $param_name:ident : $param:ty + )* + ) -> $result:ty; + )*} + ) => { + // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. + #[derive(Clone, Copy, PartialEq, Eq)] + #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] + // TODO: switching based on std feature is because of an issue in + // serde-derive for when we attempt to derive `Deserialize` on these types, + // in a situation where we've imported `substrate_runtime_support` as another name. + #[cfg(feature = "std")] + pub struct $mod_type<$trait_instance: $trait_name>(::std::marker::PhantomData<$trait_instance>); + + // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. + #[derive(Clone, Copy, PartialEq, Eq)] + #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] + #[cfg(not(feature = "std"))] + pub struct $mod_type<$trait_instance: $trait_name>(::core::marker::PhantomData<$trait_instance>); + + #[cfg(feature = "std")] + $(#[$attr])* + #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] + pub enum $call_type<$trait_instance: $trait_name> { + __PhantomItem(::std::marker::PhantomData<$trait_instance>), + __OtherPhantomItem(::std::marker::PhantomData<$trait_instance>), + $( + #[allow(non_camel_case_types)] + $fn_name ( $( $param ),* ), + )* + } + + #[cfg(not(feature = "std"))] + $(#[$attr])* + #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] + pub enum $call_type<$trait_instance: $trait_name> { + __PhantomItem(::core::marker::PhantomData<$trait_instance>), + __OtherPhantomItem(::core::marker::PhantomData<$trait_instance>), + $( + #[allow(non_camel_case_types)] + $fn_name ( $( $param ),* ), + )* + } + + // manual implementation of clone/eq/partialeq because using derive erroneously requires + // clone/eq/partialeq from T. + impl<$trait_instance: $trait_name> $crate::dispatch::Clone + for $call_type<$trait_instance> + { + fn clone(&self) -> Self { + match *self { + $( + $call_type::$fn_name( $( ref $param_name ),* ) => + $call_type::$fn_name( $( $param_name.clone() ),* ) + ,)* + _ => unreachable!(), + } + } + } + impl<$trait_instance: $trait_name> $crate::dispatch::PartialEq + for $call_type<$trait_instance> + { + fn eq(&self, _other: &Self) -> bool { + match *self { + $( + $call_type::$fn_name( $( ref $param_name ),* ) => { + let self_params = ( $( $param_name, )* ); + if let $call_type::$fn_name( $( ref $param_name ),* ) = *_other { + self_params == ( $( $param_name, )* ) + } else { + match *_other { + $call_type::__PhantomItem(_) => unreachable!(), + $call_type::__OtherPhantomItem(_) => unreachable!(), + _ => false, + } + } + } + )* + _ => unreachable!(), + } + } + } + impl<$trait_instance: $trait_name> $crate::dispatch::Eq + for $call_type<$trait_instance> + {} + + #[cfg(feature = "std")] + impl<$trait_instance: $trait_name> $crate::dispatch::fmt::Debug + for $call_type<$trait_instance> + { + 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 ),* ) => + write!(_f, "{}{:?}", + stringify!($fn_name), + ( $( $param_name.clone(), )* ) + ) + ,)* + _ => unreachable!(), + } + } + } + + impl<$trait_instance: $trait_name> $crate::dispatch::Decode for $call_type<$trait_instance> { + fn decode(input: &mut I) -> Option { + let _input_id = input.read_byte()?; + __impl_decode!(input; _input_id; 0; $call_type; $( fn $fn_name( $( $param_name ),* ); )*) + } + } + + impl<$trait_instance: $trait_name> $crate::dispatch::Encode for $call_type<$trait_instance> { + fn encode_to(&self, _dest: &mut W) { + __impl_encode!(_dest; *self; 0; $call_type; $( fn $fn_name( $( $param_name ),* ); )*); + if let $call_type::__PhantomItem(_) = *self { unreachable!() } + if let $call_type::__OtherPhantomItem(_) = *self { unreachable!() } + } + } + impl<$trait_instance: $trait_name> $crate::dispatch::Dispatchable + for $call_type<$trait_instance> + { + type Trait = $trait_instance; + type Origin = $origin_type; + fn dispatch(self, _origin: Self::Origin) -> $crate::dispatch::Result { + match self { + $( + $call_type::$fn_name( $( $param_name ),* ) => + <$mod_type<$trait_instance>>::$fn_name( _origin $(, $param_name )* ), + )* + _ => { panic!("__PhantomItem should never be used.") }, + } + } + } + impl<$trait_instance: $trait_name> $crate::dispatch::Callable + for $mod_type<$trait_instance> + { + type Call = $call_type<$trait_instance>; + } + + impl<$trait_instance: $trait_name> $mod_type<$trait_instance> { + pub fn dispatch>(d: D, origin: D::Origin) -> $crate::dispatch::Result { + d.dispatch(origin) + } + } + + __dispatch_impl_json_metadata! { + $mod_type $trait_instance $trait_name $call_type $origin_type + {$( $(#[doc = $doc_attr])* fn $fn_name(origin $(, $param_name : $param )*) -> $result; )*} + } + } +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __impl_decode { + ( + $input:expr; + $input_id:expr; + $fn_id:expr; + $call_type:ident; + fn $fn_name:ident( + $( $param_name:ident ),* + ); + $($rest:tt)* + ) => { + { + if $input_id == ($fn_id) { + $( + let $param_name = $crate::dispatch::Decode::decode($input)?; + )* + return Some($call_type:: $fn_name( $( $param_name ),* )); + } + + __impl_decode!($input; $input_id; $fn_id + 1; $call_type; $($rest)*) + } + }; + ( + $input:expr; + $input_id:expr; + $fn_id:expr; + $call_type:ident; + ) => { + None + } +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __impl_encode { + ( + $dest:expr; + $self:expr; + $fn_id:expr; + $call_type:ident; + fn $fn_name:ident( + $( $param_name:ident ),* + ); + $($rest:tt)* + ) => { + { + if let $call_type::$fn_name( + $( + ref $param_name + ),* + ) = $self { + $dest.push_byte(($fn_id) as u8); + $( + $param_name.encode_to($dest); + )* + } + + __impl_encode!($dest; $self; $fn_id + 1; $call_type; $($rest)*) + } + }; + ( + $dest:expr; + $self:expr; + $fn_id:expr; + $call_type:ident; + ) => {{}} +} + +pub trait IsSubType { + fn is_aux_sub_type(&self) -> Option<&::Call>; +} + +/// Implement a meta-dispatch module to dispatch to other dispatchers. +#[macro_export] +macro_rules! impl_outer_dispatch { + () => (); + ( + $(#[$attr:meta])* + pub enum $call_type:ident where origin: $origin:ty { + $( + $camelcase:ident, + )* + } + $( $rest:tt )* + ) => { + $(#[$attr])* + #[derive(Clone, PartialEq, Eq)] + #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] + pub enum $call_type { + $( + $camelcase ( $crate::dispatch::CallableCallFor<$camelcase> ) + ,)* + } + __impl_outer_dispatch_common! { $call_type, $($camelcase,)* } + impl $crate::dispatch::Dispatchable for $call_type { + type Origin = $origin; + type Trait = $call_type; + fn dispatch(self, origin: $origin) -> $crate::dispatch::Result { + match self { + $( + $call_type::$camelcase(call) => call.dispatch(origin), + )* + } + } + } + $( + 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_outer_dispatch!{ $($rest)* } + } +} + +/// Implement a meta-dispatch module to dispatch to other dispatchers. +#[macro_export] +#[doc(hidden)] +macro_rules! __impl_outer_dispatch_common { + ( + $call_type:ident, $( $camelcase:ident, )* + ) => { + impl $crate::dispatch::Decode for $call_type { + fn decode(input: &mut I) -> Option { + let input_id = input.read_byte()?; + __impl_decode!(input; input_id; 0; $call_type; $( fn $camelcase ( outer_dispatch_param ); )*) + } + } + + impl $crate::dispatch::Encode for $call_type { + fn encode_to(&self, dest: &mut W) { + __impl_encode!(dest; *self; 0; $call_type; $( fn $camelcase( outer_dispatch_param ); )*) + } + } + + } +} + +/// Implement the `json_metadata` function. +#[macro_export] +#[doc(hidden)] +macro_rules! __dispatch_impl_json_metadata { + ( + $mod_type:ident $trait_instance:ident $trait_name:ident + $($rest:tt)* + ) => { + impl<$trait_instance: $trait_name> $mod_type<$trait_instance> { + pub fn json_metadata() -> &'static str { + concat!(r#"{ "name": ""#, stringify!($mod_type), r#"", "call": "#, + __call_to_json!($($rest)*), " }") + } + } + } +} + +/// Convert the list of calls into their JSON representation, joined by ",". +#[macro_export] +#[doc(hidden)] +macro_rules! __call_to_json { + // WITH AUX + ( + $call_type:ident $origin_type:ty + {$( + $(#[doc = $doc_attr:tt])* + fn $fn_name:ident(origin + $( + , $param_name:ident : $param:ty + )* + ) -> $result:ty; + )*} + ) => { + concat!( + r#"{ "name": ""#, stringify!($call_type), + r#"", "functions": {"#, + __functions_to_json!(""; 0; $origin_type; $( + fn $fn_name(origin $(, $param_name: $param )* ) -> $result; + __function_doc_to_json!(""; $($doc_attr)*); + )*), " } }" + ) + }; +} + +/// Convert a list of functions into their JSON representation, joined by ",". +#[macro_export] +#[doc(hidden)] +macro_rules! __functions_to_json { + // WITHOUT AUX + ( + $prefix_str:tt; + $fn_id:expr; + fn $fn_name:ident( + $($param_name:ident : $param:ty),* + ) -> $result:ty; + $fn_doc:expr; + $($rest:tt)* + ) => { + concat!($prefix_str, " ", + __function_to_json!( + fn $fn_name( + $($param_name : $param),* + ) -> $result; + $fn_doc; + $fn_id; + ), __functions_to_json!(","; $fn_id + 1; $($rest)*) + ) + }; + // WITH AUX + ( + $prefix_str:tt; + $fn_id:expr; + $origin_type:ty; + fn $fn_name:ident(origin + $( + , $param_name:ident : $param:ty + )* + ) -> $result:ty; + $fn_doc:expr; + $($rest:tt)* + ) => { + concat!($prefix_str, " ", + __function_to_json!( + fn $fn_name( + origin: $origin_type + $(, $param_name : $param)* + ) -> $result; + $fn_doc; + $fn_id; + ), __functions_to_json!(","; $fn_id + 1; $origin_type; $($rest)*) + ) + }; + // BASE CASE + ( + $prefix_str:tt; + $fn_id:expr; + $($origin_type:ty;)* + ) => { + "" + } +} + +/// Convert a function into its JSON representation. +#[macro_export] +#[doc(hidden)] +macro_rules! __function_to_json { + ( + fn $fn_name:ident( + $first_param_name:ident : $first_param:ty $(, $param_name:ident : $param:ty)* + ) -> $result:ty; + $fn_doc:tt; + $fn_id:expr; + ) => { + concat!( + r#"""#, stringify!($fn_id), r#"""#, + r#": { "name": ""#, stringify!($fn_name), + r#"", "params": [ "#, + concat!(r#"{ "name": ""#, stringify!($first_param_name), r#"", "type": ""#, stringify!($first_param), r#"" }"# ), + $( + concat!(r#", { "name": ""#, stringify!($param_name), r#"", "type": ""#, stringify!($param), r#"" }"# ), + )* + r#" ], "description": ["#, $fn_doc, " ] }" + ) + }; +} + +/// Convert a function documentation attribute into its JSON representation. +#[macro_export] +#[doc(hidden)] +macro_rules! __function_doc_to_json { + ( + $prefix_str:tt; + $doc_attr:tt + $($rest:tt)* + ) => { + concat!( + $prefix_str, r#" ""#, + $doc_attr, + r#"""#, + __function_doc_to_json!(","; $($rest)*) + ) + }; + ( + $prefix_str:tt; + ) => { + "" + } +} + +#[cfg(test)] +// Do not complain about unused `dispatch` and `dispatch_aux`. +#[allow(dead_code)] +mod tests { + use super::*; + use serde; + use serde_json; + + pub trait Trait { + type Origin; + } + + decl_module! { + pub struct Module for enum Call where origin: T::Origin { + /// Hi, this is a comment. + fn aux_0(origin) -> Result; + fn aux_1(origin, data: i32) -> Result; + fn aux_2(origin, data: i32, data2: String) -> Result; + } + } + + const EXPECTED_METADATA: &str = concat!( + r#"{ "name": "Module", "call": "#, + r#"{ "name": "Call", "functions": { "#, + r#""0": { "name": "aux_0", "params": [ "#, + r#"{ "name": "origin", "type": "T::Origin" }"#, + r#" ], "description": [ " Hi, this is a comment." ] }, "#, + r#""0 + 1": { "name": "aux_1", "params": [ "#, + r#"{ "name": "origin", "type": "T::Origin" }, "#, + r#"{ "name": "data", "type": "i32" }"#, + r#" ], "description": [ ] }, "#, + r#""0 + 1 + 1": { "name": "aux_2", "params": [ "#, + r#"{ "name": "origin", "type": "T::Origin" }, "#, + r#"{ "name": "data", "type": "i32" }, "#, + r#"{ "name": "data2", "type": "String" }"#, + r#" ], "description": [ ] }"#, + r#" } }"#, + r#" }"#, + ); + + impl Module { + fn aux_0(_: T::Origin) -> Result { + unreachable!() + } + + fn aux_1(_: T::Origin, _: i32) -> Result { + unreachable!() + } + + fn aux_2(_: T::Origin, _: i32, _: String) -> Result { + unreachable!() + } + } + + struct TraitImpl {} + + impl Trait for TraitImpl { + type Origin = u32; + } + + #[test] + fn module_json_metadata() { + let metadata = Module::::json_metadata(); + assert_eq!(EXPECTED_METADATA, metadata); + let _: serde::de::IgnoredAny = + serde_json::from_str(metadata).expect("Is valid json syntax"); + } +} diff --git a/runtime/support/src/event.rs b/runtime/support/src/event.rs new file mode 100644 index 0000000000000..a9767f51bf74e --- /dev/null +++ b/runtime/support/src/event.rs @@ -0,0 +1,298 @@ +// Copyright 2018 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 . + +/// Implement an `Event`/`RawEvent` for a module. +#[macro_export] +macro_rules! decl_event { + ( + $(#[$attr:meta])* + pub enum Event<$( $evt_generic_param:ident )*> with RawEvent<$( $generic_param:ident ),*> + where $( <$generic:ident as $trait:path>::$trait_type:ident),* { + $( + $(#[doc = $doc_attr:tt])* + $event:ident( $( $param:path ),* ), + )* + } + ) => { + pub type Event<$( $evt_generic_param )*> = RawEvent<$( <$generic as $trait>::$trait_type ),*>; + // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. + #[derive(Clone, PartialEq, Eq, Encode, Decode)] + #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] + $(#[$attr])* + pub enum RawEvent<$( $generic_param ),*> { + $( + $( #[doc = $doc_attr] )* + $event($( $param ),*), + )* + } + impl<$( $generic_param ),*> From> for () { + fn from(_: RawEvent<$( $generic_param ),*>) -> () { () } + } + impl<$( $generic_param ),*> RawEvent<$( $generic_param ),*> { + #[allow(dead_code)] + pub fn event_json_metadata() -> &'static str { + concat!( + "{", + __impl_event_json_metadata!(""; + $( + $event ( $( $param ),* ); + __function_doc_to_json!(""; $($doc_attr)*); + )* + ), + " }" + ) + } + } + }; + ( + $(#[$attr:meta])* + pub enum Event { + $( + $(#[doc = $doc_attr:tt])* + $event:ident, + )* + } + ) => { + // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. + #[derive(Clone, PartialEq, Eq, Encode, Decode)] + #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] + $(#[$attr])* + pub enum Event { + $( + $( #[doc = $doc_attr] )* + $event, + )* + } + impl From for () { + fn from(_: Event) -> () { () } + } + impl Event { + #[allow(dead_code)] + pub fn event_json_metadata() -> &'static str { + concat!( + "{", + __impl_event_json_metadata!(""; + $( + $event; + __function_doc_to_json!(""; $($doc_attr)*); + )* + ), + " }" + ) + } + } + } +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __impl_event_json_metadata { + ( + $prefix_str:expr; + $event:ident( $first_param:path $(, $param:path )* ); + $event_doc:expr; + $( $rest:tt )* + ) => { + concat!($prefix_str, " ", "\"", stringify!($event), r#"": { "params": [ ""#, + stringify!($first_param), "\"" + $(, concat!(", \"", stringify!($param), "\"") )*, r#" ], "description": ["#, + $event_doc, " ] }", + __impl_event_json_metadata!(","; $( $rest )*) + ) + }; + ( + $prefix_str:expr; + $event:ident; + $event_doc:expr; + $( $rest:tt )* + ) => { + concat!($prefix_str, " ", "\"", stringify!($event), + r#"": { "params": null, "description": ["#, $event_doc, " ] }", + __impl_event_json_metadata!(","; $( $rest )*) + ) + }; + ( + $prefix_str:expr; + ) => { + "" + } +} + +#[macro_export] +macro_rules! impl_outer_event { + ($(#[$attr:meta])* pub enum $name:ident for $runtime:ident { $( $module:ident ),* }) => { + // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. + #[derive(Clone, PartialEq, Eq, Encode, Decode)] + #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] + $(#[$attr])* + #[allow(non_camel_case_types)] + pub enum $name { + system(system::Event), + $( + $module($module::Event<$runtime>), + )* + } + impl From for $name { + fn from(x: system::Event) -> Self { + $name::system(x) + } + } + $( + impl From<$module::Event<$runtime>> for $name { + fn from(x: $module::Event<$runtime>) -> Self { + $name::$module(x) + } + } + )* + __impl_outer_event_json_metadata!($runtime; $name; $( $module )*); + } +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __impl_outer_event_json_metadata { + ( + $runtime:ident; + $event_name:ident; + $( $module:ident )* + ) => { + impl $runtime { + #[allow(dead_code)] + pub fn outer_event_json_metadata() -> (&'static str, &'static [(&'static str, fn() -> &'static str)]) { + static METADATA: &[(&str, fn() -> &'static str)] = &[ + ("system", system::Event::event_json_metadata) + $( + , ( + stringify!($module), + $module::Event::<$runtime>::event_json_metadata + ) + )* + ]; + ( + stringify!($event_name), + METADATA + ) + } + } + } +} + +#[cfg(test)] +#[allow(dead_code)] +mod tests { + use serde; + use serde_json; + + mod system { + pub trait Trait { + type Origin; + } + + decl_module! { + pub struct Module for enum Call where origin: T::Origin {} + } + + decl_event!( + pub enum Event { + SystemEvent, + } + ); + } + + mod event_module { + pub trait Trait { + type Origin; + type Balance; + } + + decl_module! { + pub struct Module for enum Call where origin: T::Origin {} + } + + decl_event!( + pub enum Event with RawEvent + where ::Balance + { + /// Hi, I am a comment. + TestEvent(Balance), + } + ); + } + + mod event_module2 { + pub trait Trait { + type Origin; + type Balance; + } + + decl_module! { + pub struct Module for enum Call where origin: T::Origin {} + } + + decl_event!( + pub enum Event with RawEvent + where ::Balance + { + TestEvent(Balance), + } + ); + } + + #[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, Deserialize, Serialize)] + pub struct TestRuntime; + + impl_outer_event! { + pub enum TestEvent for TestRuntime { + event_module, event_module2 + } + } + + impl event_module::Trait for TestRuntime { + type Origin = u32; + type Balance = u32; + } + + impl event_module2::Trait for TestRuntime { + type Origin = u32; + type Balance = u32; + } + + impl system::Trait for TestRuntime { + type Origin = u32; + } + + const EXPECTED_METADATA: (&str, &[(&str, &str)]) = ( + "TestEvent", &[ + ("system", r#"{ "SystemEvent": { "params": null, "description": [ ] } }"#), + ("event_module", r#"{ "TestEvent": { "params": [ "Balance" ], "description": [ " Hi, I am a comment." ] } }"#), + ("event_module2", r#"{ "TestEvent": { "params": [ "Balance" ], "description": [ ] } }"#), + ] + ); + + #[test] + fn outer_event_json_metadata() { + let metadata = TestRuntime::outer_event_json_metadata(); + assert_eq!(EXPECTED_METADATA.0, metadata.0); + assert_eq!(EXPECTED_METADATA.1.len(), metadata.1.len()); + + for (expected, got) in EXPECTED_METADATA.1.iter().zip(metadata.1.iter()) { + assert_eq!(expected.0, got.0); + assert_eq!(expected.1, got.1()); + let _: serde::de::IgnoredAny = + serde_json::from_str(got.1()).expect(&format!("Is valid json syntax: {}", got.1())); + } + } +} diff --git a/runtime/support/src/hashable.rs b/runtime/support/src/hashable.rs new file mode 100644 index 0000000000000..f069e3666179c --- /dev/null +++ b/runtime/support/src/hashable.rs @@ -0,0 +1,38 @@ +// Copyright 2017 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 . + +//! Hashable trait. + +use codec::Codec; +use runtime_io::{blake2_256, twox_128, twox_256}; + +pub trait Hashable: Sized { + fn blake2_256(&self) -> [u8; 32]; + fn twox_128(&self) -> [u8; 16]; + fn twox_256(&self) -> [u8; 32]; +} + +impl Hashable for T { + fn blake2_256(&self) -> [u8; 32] { + blake2_256(&self.encode()) + } + fn twox_128(&self) -> [u8; 16] { + twox_128(&self.encode()) + } + fn twox_256(&self) -> [u8; 32] { + twox_256(&self.encode()) + } +} diff --git a/runtime/support/src/lib.rs b/runtime/support/src/lib.rs new file mode 100644 index 0000000000000..73099360dad47 --- /dev/null +++ b/runtime/support/src/lib.rs @@ -0,0 +1,191 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +// tag::description[] +//! Support code for the runtime. +// end::description[] + +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(not(feature = "std"), feature(alloc))] + +#[cfg(not(feature = "std"))] +extern crate alloc; + +#[cfg(feature = "std")] +extern crate serde; + +extern crate substrate_runtime_std as rstd; +extern crate substrate_runtime_io as runtime_io; +extern crate substrate_primitives as primitives; + +#[cfg(test)] +#[macro_use] +extern crate pretty_assertions; +#[cfg(test)] +#[macro_use] +extern crate serde_derive; +#[cfg(test)] +extern crate serde_json; +#[cfg(test)] +#[macro_use] +extern crate substrate_codec_derive; + +#[doc(hidden)] +pub extern crate substrate_codec as codec; +pub use self::storage::generator::Storage as GenericStorage; + +#[cfg(feature = "std")] +pub mod alloc { + pub use std::boxed; + pub use std::vec; +} + +#[macro_use] +pub mod dispatch; +#[macro_use] +pub mod storage; +mod hashable; +#[macro_use] +mod event; +#[macro_use] +pub mod metadata; + +pub use self::storage::{StorageVec, StorageList, StorageValue, StorageMap}; +pub use self::hashable::Hashable; +pub use self::dispatch::{Parameter, Dispatchable, Callable, IsSubType}; +pub use runtime_io::print; + + +#[macro_export] +macro_rules! fail { + ( $y:expr ) => {{ + return Err($y); + }} +} + +#[macro_export] +macro_rules! ensure { + ( $x:expr, $y:expr ) => {{ + if !$x { + fail!($y); + } + }} +} + +#[macro_export] +#[cfg(feature = "std")] +macro_rules! assert_noop { + ( $x:expr , $y:expr ) => { + let h = runtime_io::storage_root(); + assert_err!($x, $y); + assert_eq!(h, runtime_io::storage_root()); + } +} + +#[macro_export] +#[cfg(feature = "std")] +macro_rules! assert_err { + ( $x:expr , $y:expr ) => { + assert_eq!($x, Err($y)); + } +} + +#[macro_export] +#[cfg(feature = "std")] +macro_rules! assert_ok { + ( $x:expr ) => { + assert_eq!($x, Ok(())); + }; + ( $x:expr, $y:expr ) => { + assert_eq!($x, Ok($y)); + } +} + +/// The void type - it cannot exist. +// Oh rust, you crack me up... +#[derive(Clone, Eq, PartialEq)] +#[cfg_attr(feature = "std", derive(Debug))] +pub enum Void {} + +#[macro_export] +macro_rules! impl_outer_origin { + ($(#[$attr:meta])* pub enum $name:ident for $trait:ident where system = $system:ident { $( $module: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<$trait>), + $( + $module($module::Origin), + )* + #[allow(dead_code)] + Void($crate::Void) + } + #[allow(dead_code)] + impl $name { + pub const INHERENT: Self = $name::system($system::RawOrigin::Inherent); + pub const ROOT: Self = $name::system($system::RawOrigin::Root); + pub fn signed(by: <$trait as $system::Trait>::AccountId) -> Self { + $name::system($system::RawOrigin::Signed(by)) + } + } + impl From<$system::Origin<$trait>> for $name { + fn from(x: $system::Origin<$trait>) -> Self { + $name::system(x) + } + } + impl Into>> for $name { + fn into(self) -> Option<$system::Origin<$trait>> { + if let $name::system(l) = self { + Some(l) + } else { + None + } + } + } + impl From::AccountId>> for $name { + fn from(x: Option<<$trait as $system::Trait>::AccountId>) -> Self { + <$system::Origin<$trait>>::from(x).into() + } + } + $( + impl From<$module::Origin> for $name { + fn from(x: $module::Origin) -> Self { + $name::$module(x) + } + } + impl Into> for $name { + fn into(self) -> Option<$module::Origin> { + if let $name::$module(l) = self { + Some(l) + } else { + None + } + } + } + )* + }; + ($(#[$attr:meta])* pub enum $name:ident for $trait:ident { $( $module:ident ),* }) => { + impl_outer_origin! { + $(#[$attr])* + pub enum $name for $trait where system = system { + $( $module ),* + } + } + } +} diff --git a/runtime/support/src/metadata.rs b/runtime/support/src/metadata.rs new file mode 100644 index 0000000000000..12953871d324a --- /dev/null +++ b/runtime/support/src/metadata.rs @@ -0,0 +1,459 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +use codec::{Encode, Output}; +#[cfg(feature = "std")] +use codec::{Decode, Input}; +use alloc; + +/// Make Box available on `std` and `no_std`. +pub type Box = alloc::boxed::Box; +/// Make Vec available on `std` and `no_std`. +pub type Vec = alloc::vec::Vec; + +/// Implements the json metadata support for the given runtime and all its modules. +/// +/// Example: +/// ```compile_fail +/// impl_json_metadata!(for RUNTIME_NAME with modules MODULE0, MODULE2, MODULE3 with Storage); +/// ``` +/// +/// In this example, just `MODULE3` implements the `Storage` trait. +#[macro_export] +macro_rules! impl_json_metadata { + ( + for $runtime:ident with modules + $( $rest:tt )* + ) => { + impl $runtime { + pub fn json_metadata() -> $crate::metadata::Vec<$crate::metadata::JSONMetadata> { + let events = Self::outer_event_json_metadata(); + __impl_json_metadata!($runtime; + $crate::metadata::JSONMetadata::Events { + name: events.0, + events: events.1, + }; + $( $rest )* + ) + } + } + } +} + +/// The metadata of a runtime encoded as JSON. +#[derive(Eq)] +#[cfg_attr(feature = "std", derive(Debug))] +pub enum JSONMetadata { + Events { name: &'static str, events: &'static [(&'static str, fn() -> &'static str)] }, + Module { module: &'static str, prefix: &'static str }, + ModuleWithStorage { module: &'static str, prefix: &'static str, storage: &'static str } +} + +impl Encode for JSONMetadata { + fn encode_to(&self, dest: &mut W) { + match self { + JSONMetadata::Events { name, events } => { + 0i8.encode_to(dest); + name.encode_to(dest); + events.iter().fold(0u32, |count, _| count + 1).encode_to(dest); + events + .iter() + .map(|(module, data)| (module, data())) + .for_each(|val| val.encode_to(dest)); + }, + JSONMetadata::Module { module, prefix } => { + 1i8.encode_to(dest); + prefix.encode_to(dest); + module.encode_to(dest); + }, + JSONMetadata::ModuleWithStorage { module, prefix, storage } => { + 2i8.encode_to(dest); + prefix.encode_to(dest); + module.encode_to(dest); + storage.encode_to(dest); + } + } + } +} + +impl PartialEq for JSONMetadata { + fn eq(&self, other: &JSONMetadata) -> bool { + match (self, other) { + ( + JSONMetadata::Events { name: lname, events: left }, + JSONMetadata::Events { name: rname, events: right } + ) => { + lname == rname && left.iter().zip(right.iter()).fold(true, |res, (l, r)| { + res && l.0 == r.0 && l.1() == r.1() + }) + }, + ( + JSONMetadata::Module { prefix: lpre, module: lmod }, + JSONMetadata::Module { prefix: rpre, module: rmod } + ) => { + lpre == rpre && lmod == rmod + }, + ( + JSONMetadata::ModuleWithStorage { prefix: lpre, module: lmod, storage: lstore }, + JSONMetadata::ModuleWithStorage { prefix: rpre, module: rmod, storage: rstore } + ) => { + lpre == rpre && lmod == rmod && lstore == rstore + }, + _ => false, + } + } +} + +/// Utility struct for making `JSONMetadata` decodeable. +#[derive(Eq, PartialEq, Debug)] +#[cfg(feature = "std")] +pub enum JSONMetadataDecodable { + Events { name: String, events: Vec<(String, String)> }, + Module { module: String, prefix: String }, + ModuleWithStorage { module: String, prefix: String, storage: String } +} + +#[cfg(feature = "std")] +impl JSONMetadataDecodable { + /// Returns the instance as JSON string. + /// The first value of the tuple is the name of the metadata type and the second in the JSON string. + pub fn into_json_string(self) -> (&'static str, String) { + match self { + JSONMetadataDecodable::Events { name, events } => { + ( + "events", + format!( + r#"{{ "name": "{}", "events": {{ {} }} }}"#, name, + events.iter().enumerate() + .fold(String::from(""), |mut json, (i, (name, data))| { + if i > 0 { + json.push_str(", "); + } + json.push_str(&format!(r#""{}": {}"#, name, data)); + json + }) + ) + ) + }, + JSONMetadataDecodable::Module { prefix, module } => { + ("module", format!(r#"{{ "prefix": "{}", "module": {} }}"#, prefix, module)) + }, + JSONMetadataDecodable::ModuleWithStorage { prefix, module, storage } => { + ( + "moduleWithStorage", + format!( + r#"{{ "prefix": "{}", "module": {}, "storage": {} }}"#, + prefix, module, storage + ) + ) + } + } + } +} + +#[cfg(feature = "std")] +impl Decode for JSONMetadataDecodable { + fn decode(input: &mut I) -> Option { + i8::decode(input).and_then(|variant| { + match variant { + 0 => String::decode(input) + .and_then(|name| Vec::<(String, String)>::decode(input).map(|events| (name, events))) + .and_then(|(name, events)| Some(JSONMetadataDecodable::Events { name, events })), + 1 => String::decode(input) + .and_then(|prefix| String::decode(input).map(|v| (prefix, v))) + .and_then(|(prefix, module)| Some(JSONMetadataDecodable::Module { prefix, module })), + 2 => String::decode(input) + .and_then(|prefix| String::decode(input).map(|v| (prefix, v))) + .and_then(|(prefix, module)| String::decode(input).map(|v| (prefix, module, v))) + .and_then(|(prefix, module, storage)| Some(JSONMetadataDecodable::ModuleWithStorage { prefix, module, storage })), + _ => None, + } + }) + } +} + +#[cfg(test)] +impl PartialEq for JSONMetadataDecodable { + fn eq(&self, other: &JSONMetadata) -> bool { + match (self, other) { + ( + JSONMetadataDecodable::Events { name: lname, events: left }, + JSONMetadata::Events { name: rname, events: right } + ) => { + lname == rname && left.iter().zip(right.iter()).fold(true, |res, (l, r)| { + res && l.0 == r.0 && l.1 == r.1() + }) + }, + ( + JSONMetadataDecodable::Module { prefix: lpre, module: lmod }, + JSONMetadata::Module { prefix: rpre, module: rmod } + ) => { + lpre == rpre && lmod == rmod + }, + ( + JSONMetadataDecodable::ModuleWithStorage { prefix: lpre, module: lmod, storage: lstore }, + JSONMetadata::ModuleWithStorage { prefix: rpre, module: rmod, storage: rstore } + ) => { + lpre == rpre && lmod == rmod && lstore == rstore + }, + _ => false, + } + } +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __impl_json_metadata { + ( + $runtime: ident; + $( $metadata:expr ),*; + $mod:ident::$module:ident, + $( $rest:tt )* + ) => { + __impl_json_metadata!( + $runtime; + $( $metadata, )* $crate::metadata::JSONMetadata::Module { + module: $mod::$module::<$runtime>::json_metadata(), prefix: stringify!($mod) + }; + $( $rest )* + ) + }; + ( + $runtime: ident; + $( $metadata:expr ),*; + $mod:ident::$module:ident + ) => { + __impl_json_metadata!( + $runtime; + $( $metadata, )* $crate::metadata::JSONMetadata::Module { + module: $mod::$module::<$runtime>::json_metadata(), prefix: stringify!($mod) + }; + ) + }; + ( + $runtime: ident; + $( $metadata:expr ),*; + $mod:ident::$module:ident with Storage, + $( $rest:tt )* + ) => { + __impl_json_metadata!( + $runtime; + $( $metadata, )* $crate::metadata::JSONMetadata::ModuleWithStorage { + module: $mod::$module::<$runtime>::json_metadata(), prefix: stringify!($mod), + storage: $mod::$module::<$runtime>::store_json_metadata() + }; + $( $rest )* + ) + }; + ( + $runtime: ident; + $( $metadata:expr ),*; + $mod:ident::$module:ident with Storage + ) => { + __impl_json_metadata!( + $runtime; + $( $metadata, )* $crate::metadata::JSONMetadata::ModuleWithStorage { + module: $mod::$module::<$runtime>::json_metadata(), prefix: stringify!($mod), + storage: $mod::$module::<$runtime>::store_json_metadata() + }; + ) + }; + ( + $runtime:ident; + $( $metadata:expr ),*; + ) => { + <[_]>::into_vec($crate::metadata::Box::new([ $( $metadata ),* ])) + }; +} + +#[cfg(test)] +// Do not complain about unused `dispatch` and `dispatch_aux`. +#[allow(dead_code)] +mod tests { + use super::*; + use serde; + use serde_json; + + mod system { + pub trait Trait { + type Origin; + } + + decl_module! { + pub struct Module for enum Call where origin: T::Origin {} + } + + decl_event!( + pub enum Event { + SystemEvent, + } + ); + } + + mod event_module { + use dispatch::Result; + + pub trait Trait { + type Origin; + type Balance; + } + + decl_event!( + pub enum Event with RawEvent + where ::Balance + { + /// Hi, I am a comment. + TestEvent(Balance), + } + ); + + decl_module! { + pub struct Module for enum Call where origin: T::Origin { + fn aux_0(origin) -> Result; + } + } + + impl Module { + fn aux_0(_: T::Origin) -> Result { + unreachable!() + } + } + } + + mod event_module2 { + pub trait Trait { + type Origin; + type Balance; + } + + decl_event!( + pub enum Event with RawEvent + where ::Balance + { + TestEvent(Balance), + } + ); + + decl_module! { + pub struct ModuleWithStorage for enum Call where origin: T::Origin {} + } + + decl_storage! { + trait Store for ModuleWithStorage as TestStorage { + StorageMethod : u32; + } + } + } + + #[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, Deserialize, Serialize)] + pub struct TestRuntime; + + impl_outer_event! { + pub enum TestEvent for TestRuntime { + event_module, event_module2 + } + } + + impl event_module::Trait for TestRuntime { + type Origin = u32; + type Balance = u32; + } + + impl event_module2::Trait for TestRuntime { + type Origin = u32; + type Balance = u32; + } + + impl system::Trait for TestRuntime { + type Origin = u32; + } + + impl_json_metadata!( + for TestRuntime with modules + event_module::Module, + event_module2::ModuleWithStorage with Storage + ); + + fn system_event_json() -> &'static str { + r#"{ "SystemEvent": { "params": null, "description": [ ] } }"# + } + + fn event_module_event_json() -> &'static str { + r#"{ "TestEvent": { "params": [ "Balance" ], "description": [ " Hi, I am a comment." ] } }"# + } + + fn event_module2_event_json() -> &'static str { + r#"{ "TestEvent": { "params": [ "Balance" ], "description": [ ] } }"# + } + + const EXPECTED_METADATA: &[JSONMetadata] = &[ + JSONMetadata::Events { + name: "TestEvent", + events: &[ + ("system", system_event_json), + ("event_module", event_module_event_json), + ("event_module2", event_module2_event_json), + ] + }, + JSONMetadata::Module { + module: concat!( + r#"{ "name": "Module", "call": "#, + r#"{ "name": "Call", "functions": "#, + r#"{ "0": { "name": "aux_0", "params": [ "#, + r#"{ "name": "origin", "type": "T::Origin" } ], "#, + r#""description": [ ] } } } }"# + ), + prefix: "event_module" + }, + JSONMetadata::ModuleWithStorage { + module: r#"{ "name": "ModuleWithStorage", "call": { "name": "Call", "functions": { } } }"#, + prefix: "event_module2", + storage: concat!( + r#"{ "prefix": "TestStorage", "items": { "#, + r#""StorageMethod": { "description": [ ], "modifier": null, "type": "u32" }"#, + r#" } }"# + ) + } + ]; + + #[test] + fn runtime_json_metadata() { + let metadata = TestRuntime::json_metadata(); + assert_eq!(EXPECTED_METADATA, &metadata[..]); + } + + #[test] + fn json_metadata_encode_and_decode() { + let metadata = TestRuntime::json_metadata(); + let metadata_encoded = metadata.encode(); + let metadata_decoded = Vec::::decode(&mut &metadata_encoded[..]); + + assert_eq!(&metadata_decoded.unwrap()[..], &metadata[..]); + } + + #[test] + fn into_json_string_is_valid_json() { + let metadata = TestRuntime::json_metadata(); + let metadata_encoded = metadata.encode(); + let metadata_decoded = Vec::::decode(&mut &metadata_encoded[..]); + + for mdata in metadata_decoded.unwrap().into_iter() { + let json = mdata.into_json_string(); + let _: serde::de::IgnoredAny = + serde_json::from_str(&json.1).expect(&format!("Is valid json syntax: {}", json.1)); + } + } +} diff --git a/runtime/support/src/storage/generator.rs b/runtime/support/src/storage/generator.rs new file mode 100644 index 0000000000000..7c820cc5ccecf --- /dev/null +++ b/runtime/support/src/storage/generator.rs @@ -0,0 +1,1659 @@ +// Copyright 2017 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 . + +//! Strongly typed wrappers around values in storage. +//! +//! This crate exports a macro `storage_items!` and traits describing behavior of generated +//! structs. +//! +//! Three kinds of data types are currently supported: +//! - values +//! - maps +//! - lists +//! +//! # Examples: +//! +//! ```rust +//! #[macro_use] +//! extern crate substrate_runtime_support; +//! +//! type AuthorityId = [u8; 32]; +//! type Balance = u64; +//! pub type SessionKey = [u8; 32]; +//! +//! storage_items! { +//! // public value +//! pub Value: b"putd_key" => SessionKey; +//! // private map. +//! Balances: b"private_map:" => map [AuthorityId => Balance]; +//! // private list. +//! Authorities: b"auth:" => list [AuthorityId]; +//! } +//! +//!# fn main() { } +//! ``` + +use codec; +use rstd::vec::Vec; +#[doc(hidden)] +pub use rstd::borrow::Borrow; +#[doc(hidden)] +pub use rstd::marker::PhantomData; + +/// Abstraction around storage. +pub trait Storage { + /// true if the key exists in storage. + fn exists(&self, key: &[u8]) -> bool; + + /// Load the bytes of a key from storage. Can panic if the type is incorrect. + fn get(&self, key: &[u8]) -> Option; + + /// Load the bytes of a key from storage. Can panic if the type is incorrect. Will panic if + /// it's not there. + fn require(&self, key: &[u8]) -> T { self.get(key).expect("Required values must be in storage") } + + /// Load the bytes of a key from storage. Can panic if the type is incorrect. The type's + /// default is returned if it's not there. + fn get_or_default(&self, key: &[u8]) -> T { self.get(key).unwrap_or_default() } + + /// Put a value in under a key. + fn put(&self, key: &[u8], val: &T); + + /// Remove the bytes of a key from storage. + fn kill(&self, key: &[u8]); + + /// Take a value from storage, deleting it after reading. + fn take(&self, key: &[u8]) -> Option { + let value = self.get(key); + self.kill(key); + value + } + + /// Take a value from storage, deleting it after reading. + fn take_or_panic(&self, key: &[u8]) -> T { self.take(key).expect("Required values must be in storage") } + + /// Take a value from storage, deleting it after reading. + fn take_or_default(&self, key: &[u8]) -> T { self.take(key).unwrap_or_default() } +} + +/// A strongly-typed value kept in storage. +pub trait StorageValue { + /// The type that get/take returns. + type Query; + + /// Get the storage key. + fn key() -> &'static [u8]; + + /// true if the value is defined in storage. + fn exists(storage: &S) -> bool { + storage.exists(Self::key()) + } + + /// Load the value from the provided storage instance. + fn get(storage: &S) -> Self::Query; + + /// Take a value from storage, removing it afterwards. + fn take(storage: &S) -> Self::Query; + + /// Store a value under this key into the provided storage instance. + fn put(val: &T, storage: &S) { + storage.put(Self::key(), val) + } + + /// Mutate this value + fn mutate(f: F, storage: &S); + + /// Clear the storage value. + fn kill(storage: &S) { + storage.kill(Self::key()) + } +} + +/// A strongly-typed list in storage. +pub trait StorageList { + /// Get the prefix key in storage. + fn prefix() -> &'static [u8]; + + /// Get the key used to put the length field. + fn len_key() -> Vec; + + /// Get the storage key used to fetch a value at a given index. + fn key_for(index: u32) -> Vec; + + /// Read out all the items. + fn items(storage: &S) -> Vec; + + /// Set the current set of items. + fn set_items(items: &[T], storage: &S); + + /// Set the item at the given index. + fn set_item(index: u32, item: &T, storage: &S); + + /// Load the value at given index. Returns `None` if the index is out-of-bounds. + fn get(index: u32, storage: &S) -> Option; + + /// Load the length of the list + fn len(storage: &S) -> u32; + + /// Clear the list. + fn clear(storage: &S); +} + +/// A strongly-typed map in storage. +pub trait StorageMap { + /// 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(x: &K) -> Vec; + + /// true if the value is defined in storage. + fn exists(key: &K, storage: &S) -> bool { + storage.exists(&Self::key_for(key)[..]) + } + + /// Load the value associated with the given key from the map. + fn get(key: &K, storage: &S) -> Self::Query; + + /// Take the value under a key. + fn take(key: &K, storage: &S) -> Self::Query; + + /// Store a value to be associated with the given key from the map. + fn insert(key: &K, val: &V, storage: &S) { + storage.put(&Self::key_for(key)[..], val); + } + + /// Remove the value under a key. + fn remove(key: &K, storage: &S) { + storage.kill(&Self::key_for(key)[..]); + } + + /// Mutate the value under a key. + fn mutate(key: &K, f: F, storage: &S); +} + +// TODO: Remove this in favour of `decl_storage` macro. +/// Declares strongly-typed wrappers around codec-compatible types in storage. +#[macro_export] +macro_rules! storage_items { + // simple values + ($name:ident : $key:expr => $ty:ty; $($t:tt)*) => { + __storage_items_internal!(() () (OPTION_TYPE Option<$ty>) (get) (take) $name: $key => $ty); + storage_items!($($t)*); + }; + (pub $name:ident : $key:expr => $ty:ty; $($t:tt)*) => { + __storage_items_internal!((pub) () (OPTION_TYPE Option<$ty>) (get) (take) $name: $key => $ty); + storage_items!($($t)*); + }; + ($name:ident : $key:expr => default $ty:ty; $($t:tt)*) => { + __storage_items_internal!(() () (RAW_TYPE $ty) (get_or_default) (take_or_default) $name: $key => $ty); + storage_items!($($t)*); + }; + (pub $name:ident : $key:expr => default $ty:ty; $($t:tt)*) => { + __storage_items_internal!((pub) () (RAW_TYPE $ty) (get_or_default) (take_or_default) $name: $key => $ty); + storage_items!($($t)*); + }; + ($name:ident : $key:expr => required $ty:ty; $($t:tt)*) => { + __storage_items_internal!(() () (RAW_TYPE $ty) (require) (take_or_panic) $name: $key => $ty); + storage_items!($($t)*); + }; + (pub $name:ident : $key:expr => required $ty:ty; $($t:tt)*) => { + __storage_items_internal!((pub) () (RAW_TYPE $ty) (require) (take_or_panic) $name: $key => $ty); + storage_items!($($t)*); + }; + + ($name:ident get($getfn:ident) : $key:expr => $ty:ty; $($t:tt)*) => { + __storage_items_internal!(() ($getfn) (OPTION_TYPE Option<$ty>) (get) (take) $name: $key => $ty); + storage_items!($($t)*); + }; + (pub $name:ident get($getfn:ident) : $key:expr => $ty:ty; $($t:tt)*) => { + __storage_items_internal!((pub) ($getfn) (OPTION_TYPE Option<$ty>) (get) (take) $name: $key => $ty); + storage_items!($($t)*); + }; + ($name:ident get($getfn:ident) : $key:expr => default $ty:ty; $($t:tt)*) => { + __storage_items_internal!(() ($getfn) (RAW_TYPE $ty) (get_or_default) (take_or_default) $name: $key => $ty); + storage_items!($($t)*); + }; + (pub $name:ident get($getfn:ident) : $key:expr => default $ty:ty; $($t:tt)*) => { + __storage_items_internal!((pub) ($getfn) (RAW_TYPE $ty) (get_or_default) (take_or_default) $name: $key => $ty); + storage_items!($($t)*); + }; + ($name:ident get($getfn:ident) : $key:expr => required $ty:ty; $($t:tt)*) => { + __storage_items_internal!(() ($getfn) (RAW_TYPE $ty) (require) (take_or_panic) $name: $key => $ty); + storage_items!($($t)*); + }; + (pub $name:ident get($getfn:ident) : $key:expr => required $ty:ty; $($t:tt)*) => { + __storage_items_internal!((pub) ($getfn) (RAW_TYPE $ty) (require) (take_or_panic) $name: $key => $ty); + storage_items!($($t)*); + }; + + // maps + ($name:ident : $prefix:expr => map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __storage_items_internal!(() () (OPTION_TYPE Option<$ty>) (get) (take) $name: $prefix => map [$kty => $ty]); + storage_items!($($t)*); + }; + (pub $name:ident : $prefix:expr => map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __storage_items_internal!((pub) () (OPTION_TYPE Option<$ty>) (get) (take) $name: $prefix => map [$kty => $ty]); + storage_items!($($t)*); + }; + ($name:ident : $prefix:expr => default map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __storage_items_internal!(() () (RAW_TYPE $ty) (get_or_default) (take_or_default) $name: $prefix => map [$kty => $ty]); + storage_items!($($t)*); + }; + (pub $name:ident : $prefix:expr => default map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __storage_items_internal!((pub) () (RAW_TYPE $ty) (get_or_default) (take_or_default) $name: $prefix => map [$kty => $ty]); + storage_items!($($t)*); + }; + ($name:ident : $prefix:expr => required map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __storage_items_internal!(() () (RAW_TYPE $ty) (require) (take_or_panic) $name: $prefix => map [$kty => $ty]); + storage_items!($($t)*); + }; + (pub $name:ident : $prefix:expr => required map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __storage_items_internal!((pub) () (RAW_TYPE $ty) (require) (take_or_panic) $name: $prefix => map [$kty => $ty]); + storage_items!($($t)*); + }; + + ($name:ident get($getfn:ident) : $prefix:expr => map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __storage_items_internal!(() ($getfn) (OPTION_TYPE Option<$ty>) (get) (take) $name: $prefix => map [$kty => $ty]); + storage_items!($($t)*); + }; + (pub $name:ident get($getfn:ident) : $prefix:expr => map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __storage_items_internal!((pub) ($getfn) (OPTION_TYPE Option<$ty>) (get) (take) $name: $prefix => map [$kty => $ty]); + storage_items!($($t)*); + }; + ($name:ident get($getfn:ident) : $prefix:expr => default map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __storage_items_internal!(() ($getfn) (RAW_TYPE $ty) (get_or_default) (take_or_default) $name: $prefix => map [$kty => $ty]); + storage_items!($($t)*); + }; + (pub $name:ident get($getfn:ident) : $prefix:expr => default map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __storage_items_internal!((pub) ($getfn) (RAW_TYPE $ty) (get_or_default) (take_or_default) $name: $prefix => map [$kty => $ty]); + storage_items!($($t)*); + }; + ($name:ident get($getfn:ident) : $prefix:expr => required map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __storage_items_internal!(() ($getfn) (RAW_TYPE $ty) (require) (take_or_panic) $name: $prefix => map [$kty => $ty]); + storage_items!($($t)*); + }; + (pub $name:ident get($getfn:ident) : $prefix:expr => required map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __storage_items_internal!((pub) ($getfn) (RAW_TYPE $ty) (require) (take_or_panic) $name: $prefix => map [$kty => $ty]); + storage_items!($($t)*); + }; + + + // lists + ($name:ident : $prefix:expr => list [$ty:ty]; $($t:tt)*) => { + __storage_items_internal!(() $name: $prefix => list [$ty]); + storage_items!($($t)*); + }; + (pub $name:ident : $prefix:expr => list [$ty:ty]; $($t:tt)*) => { + __storage_items_internal!((pub) $name: $prefix => list [$ty]); + storage_items!($($t)*); + }; + () => () +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __handle_wrap_internal { + (RAW_TYPE { $($raw:tt)* } { $($option:tt)* }) => { + $($raw)*; + }; + (OPTION_TYPE { $($raw:tt)* } { $($option:tt)* }) => { + $($option)*; + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __storage_items_internal { + // generator for values. + (($($vis:tt)*) ($get_fn:ident) ($wraptype:ident $gettype:ty) ($getter:ident) ($taker:ident) $name:ident : $key:expr => $ty:ty) => { + __storage_items_internal!{ ($($vis)*) () ($wraptype $gettype) ($getter) ($taker) $name : $key => $ty } + pub fn $get_fn() -> $gettype { <$name as $crate::storage::generator::StorageValue<$ty>> :: get(&$crate::storage::RuntimeStorage) } + }; + (($($vis:tt)*) () ($wraptype:ident $gettype:ty) ($getter:ident) ($taker:ident) $name:ident : $key:expr => $ty:ty) => { + $($vis)* struct $name; + + impl $crate::storage::generator::StorageValue<$ty> for $name { + type Query = $gettype; + + /// Get the storage key. + fn key() -> &'static [u8] { + $key + } + + /// Load the value from the provided storage instance. + fn get(storage: &S) -> Self::Query { + storage.$getter($key) + } + + /// Take a value from storage, removing it afterwards. + fn take(storage: &S) -> Self::Query { + storage.$taker($key) + } + + /// Mutate this value. + fn mutate(f: F, storage: &S) { + let mut val = >::get(storage); + + f(&mut val); + + __handle_wrap_internal!($wraptype { + // raw type case + >::put(&val, storage) + } { + // Option<> type case + match val { + Some(val) => >::put(&val, storage), + None => >::kill(storage), + } + }); + } + } + }; + // generator for maps. + (($($vis:tt)*) ($get_fn:ident) ($wraptype:ident $gettype:ty) ($getter:ident) ($taker:ident) $name:ident : $prefix:expr => map [$kty:ty => $ty:ty]) => { + __storage_items_internal!{ ($($vis)*) () ($wraptype $gettype) ($getter) ($taker) $name : $prefix => map [$kty => $ty] } + pub fn $get_fn>(key: K) -> $gettype { + <$name as $crate::storage::generator::StorageMap<$kty, $ty>> :: get(key.borrow(), &$crate::storage::RuntimeStorage) + } + }; + (($($vis:tt)*) () ($wraptype:ident $gettype:ty) ($getter:ident) ($taker:ident) $name:ident : $prefix:expr => map [$kty:ty => $ty:ty]) => { + $($vis)* struct $name; + + impl $crate::storage::generator::StorageMap<$kty, $ty> for $name { + type Query = $gettype; + + /// Get the prefix key in storage. + fn prefix() -> &'static [u8] { + $prefix + } + + /// Get the storage key used to fetch a value corresponding to a specific key. + fn key_for(x: &$kty) -> Vec { + let mut key = $prefix.to_vec(); + $crate::codec::Encode::encode_to(x, &mut key); + key + } + + /// Load the value associated with the given key from the map. + fn get(key: &$kty, storage: &S) -> Self::Query { + let key = <$name as $crate::storage::generator::StorageMap<$kty, $ty>>::key_for(key); + storage.$getter(&key[..]) + } + + /// Take the value, reading and removing it. + fn take(key: &$kty, storage: &S) -> Self::Query { + let key = <$name as $crate::storage::generator::StorageMap<$kty, $ty>>::key_for(key); + storage.$taker(&key[..]) + } + + /// Mutate the value under a key. + fn mutate(key: &$kty, f: F, storage: &S) { + let mut val = >::take(key, storage); + + f(&mut val); + + __handle_wrap_internal!($wraptype { + // raw type case + >::insert(key, &val, storage) + } { + // Option<> type case + match val { + Some(val) => >::insert(key, &val, storage), + None => >::remove(key, storage), + } + }); + } + } + }; + // generator for lists. + (($($vis:tt)*) $name:ident : $prefix:expr => list [$ty:ty]) => { + $($vis)* struct $name; + + impl $name { + fn clear_item(index: u32, storage: &S) { + if index < <$name as $crate::storage::generator::StorageList<$ty>>::len(storage) { + storage.kill(&<$name as $crate::storage::generator::StorageList<$ty>>::key_for(index)); + } + } + + fn set_len(count: u32, storage: &S) { + (count..<$name as $crate::storage::generator::StorageList<$ty>>::len(storage)).for_each(|i| $name::clear_item(i, storage)); + storage.put(&<$name as $crate::storage::generator::StorageList<$ty>>::len_key(), &count); + } + } + + impl $crate::storage::generator::StorageList<$ty> for $name { + /// Get the prefix key in storage. + fn prefix() -> &'static [u8] { + $prefix + } + + /// Get the key used to put the length field. + // TODO: concat macro should accept byte literals. + fn len_key() -> Vec { + let mut key = $prefix.to_vec(); + key.extend(b"len"); + key + } + + /// Get the storage key used to fetch a value at a given index. + fn key_for(index: u32) -> Vec { + let mut key = $prefix.to_vec(); + $crate::codec::Encode::encode_to(&index, &mut key); + key + } + + /// Read out all the items. + fn items(storage: &S) -> Vec<$ty> { + (0..<$name as $crate::storage::generator::StorageList<$ty>>::len(storage)) + .map(|i| <$name as $crate::storage::generator::StorageList<$ty>>::get(i, storage).expect("all items within length are set; qed")) + .collect() + } + + /// Set the current set of items. + fn set_items(items: &[$ty], storage: &S) { + $name::set_len(items.len() as u32, storage); + items.iter() + .enumerate() + .for_each(|(i, item)| <$name as $crate::storage::generator::StorageList<$ty>>::set_item(i as u32, item, storage)); + } + + fn set_item(index: u32, item: &$ty, storage: &S) { + if index < <$name as $crate::storage::generator::StorageList<$ty>>::len(storage) { + storage.put(&<$name as $crate::storage::generator::StorageList<$ty>>::key_for(index)[..], item); + } + } + + /// Load the value at given index. Returns `None` if the index is out-of-bounds. + fn get(index: u32, storage: &S) -> Option<$ty> { + storage.get(&<$name as $crate::storage::generator::StorageList<$ty>>::key_for(index)[..]) + } + + /// Load the length of the list. + fn len(storage: &S) -> u32 { + storage.get(&<$name as $crate::storage::generator::StorageList<$ty>>::len_key()).unwrap_or_default() + } + + /// Clear the list. + fn clear(storage: &S) { + for i in 0..<$name as $crate::storage::generator::StorageList<$ty>>::len(storage) { + $name::clear_item(i, storage); + } + + storage.kill(&<$name as $crate::storage::generator::StorageList<$ty>>::len_key()[..]) + } + } + }; +} + +// TODO: revisit this idiom once we get `type`s in `impl`s. +/*impl Module { + type Now = super::Now; +}*/ + +/// Declares strongly-typed wrappers around codec-compatible types in storage. +/// +/// For now we implement a convenience trait with pre-specialised associated types, one for each +/// storage item. This allows you to gain access to publicly visisible storage items from a +/// module type. Currently you must disambiguate by using `::Item` rather than +/// the simpler `Module::Item`. Hopefully the rust guys with fix this soon. +#[macro_export] +macro_rules! decl_storage { + ( + trait $storetype:ident for $modulename:ident<$traitinstance:ident: $traittype:ident> as $cratename:ident { + $($t:tt)* + } + ) => { + __decl_storage_items!($cratename $traittype $traitinstance $($t)*); + trait $storetype { + __decl_store_items!($($t)*); + } + impl<$traitinstance: $traittype> $storetype for $modulename<$traitinstance> { + __impl_store_items!($traitinstance $($t)*); + } + impl<$traitinstance: $traittype> $modulename<$traitinstance> { + __impl_store_fns!($traitinstance $($t)*); + __impl_store_json_metadata!($cratename; $($t)*); + } + }; + ( + pub trait $storetype:ident for $modulename:ident<$traitinstance:ident: $traittype:ident> as $cratename:ident { + $($t:tt)* + } + ) => { + __decl_storage_items!($cratename $traittype $traitinstance $($t)*); + pub trait $storetype { + __decl_store_items!($($t)*); + } + impl<$traitinstance: $traittype> $storetype for $modulename<$traitinstance> { + __impl_store_items!($traitinstance $($t)*); + } + impl<$traitinstance: $traittype> $modulename<$traitinstance> { + __impl_store_fns!($traitinstance $($t)*); + } + } +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __decl_storage_items { + // simple values + ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* $name:ident : $ty:ty; $($t:tt)*) => { + __decl_storage_item!(() ($traittype as $traitinstance) () (OPTION_TYPE Option<$ty>) (get) (take) $cratename $name: $ty); + __decl_storage_items!($cratename $traittype $traitinstance $($t)*); + }; + ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* pub $name:ident : $ty:ty; $($t:tt)*) => { + __decl_storage_item!((pub) ($traittype as $traitinstance) () (OPTION_TYPE Option<$ty>) (get) (take) $cratename $name: $ty); + __decl_storage_items!($cratename $traittype $traitinstance $($t)*); + }; + ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* $name:ident : default $ty:ty; $($t:tt)*) => { + __decl_storage_item!(() ($traittype as $traitinstance) () (RAW_TYPE $ty) (get_or_default) (take_or_default) $cratename $name: $ty); + __decl_storage_items!($cratename $traittype $traitinstance $($t)*); + }; + ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* pub $name:ident : default $ty:ty; $($t:tt)*) => { + __decl_storage_item!((pub) ($traittype as $traitinstance) () (RAW_TYPE $ty) (get_or_default) (take_or_default) $cratename $name: $ty); + __decl_storage_items!($cratename $traittype $traitinstance $($t)*); + }; + ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* $name:ident : required $ty:ty; $($t:tt)*) => { + __decl_storage_item!(() ($traittype as $traitinstance) () (RAW_TYPE $ty) (require) (take_or_panic) $cratename $name: $ty); + __decl_storage_items!($cratename $traittype $traitinstance $($t)*); + }; + ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* pub $name:ident : required $ty:ty; $($t:tt)*) => { + __decl_storage_item!((pub) ($traittype as $traitinstance) () (RAW_TYPE $ty) (require) (take_or_panic) $cratename $name: $ty); + __decl_storage_items!($cratename $traittype $traitinstance $($t)*); + }; + + ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : $ty:ty; $($t:tt)*) => { + __decl_storage_item!(() ($traittype as $traitinstance) ($getfn) (OPTION_TYPE Option<$ty>) (get) (take) $cratename $name: $ty); + __decl_storage_items!($cratename $traittype $traitinstance $($t)*); + }; + ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : $ty:ty; $($t:tt)*) => { + __decl_storage_item!((pub) ($traittype as $traitinstance) ($getfn) (OPTION_TYPE Option<$ty>) (get) (take) $cratename $name: $ty); + __decl_storage_items!($cratename $traittype $traitinstance $($t)*); + }; + ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : default $ty:ty; $($t:tt)*) => { + __decl_storage_item!(() ($traittype as $traitinstance) ($getfn) (RAW_TYPE $ty) (get_or_default) (take_or_default) $cratename $name: $ty); + __decl_storage_items!($cratename $traittype $traitinstance $($t)*); + }; + ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : default $ty:ty; $($t:tt)*) => { + __decl_storage_item!((pub) ($traittype as $traitinstance) ($getfn) (RAW_TYPE $ty) (get_or_default) (take_or_default) $cratename $name: $ty); + __decl_storage_items!($cratename $traittype $traitinstance $($t)*); + }; + ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : required $ty:ty; $($t:tt)*) => { + __decl_storage_item!(() ($traittype as $traitinstance) ($getfn) (RAW_TYPE $ty) (require) (take_or_panic) $cratename $name: $ty); + __decl_storage_items!($cratename $traittype $traitinstance $($t)*); + }; + ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : required $ty:ty; $($t:tt)*) => { + __decl_storage_item!((pub) ($traittype as $traitinstance) ($getfn) (RAW_TYPE $ty) (require) (take_or_panic) $cratename $name: $ty); + __decl_storage_items!($cratename $traittype $traitinstance $($t)*); + }; + + // maps + ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* $name:ident : map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __decl_storage_item!(() ($traittype as $traitinstance) () (OPTION_TYPE Option<$ty>) (get) (take) $cratename $name: map [$kty => $ty]); + __decl_storage_items!($cratename $traittype $traitinstance $($t)*); + }; + ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* pub $name:ident : map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __decl_storage_item!((pub) ($traittype as $traitinstance) () (OPTION_TYPE Option<$ty>) (get) (take) $cratename $name: map [$kty => $ty]); + __decl_storage_items!($cratename $traittype $traitinstance $($t)*); + }; + ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* $name:ident : default map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __decl_storage_item!(() ($traittype as $traitinstance) () (RAW_TYPE $ty) (get_or_default) (take_or_default) $cratename $name: map [$kty => $ty]); + __decl_storage_items!($cratename $traittype $traitinstance $($t)*); + }; + ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* pub $name:ident : default map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __decl_storage_item!((pub) ($traittype as $traitinstance) () (RAW_TYPE $ty) (get_or_default) (take_or_default) $cratename $name: map [$kty => $ty]); + __decl_storage_items!($cratename $traittype $traitinstance $($t)*); + }; + ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* $name:ident : required map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __decl_storage_item!(() ($traittype as $traitinstance) () (RAW_TYPE $ty) (require) (take_or_panic) $cratename $name: map [$kty => $ty]); + __decl_storage_items!($cratename $traittype $traitinstance $($t)*); + }; + ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* pub $name:ident : required map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __decl_storage_item!((pub) ($traittype as $traitinstance) () (RAW_TYPE $ty) (require) (take_or_panic) $cratename $name: map [$kty => $ty]); + __decl_storage_items!($cratename $traittype $traitinstance $($t)*); + }; + + ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __decl_storage_item!(() ($traittype as $traitinstance) ($getfn) (OPTION_TYPE Option<$ty>) (get) (take) $cratename $name: map [$kty => $ty]); + __decl_storage_items!($cratename $traittype $traitinstance $($t)*); + }; + ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __decl_storage_item!((pub) ($traittype as $traitinstance) ($getfn) (OPTION_TYPE Option<$ty>) (get) (take) $cratename $name: map [$kty => $ty]); + __decl_storage_items!($cratename $traittype $traitinstance $($t)*); + }; + ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : default map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __decl_storage_item!(() ($traittype as $traitinstance) ($getfn) (RAW_TYPE $ty) (get_or_default) (take_or_default) $cratename $name: map [$kty => $ty]); + __decl_storage_items!($cratename $traittype $traitinstance $($t)*); + }; + ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : default map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __decl_storage_item!((pub) ($traittype as $traitinstance) ($getfn) (RAW_TYPE $ty) (get_or_default) (take_or_default) $cratename $name: map [$kty => $ty]); + __decl_storage_items!($cratename $traittype $traitinstance $($t)*); + }; + ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : required map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __decl_storage_item!(() ($traittype as $traitinstance) ($getfn) (RAW_TYPE $ty) (require) (take_or_panic) $cratename $name: map [$kty => $ty]); + __decl_storage_items!($cratename $traittype $traitinstance $($t)*); + }; + ($cratename:ident $traittype:ident $traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : required map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __decl_storage_item!((pub) ($traittype as $traitinstance) ($getfn) (RAW_TYPE $ty) (require) (take_or_panic) $cratename $name: map [$kty => $ty]); + __decl_storage_items!($cratename $traittype $traitinstance $($t)*); + }; + + // exit + ($cratename:ident $traittype:ident $traitinstance:ident) => () +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __decl_storage_item { + // generator for values. + (($($vis:tt)*) ($traittype:ident as $traitinstance:ident) ($get_fn:ident) ($wraptype:ident $gettype:ty) ($getter:ident) ($taker:ident) $cratename:ident $name:ident : $ty:ty) => { + __decl_storage_item!{ ($($vis)*) ($traittype as $traitinstance) () ($wraptype $gettype) ($getter) ($taker) $cratename $name : $ty } + }; + (($($vis:tt)*) ($traittype:ident as $traitinstance:ident) () ($wraptype:ident $gettype:ty) ($getter:ident) ($taker:ident) $cratename:ident $name:ident : $ty:ty) => { + $($vis)* struct $name<$traitinstance: $traittype>($crate::storage::generator::PhantomData<$traitinstance>); + + impl<$traitinstance: $traittype> $crate::storage::generator::StorageValue<$ty> for $name<$traitinstance> { + type Query = $gettype; + + /// Get the storage key. + fn key() -> &'static [u8] { + stringify!($cratename $name).as_bytes() + } + + /// Load the value from the provided storage instance. + fn get(storage: &S) -> Self::Query { + storage.$getter(<$name<$traitinstance> as $crate::storage::generator::StorageValue<$ty>>::key()) + } + + /// Take a value from storage, removing it afterwards. + fn take(storage: &S) -> Self::Query { + storage.$taker(<$name<$traitinstance> as $crate::storage::generator::StorageValue<$ty>>::key()) + } + + /// Mutate the value under a key. + fn mutate(f: F, storage: &S) { + let mut val = >::get(storage); + + f(&mut val); + + __handle_wrap_internal!($wraptype { + // raw type case + >::put(&val, storage) + } { + // Option<> type case + match val { + Some(val) => >::put(&val, storage), + None => >::kill(storage), + } + }) + } + } + }; + // generator for maps. + (($($vis:tt)*) ($traittype:ident as $traitinstance:ident) ($get_fn:ident) ($wraptype:ident $gettype:ty) ($getter:ident) ($taker:ident) $cratename:ident $name:ident : map [$kty:ty => $ty:ty]) => { + __decl_storage_item!{ ($($vis)*) ($traittype as $traitinstance) () ($wraptype $gettype) ($getter) ($taker) $cratename $name : map [$kty => $ty] } + }; + (($($vis:tt)*) ($traittype:ident as $traitinstance:ident) () ($wraptype:ident $gettype:ty) ($getter:ident) ($taker:ident) $cratename:ident $name:ident : map [$kty:ty => $ty:ty]) => { + $($vis)* struct $name<$traitinstance: $traittype>($crate::storage::generator::PhantomData<$traitinstance>); + + impl<$traitinstance: $traittype> $crate::storage::generator::StorageMap<$kty, $ty> for $name<$traitinstance> { + type Query = $gettype; + + /// Get the prefix key in storage. + fn prefix() -> &'static [u8] { + stringify!($cratename $name).as_bytes() + } + + /// Get the storage key used to fetch a value corresponding to a specific key. + fn key_for(x: &$kty) -> Vec { + let mut key = <$name<$traitinstance> as $crate::storage::generator::StorageMap<$kty, $ty>>::prefix().to_vec(); + $crate::codec::Encode::encode_to(x, &mut key); + key + } + + /// Load the value associated with the given key from the map. + fn get(key: &$kty, storage: &S) -> Self::Query { + let key = <$name<$traitinstance> as $crate::storage::generator::StorageMap<$kty, $ty>>::key_for(key); + storage.$getter(&key[..]) + } + + /// Take the value, reading and removing it. + fn take(key: &$kty, storage: &S) -> Self::Query { + let key = <$name<$traitinstance> as $crate::storage::generator::StorageMap<$kty, $ty>>::key_for(key); + storage.$taker(&key[..]) + } + + /// Mutate the value under a key + fn mutate(key: &$kty, f: F, storage: &S) { + let mut val = >::take(key, storage); + + f(&mut val); + + __handle_wrap_internal!($wraptype { + >::insert(key, &val, storage); + } { + match val { + Some(val) => >::insert(key, &val, storage), + None => >::remove(key, storage), + } + }); + } + } + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __decl_store_items { + // simple values + ($(#[$doc:meta])* $name:ident : default $ty:ty; $($t:tt)*) => { + __decl_store_item!($name); __decl_store_items!($($t)*); + }; + ($(#[$doc:meta])* pub $name:ident : default $ty:ty; $($t:tt)*) => { + __decl_store_item!($name); __decl_store_items!($($t)*); + }; + ($(#[$doc:meta])* $name:ident : required $ty:ty; $($t:tt)*) => { + __decl_store_item!($name); __decl_store_items!($($t)*); + }; + ($(#[$doc:meta])* pub $name:ident : required $ty:ty; $($t:tt)*) => { + __decl_store_item!($name); __decl_store_items!($($t)*); + }; + ($(#[$doc:meta])* $name:ident : $ty:ty; $($t:tt)*) => { + __decl_store_item!($name); __decl_store_items!($($t)*); + }; + ($(#[$doc:meta])* pub $name:ident : $ty:ty; $($t:tt)*) => { + __decl_store_item!($name); __decl_store_items!($($t)*); + }; + + ($(#[$doc:meta])* $name:ident get($getfn:ident) : default $ty:ty; $($t:tt)*) => { + __decl_store_item!($name); __decl_store_items!($($t)*); + }; + ($(#[$doc:meta])* pub $name:ident get($getfn:ident) : default $ty:ty; $($t:tt)*) => { + __decl_store_item!($name); __decl_store_items!($($t)*); + }; + ($(#[$doc:meta])* $name:ident get($getfn:ident) : required $ty:ty; $($t:tt)*) => { + __decl_store_item!($name); __decl_store_items!($($t)*); + }; + ($(#[$doc:meta])* pub $name:ident get($getfn:ident) : required $ty:ty; $($t:tt)*) => { + __decl_store_item!($name); __decl_store_items!($($t)*); + }; + ($(#[$doc:meta])* $name:ident get($getfn:ident) : $ty:ty; $($t:tt)*) => { + __decl_store_item!($name); __decl_store_items!($($t)*); + }; + ($(#[$doc:meta])* pub $name:ident get($getfn:ident) : $ty:ty; $($t:tt)*) => { + __decl_store_item!($name); __decl_store_items!($($t)*); + }; + + // maps + ($(#[$doc:meta])* $name:ident : default map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __decl_store_item!($name); __decl_store_items!($($t)*); + }; + ($(#[$doc:meta])* pub $name:ident : default map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __decl_store_item!($name); __decl_store_items!($($t)*); + }; + ($(#[$doc:meta])* $name:ident : required map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __decl_store_item!($name); __decl_store_items!($($t)*); + }; + ($(#[$doc:meta])* pub $name:ident : required map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __decl_store_item!($name); __decl_store_items!($($t)*); + }; + ($(#[$doc:meta])* $name:ident : map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __decl_store_item!($name); __decl_store_items!($($t)*); + }; + ($(#[$doc:meta])* pub $name:ident : map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __decl_store_item!($name); __decl_store_items!($($t)*); + }; + + ($(#[$doc:meta])* $name:ident get($getfn:ident) : default map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __decl_store_item!($name); __decl_store_items!($($t)*); + }; + ($(#[$doc:meta])* pub $name:ident get($getfn:ident) : default map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __decl_store_item!($name); __decl_store_items!($($t)*); + }; + ($(#[$doc:meta])* $name:ident get($getfn:ident) : required map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __decl_store_item!($name); __decl_store_items!($($t)*); + }; + ($(#[$doc:meta])* pub $name:ident get($getfn:ident) : required map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __decl_store_item!($name); __decl_store_items!($($t)*); + }; + ($(#[$doc:meta])* $name:ident get($getfn:ident) : map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __decl_store_item!($name); __decl_store_items!($($t)*); + }; + ($(#[$doc:meta])* pub $name:ident get($getfn:ident) : map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __decl_store_item!($name); __decl_store_items!($($t)*); + }; + + // exit + () => () +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __decl_store_item { + ($name:ident) => { type $name; } +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __impl_store_fns { + // simple values + ($traitinstance:ident $(#[$doc:meta])* $name:ident : default $ty:ty; $($t:tt)*) => { + __impl_store_fns!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* pub $name:ident : default $ty:ty; $($t:tt)*) => { + __impl_store_fns!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* $name:ident : required $ty:ty; $($t:tt)*) => { + __impl_store_fns!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* pub $name:ident : required $ty:ty; $($t:tt)*) => { + __impl_store_fns!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* $name:ident : $ty:ty; $($t:tt)*) => { + __impl_store_fns!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* pub $name:ident : $ty:ty; $($t:tt)*) => { + __impl_store_fns!($traitinstance $($t)*); + }; + + ($traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : default $ty:ty; $($t:tt)*) => { + __impl_store_fn!($traitinstance $name $getfn ($ty) $ty); + __impl_store_fns!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : default $ty:ty; $($t:tt)*) => { + __impl_store_fn!($traitinstance $name $getfn ($ty) $ty); + __impl_store_fns!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : required $ty:ty; $($t:tt)*) => { + __impl_store_fn!($traitinstance $name $getfn ($ty) $ty); + __impl_store_fns!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : required $ty:ty; $($t:tt)*) => { + __impl_store_fn!($traitinstance $name $getfn ($ty) $ty); + __impl_store_fns!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : $ty:ty; $($t:tt)*) => { + __impl_store_fn!($traitinstance $name $getfn (Option<$ty>) $ty); + __impl_store_fns!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : $ty:ty; $($t:tt)*) => { + __impl_store_fn!($traitinstance $name $getfn (Option<$ty>) $ty); + __impl_store_fns!($traitinstance $($t)*); + }; + + // maps + ($traitinstance:ident $(#[$doc:meta])* $name:ident : default map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __impl_store_fns!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* pub $name:ident : default map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __impl_store_fns!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* $name:ident : required map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __impl_store_fns!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* pub $name:ident : required map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __impl_store_fns!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* $name:ident : map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __impl_store_fns!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* pub $name:ident : map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __impl_store_fns!($traitinstance $($t)*); + }; + + ($traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : default map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __impl_store_fn!($traitinstance $name $getfn ($ty) map [$kty => $ty]); + __impl_store_fns!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : default map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __impl_store_fn!($traitinstance $name $getfn ($ty) map [$kty => $ty]); + __impl_store_fns!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : required map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __impl_store_fn!($traitinstance $name $getfn ($ty) map [$kty => $ty]); + __impl_store_fns!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : required map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __impl_store_fn!($traitinstance $name $getfn ($ty) map [$kty => $ty]); + __impl_store_fns!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __impl_store_fn!($traitinstance $name $getfn (Option<$ty>) map [$kty => $ty]); + __impl_store_fns!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __impl_store_fn!($traitinstance $name $getfn (Option<$ty>) map [$kty => $ty]); + __impl_store_fns!($traitinstance $($t)*); + }; + + // exit + ($traitinstance:ident) => () +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __impl_store_fn { + ($traitinstance:ident $name:ident $get_fn:ident ($gettype:ty) $ty:ty) => { + pub fn $get_fn() -> $gettype { + <$name<$traitinstance> as $crate::storage::generator::StorageValue<$ty>> :: get(&$crate::storage::RuntimeStorage) + } + }; + ($traitinstance:ident $name:ident $get_fn:ident ($gettype:ty) map [$kty:ty => $ty:ty]) => { + pub fn $get_fn>(key: K) -> $gettype { + <$name<$traitinstance> as $crate::storage::generator::StorageMap<$kty, $ty>> :: get(key.borrow(), &$crate::storage::RuntimeStorage) + } + } +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __impl_store_items { + // simple values + ($traitinstance:ident $(#[$doc:meta])* $name:ident : default $ty:ty; $($t:tt)*) => { + __impl_store_item!($name $traitinstance); + __impl_store_items!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* pub $name:ident : default $ty:ty; $($t:tt)*) => { + __impl_store_item!($name $traitinstance); + __impl_store_items!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* $name:ident : required $ty:ty; $($t:tt)*) => { + __impl_store_item!($name $traitinstance); + __impl_store_items!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* pub $name:ident : required $ty:ty; $($t:tt)*) => { + __impl_store_item!($name $traitinstance); + __impl_store_items!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* $name:ident : $ty:ty; $($t:tt)*) => { + __impl_store_item!($name $traitinstance); + __impl_store_items!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* pub $name:ident : $ty:ty; $($t:tt)*) => { + __impl_store_item!($name $traitinstance); + __impl_store_items!($traitinstance $($t)*); + }; + + ($traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : default $ty:ty; $($t:tt)*) => { + __impl_store_item!($name $traitinstance); + __impl_store_items!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : default $ty:ty; $($t:tt)*) => { + __impl_store_item!($name $traitinstance); + __impl_store_items!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : required $ty:ty; $($t:tt)*) => { + __impl_store_item!($name $traitinstance); + __impl_store_items!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : required $ty:ty; $($t:tt)*) => { + __impl_store_item!($name $traitinstance); + __impl_store_items!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : $ty:ty; $($t:tt)*) => { + __impl_store_item!($name $traitinstance); + __impl_store_items!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : $ty:ty; $($t:tt)*) => { + __impl_store_item!($name $traitinstance); + __impl_store_items!($traitinstance $($t)*); + }; + + // maps + ($traitinstance:ident $(#[$doc:meta])* $name:ident : default map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __impl_store_item!($name $traitinstance); + __impl_store_items!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* pub $name:ident : default map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __impl_store_item!($name $traitinstance); + __impl_store_items!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* $name:ident : required map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __impl_store_item!($name $traitinstance); + __impl_store_items!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* pub $name:ident : required map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __impl_store_item!($name $traitinstance); + __impl_store_items!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* $name:ident : map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __impl_store_item!($name $traitinstance); + __impl_store_items!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* pub $name:ident : map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __impl_store_item!($name $traitinstance); + __impl_store_items!($traitinstance $($t)*); + }; + + ($traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : default map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __impl_store_item!($name $traitinstance); + __impl_store_items!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : default map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __impl_store_item!($name $traitinstance); + __impl_store_items!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : required map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __impl_store_item!($name $traitinstance); + __impl_store_items!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : required map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __impl_store_item!($name $traitinstance); + __impl_store_items!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* $name:ident get($getfn:ident) : map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __impl_store_item!($name $traitinstance); + __impl_store_items!($traitinstance $($t)*); + }; + ($traitinstance:ident $(#[$doc:meta])* pub $name:ident get($getfn:ident) : map [$kty:ty => $ty:ty]; $($t:tt)*) => { + __impl_store_item!($name $traitinstance); + __impl_store_items!($traitinstance $($t)*); + }; + + // exit + ($traitinstance:ident) => () +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __impl_store_item { + ($name:ident $traitinstance:ident) => { type $name = $name<$traitinstance>; } +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __impl_store_json_metadata { + ( + $cratename:ident; + $($rest:tt)* + ) => { + pub fn store_json_metadata() -> &'static str { + concat!(r#"{ "prefix": ""#, stringify!($cratename), r#"", "items": {"#, + __store_functions_to_json!(""; $($rest)*), " } }") + } + } +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __store_functions_to_json { + ( + $prefix_str:tt; + $(#[doc = $doc_attr:tt])* + $name:ident : + default $ty:ty; $($t:tt)* + ) => { + concat!( + __store_function_to_json!($prefix_str, + __function_doc_to_json!(""; $($doc_attr)*), + $name, __store_type_to_json!($ty), default + ), + __store_functions_to_json!(","; $($t)*) + ) + }; + ( + $prefix_str:tt; + $(#[doc = $doc_attr:tt])* + pub $name:ident : + default $ty:ty; $($t:tt)* + ) => { + concat!( + __store_function_to_json!($prefix_str, + __function_doc_to_json!(""; $($doc_attr)*), + $name, __store_type_to_json!($ty), default + ), + __store_functions_to_json!(","; $($t)*) + ) + }; + ( + $prefix_str:tt; + $(#[doc = $doc_attr:tt])* + $name:ident : + required $ty:ty; $($t:tt)* + ) => { + concat!( + __store_function_to_json!($prefix_str, + __function_doc_to_json!(""; $($doc_attr)*), + $name, __store_type_to_json!($ty), required + ), + __store_functions_to_json!(","; $($t)*) + ) + }; + ( + $prefix_str:tt; + $(#[doc = $doc_attr:tt])* + pub $name:ident : + required $ty:ty; $($t:tt)* + ) => { + concat!( + __store_function_to_json!($prefix_str, + __function_doc_to_json!(""; $($doc_attr)*), + $name, __store_type_to_json!($ty), required + ), + __store_functions_to_json!(","; $($t)*) + ) + }; + // simple values + ( + $prefix_str:tt; + $(#[doc = $doc_attr:tt])* + $name:ident : + $ty:ty; $($t:tt)* + ) => { + concat!( + __store_function_to_json!($prefix_str, + __function_doc_to_json!(""; $($doc_attr)*), + $name, __store_type_to_json!($ty) + ), + __store_functions_to_json!(","; $($t)*) + ) + }; + ( + $prefix_str:tt; + $(#[doc = $doc_attr:tt])* + pub $name:ident : + $ty:ty; $($t:tt)* + ) => { + concat!( + __store_function_to_json!($prefix_str, + __function_doc_to_json!(""; $($doc_attr)*), + $name, __store_type_to_json!($ty) + ), + __store_functions_to_json!(","; $($t)*) + ) + }; + + ( + $prefix_str:tt; + $(#[doc = $doc_attr:tt])* + $name:ident get($getfn:ident) : + default $ty:ty; $($t:tt)* + ) => { + concat!( + __store_function_to_json!($prefix_str, + __function_doc_to_json!(""; $($doc_attr)*), + $name, __store_type_to_json!($ty), default + ), + __store_functions_to_json!(","; $($t)*) + ) + }; + ( + $prefix_str:tt; + $(#[doc = $doc_attr:tt])* + pub $name:ident get($getfn:ident) : + default $ty:ty; $($t:tt)* + ) => { + concat!( + __store_function_to_json!($prefix_str, + __function_doc_to_json!(""; $($doc_attr)*), + $name, __store_type_to_json!($ty), default + ), + __store_functions_to_json!(","; $($t)*) + ) + }; + ( + $prefix_str:tt; + $(#[doc = $doc_attr:tt])* + $name:ident get($getfn:ident) : + required $ty:ty; $($t:tt)* + ) => { + concat!( + __store_function_to_json!($prefix_str, + __function_doc_to_json!(""; $($doc_attr)*), + $name, __store_type_to_json!($ty), required + ), + __store_functions_to_json!(","; $($t)*) + ) + }; + ( + $prefix_str:tt; + $(#[doc = $doc_attr:tt])* + pub $name:ident get($getfn:ident) : + required $ty:ty; $($t:tt)* + ) => { + concat!( + __store_function_to_json!($prefix_str, + __function_doc_to_json!(""; $($doc_attr)*), + $name, __store_type_to_json!($ty), required + ), + __store_functions_to_json!(","; $($t)*) + ) + }; + ( + $prefix_str:tt; + $(#[doc = $doc_attr:tt])* + $name:ident get($getfn:ident) : + $ty:ty; $($t:tt)* + ) => { + concat!( + __store_function_to_json!($prefix_str, + __function_doc_to_json!(""; $($doc_attr)*), + $name, __store_type_to_json!($ty) + ), + __store_functions_to_json!(","; $($t)*) + ) + }; + ( + $prefix_str:tt; + $(#[doc = $doc_attr:tt])* + pub $name:ident get($getfn:ident) : + $ty:ty; $($t:tt)* + ) => { + concat!( + __store_function_to_json!($prefix_str, + __function_doc_to_json!(""; $($doc_attr)*), + $name, __store_type_to_json!($ty) + ), + __store_functions_to_json!(","; $($t)*) + ) + }; + + // maps + ( + $prefix_str:tt; + $(#[doc = $doc_attr:tt])* + $name:ident : + default map [$kty:ty => $ty:ty]; $($t:tt)* + ) => { + concat!( + __store_function_to_json!($prefix_str, + __function_doc_to_json!(""; $($doc_attr)*), + $name, __store_type_to_json!($kty, $ty), default + ), + __store_functions_to_json!(","; $($t)*) + ) + }; + ( + $prefix_str:tt; + $(#[doc = $doc_attr:tt])* + pub $name:ident : + default map [$kty:ty => $ty:ty]; $($t:tt)* + ) => { + concat!( + __store_function_to_json!($prefix_str, + __function_doc_to_json!(""; $($doc_attr)*), + $name, __store_type_to_json!($kty, $ty), default + ), + __store_functions_to_json!(","; $($t)*) + ) + }; + ( + $prefix_str:tt; + $(#[doc = $doc_attr:tt])* + $name:ident : + required map [$kty:ty => $ty:ty]; $($t:tt)* + ) => { + concat!( + __store_function_to_json!($prefix_str, + __function_doc_to_json!(""; $($doc_attr)*), + $name, __store_type_to_json!($kty, $ty), required + ), + __store_functions_to_json!(","; $($t)*) + ) + }; + ( + $prefix_str:tt; + $(#[doc = $doc_attr:tt])* + pub $name:ident : + required map [$kty:ty => $ty:ty]; $($t:tt)* + ) => { + concat!( + __store_function_to_json!($prefix_str, + __function_doc_to_json!(""; $($doc_attr)*), + $name, __store_type_to_json!($kty, $ty), required + ), + __store_functions_to_json!(","; $($t)*) + ) + }; + ( + $prefix_str:tt; + $(#[doc = $doc_attr:tt])* + $name:ident : + map [$kty:ty => $ty:ty]; $($t:tt)* + ) => { + concat!( + __store_function_to_json!($prefix_str, + __function_doc_to_json!(""; $($doc_attr)*), + $name, __store_type_to_json!($kty, $ty) + ), + __store_functions_to_json!(","; $($t)*) + ) + }; + ( + $prefix_str:tt; + $(#[doc = $doc_attr:tt])* + pub $name:ident : + map [$kty:ty => $ty:ty]; $($t:tt)* + ) => { + concat!( + __store_function_to_json!($prefix_str, + __function_doc_to_json!(""; $($doc_attr)*), + $name, __store_type_to_json!($kty, $ty) + ), + __store_functions_to_json!(","; $($t)*) + ) + }; + + ( + $prefix_str:tt; + $(#[doc = $doc_attr:tt])* $name:ident get($getfn:ident) : + default map [$kty:ty => $ty:ty]; $($t:tt)* + ) => { + concat!( + __store_function_to_json!($prefix_str, + __function_doc_to_json!(""; $($doc_attr)*), + $name, __store_type_to_json!($kty, $ty), default + ), + __store_functions_to_json!(","; $($t)*) + ) + }; + ( + $prefix_str:tt; + $(#[doc = $doc_attr:tt])* + pub $name:ident get($getfn:ident) : + default map [$kty:ty => $ty:ty]; $($t:tt)* + ) => { + concat!( + __store_function_to_json!($prefix_str, + __function_doc_to_json!(""; $($doc_attr)*), + $name, __store_type_to_json!($kty, $ty), default + ), + __store_functions_to_json!(","; $($t)*) + ) + }; + ( + $prefix_str:tt; + $(#[doc = $doc_attr:tt])* $name:ident get($getfn:ident) : + required map [$kty:ty => $ty:ty]; $($t:tt)* + ) => { + concat!( + __store_function_to_json!($prefix_str, + __function_doc_to_json!(""; $($doc_attr)*), + $name, __store_type_to_json!($kty, $ty), required + ), + __store_functions_to_json!(","; $($t)*) + ) + }; + ( + $prefix_str:tt; + $(#[doc = $doc_attr:tt])* + pub $name:ident get($getfn:ident) : + required map [$kty:ty => $ty:ty]; $($t:tt)* + ) => { + concat!( + __store_function_to_json!($prefix_str, + __function_doc_to_json!(""; $($doc_attr)*), + $name, __store_type_to_json!($kty, $ty), required + ), + __store_functions_to_json!(","; $($t)*) + ) + }; + ( + $prefix_str:tt; + $(#[doc = $doc_attr:tt])* + $name:ident get($getfn:ident) : + map [$kty:ty => $ty:ty]; $($t:tt)* + ) => { + concat!( + __store_function_to_json!($prefix_str, + __function_doc_to_json!(""; $($doc_attr)*), + $name, __store_type_to_json!($kty, $ty) + ), + __store_functions_to_json!(","; $($t)*) + ) + }; + ( + $prefix_str:tt; + $(#[doc = $doc_attr:tt])* + pub $name:ident get($getfn:ident) : + map [$kty:ty => $ty:ty]; $($t:tt)* + ) => { + concat!( + __store_function_to_json!($prefix_str, + __function_doc_to_json!(""; $($doc_attr)*), + $name, __store_type_to_json!($kty, $ty) + ), + __store_functions_to_json!(","; $($t)*) + ) + }; + ($prefix_str:tt;) => { "" } +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __store_function_to_json { + ($prefix_str:tt, $fn_doc:expr, $name:ident, $type:expr, $modifier:ident) => { + __store_function_to_json!($prefix_str; $fn_doc; $name; $type; + concat!("\"", stringify!($modifier), "\"")) + }; + ($prefix_str:tt, $fn_doc:expr, $name:ident, $type:expr) => { + __store_function_to_json!($prefix_str; $fn_doc; $name; $type; "null") + }; + ($prefix_str:tt; $fn_doc:expr; $name:ident; $type:expr; $modifier:expr) => { + concat!($prefix_str, " \"", stringify!($name), "\": { ", + r#""description": ["#, $fn_doc, " ], ", + r#""modifier": "#, $modifier, r#", "type": "#, $type, r#" }"# + ) + } +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __store_type_to_json { + ($name:ty) => { + concat!("\"", stringify!($name), "\"") + }; + ($key: ty, $value:ty) => { + concat!(r#"{ "key": ""#, stringify!($key), r#"", "value": ""#, + stringify!($value), "\" }") + } +} + +#[cfg(test)] +// Do not complain about unused `dispatch` and `dispatch_aux`. +#[allow(dead_code)] +mod tests { + use std::collections::HashMap; + use std::cell::RefCell; + use codec::Codec; + use super::*; + use serde; + use serde_json; + + impl Storage for RefCell, Vec>> { + fn exists(&self, key: &[u8]) -> bool { + self.borrow_mut().get(key).is_some() + } + + fn get(&self, key: &[u8]) -> Option { + self.borrow_mut().get(key).map(|v| T::decode(&mut &v[..]).unwrap()) + } + + fn put(&self, key: &[u8], val: &T) { + self.borrow_mut().insert(key.to_owned(), val.encode()); + } + + fn kill(&self, key: &[u8]) { + self.borrow_mut().remove(key); + } + } + + storage_items! { + Value: b"a" => u32; + List: b"b:" => list [u64]; + Map: b"c:" => map [u32 => [u8; 32]]; + } + + #[test] + fn value() { + let storage = RefCell::new(HashMap::new()); + assert!(Value::get(&storage).is_none()); + Value::put(&100_000, &storage); + assert_eq!(Value::get(&storage), Some(100_000)); + Value::kill(&storage); + assert!(Value::get(&storage).is_none()); + } + + #[test] + fn list() { + let storage = RefCell::new(HashMap::new()); + assert_eq!(List::len(&storage), 0); + assert!(List::items(&storage).is_empty()); + + List::set_items(&[0, 2, 4, 6, 8], &storage); + assert_eq!(List::items(&storage), &[0, 2, 4, 6, 8]); + assert_eq!(List::len(&storage), 5); + + List::set_item(2, &10, &storage); + assert_eq!(List::items(&storage), &[0, 2, 10, 6, 8]); + assert_eq!(List::len(&storage), 5); + + List::clear(&storage); + assert_eq!(List::len(&storage), 0); + assert!(List::items(&storage).is_empty()); + } + + #[test] + fn map() { + let storage = RefCell::new(HashMap::new()); + assert!(Map::get(&5, &storage).is_none()); + Map::insert(&5, &[1; 32], &storage); + assert_eq!(Map::get(&5, &storage), Some([1; 32])); + assert_eq!(Map::take(&5, &storage), Some([1; 32])); + assert!(Map::get(&5, &storage).is_none()); + assert!(Map::get(&999, &storage).is_none()); + } + + pub trait Trait { + type Origin; + } + + decl_module! { + pub struct Module for enum Call where origin: T::Origin {} + } + + decl_storage! { + trait Store for Module as TestStorage { + /// Hello, this is doc! + U32 : u32; + GETU32 get(u32_getter): u32; + pub PUBU32 : u32; + pub GETPUBU32 get(pub_u32_getter): u32; + U32Default : default u32; + GETU32Default get(get_u32_default): default u32; + pub PUBU32Default : default u32; + pub GETPUBU32Default get(pub_get_u32_default): default u32; + U32Required : required u32; + GETU32Required get(get_u32_required): required u32; + pub PUBU32Required : required u32; + pub GETPUBU32Required get(pub_get_u32_required): required u32; + + MAPU32 : map [ u32 => String ]; + /// Hello, this is doc! + /// Hello, this is doc 2! + GETMAPU32 get(map_u32_getter): map [ u32 => String ]; + pub PUBMAPU32 : map [ u32 => String ]; + pub GETPUBMAPU32 get(map_pub_u32_getter): map [ u32 => String ]; + MAPU32Default : default map [ u32 => String ]; + GETMAPU32Default get(map_get_u32_default): default map [ u32 => String ]; + pub PUBMAPU32Default : default map [ u32 => String ]; + pub GETPUBMAPU32Default get(map_pub_get_u32_default): default map [ u32 => String ]; + MAPU32Required : required map [ u32 => String ]; + GETMAPU32Required get(map_get_u32_required): required map [ u32 => String ]; + pub PUBMAPU32Required : required map [ u32 => String ]; + pub GETPUBMAPU32Required get(map_pub_get_u32_required): required map [ u32 => String ]; + + } + } + + struct TraitImpl {} + + impl Trait for TraitImpl { + type Origin = u32; + } + + const EXPECTED_METADATA: &str = concat!( + r#"{ "prefix": "TestStorage", "items": { "#, + r#""U32": { "description": [ " Hello, this is doc!" ], "modifier": null, "type": "u32" }, "#, + r#""GETU32": { "description": [ ], "modifier": null, "type": "u32" }, "#, + r#""PUBU32": { "description": [ ], "modifier": null, "type": "u32" }, "#, + r#""GETPUBU32": { "description": [ ], "modifier": null, "type": "u32" }, "#, + r#""U32Default": { "description": [ ], "modifier": "default", "type": "u32" }, "#, + r#""GETU32Default": { "description": [ ], "modifier": "default", "type": "u32" }, "#, + r#""PUBU32Default": { "description": [ ], "modifier": "default", "type": "u32" }, "#, + r#""GETPUBU32Default": { "description": [ ], "modifier": "default", "type": "u32" }, "#, + r#""U32Required": { "description": [ ], "modifier": "required", "type": "u32" }, "#, + r#""GETU32Required": { "description": [ ], "modifier": "required", "type": "u32" }, "#, + r#""PUBU32Required": { "description": [ ], "modifier": "required", "type": "u32" }, "#, + r#""GETPUBU32Required": { "description": [ ], "modifier": "required", "type": "u32" }, "#, + r#""MAPU32": { "description": [ ], "modifier": null, "type": { "key": "u32", "value": "String" } }, "#, + r#""GETMAPU32": { "description": [ " Hello, this is doc!", " Hello, this is doc 2!" ], "modifier": null, "type": { "key": "u32", "value": "String" } }, "#, + r#""PUBMAPU32": { "description": [ ], "modifier": null, "type": { "key": "u32", "value": "String" } }, "#, + r#""GETPUBMAPU32": { "description": [ ], "modifier": null, "type": { "key": "u32", "value": "String" } }, "#, + r#""MAPU32Default": { "description": [ ], "modifier": "default", "type": { "key": "u32", "value": "String" } }, "#, + r#""GETMAPU32Default": { "description": [ ], "modifier": "default", "type": { "key": "u32", "value": "String" } }, "#, + r#""PUBMAPU32Default": { "description": [ ], "modifier": "default", "type": { "key": "u32", "value": "String" } }, "#, + r#""GETPUBMAPU32Default": { "description": [ ], "modifier": "default", "type": { "key": "u32", "value": "String" } }, "#, + r#""MAPU32Required": { "description": [ ], "modifier": "required", "type": { "key": "u32", "value": "String" } }, "#, + r#""GETMAPU32Required": { "description": [ ], "modifier": "required", "type": { "key": "u32", "value": "String" } }, "#, + r#""PUBMAPU32Required": { "description": [ ], "modifier": "required", "type": { "key": "u32", "value": "String" } }, "#, + r#""GETPUBMAPU32Required": { "description": [ ], "modifier": "required", "type": { "key": "u32", "value": "String" } }"#, + " } }" + ); + + #[test] + fn store_json_metadata() { + let metadata = Module::::store_json_metadata(); + assert_eq!(EXPECTED_METADATA, metadata); + let _: serde::de::IgnoredAny = + serde_json::from_str(metadata).expect("Is valid json syntax"); + } +} + +#[cfg(test)] +// Do not complain about unused `dispatch` and `dispatch_aux`. +#[allow(dead_code)] +mod test2 { + pub trait Trait { + type Origin; + } + + decl_module! { + pub struct Module for enum Call where origin: T::Origin {} + } + + type PairOf = (T, T); + + decl_storage! { + trait Store for Module as TestStorage { + SingleDef : default u32; + PairDef : default PairOf; + Single : u32; + Pair : (u32, u32); + } + } + + struct TraitImpl {} + + impl Trait for TraitImpl { + type Origin = u32; + } +} diff --git a/runtime/support/src/storage/mod.rs b/runtime/support/src/storage/mod.rs new file mode 100644 index 0000000000000..7b5665bf9bca8 --- /dev/null +++ b/runtime/support/src/storage/mod.rs @@ -0,0 +1,609 @@ +// Copyright 2017 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 . + +//! Stuff to do with the runtime's storage. + +use rstd::prelude::*; +use rstd::borrow::Borrow; +use runtime_io::{self, twox_128}; +use codec::{Codec, Decode, KeyedVec, Input}; + +#[macro_use] +pub mod generator; + +// TODO: consider using blake256 to avoid possible preimage attack. + +struct IncrementalInput<'a> { + key: &'a [u8], + pos: usize, +} + +impl<'a> Input for IncrementalInput<'a> { + fn read(&mut self, into: &mut [u8]) -> usize { + let len = runtime_io::read_storage(self.key, into, self.pos).unwrap_or(0); + let read = ::rstd::cmp::min(len, into.len()); + self.pos += read; + read + } +} + + /// Return the value of the item in storage under `key`, or `None` if there is no explicit entry. +pub fn get(key: &[u8]) -> Option { + let key = twox_128(key); + runtime_io::read_storage(&key[..], &mut [0; 0][..], 0).map(|_| { + let mut input = IncrementalInput { + key: &key[..], + pos: 0, + }; + Decode::decode(&mut input).expect("storage is not null, therefore must be a valid type") + }) +} + +/// Return the value of the item in storage under `key`, or the type's default if there is no +/// explicit entry. +pub fn get_or_default(key: &[u8]) -> T { + get(key).unwrap_or_else(Default::default) +} + +/// Return the value of the item in storage under `key`, or `default_value` if there is no +/// explicit entry. +pub fn get_or(key: &[u8], default_value: T) -> T { + get(key).unwrap_or(default_value) +} + +/// Return the value of the item in storage under `key`, or `default_value()` if there is no +/// explicit entry. +pub fn get_or_else T>(key: &[u8], default_value: F) -> T { + get(key).unwrap_or_else(default_value) +} + +/// Put `value` in storage under `key`. +pub fn put(key: &[u8], value: &T) { + value.using_encoded(|slice| runtime_io::set_storage(&twox_128(key)[..], slice)); +} + +/// Remove `key` from storage, returning its value if it had an explicit entry or `None` otherwise. +pub fn take(key: &[u8]) -> Option { + let r = get(key); + if r.is_some() { + kill(key); + } + r +} + +/// Remove `key` from storage, returning its value, or, if there was no explicit entry in storage, +/// the default for its type. +pub fn take_or_default(key: &[u8]) -> T { + take(key).unwrap_or_else(Default::default) +} + +/// Return the value of the item in storage under `key`, or `default_value` if there is no +/// explicit entry. Ensure there is no explicit entry on return. +pub fn take_or(key: &[u8], default_value: T) -> T { + take(key).unwrap_or(default_value) +} + +/// Return the value of the item in storage under `key`, or `default_value()` if there is no +/// explicit entry. Ensure there is no explicit entry on return. +pub fn take_or_else T>(key: &[u8], default_value: F) -> T { + take(key).unwrap_or_else(default_value) +} + +/// Check to see if `key` has an explicit entry in storage. +pub fn exists(key: &[u8]) -> bool { + runtime_io::exists_storage(&twox_128(key)[..]) +} + +/// Ensure `key` has no explicit entry in storage. +pub fn kill(key: &[u8]) { + runtime_io::clear_storage(&twox_128(key)[..]); +} + +/// Get a Vec of bytes from storage. +pub fn get_raw(key: &[u8]) -> Option> { + runtime_io::storage(&twox_128(key)[..]) +} + +/// Put a raw byte slice into storage. +pub fn put_raw(key: &[u8], value: &[u8]) { + runtime_io::set_storage(&twox_128(key)[..], value) +} + +/// The underlying runtime storage. +pub struct RuntimeStorage; + +impl ::GenericStorage for RuntimeStorage { + fn exists(&self, key: &[u8]) -> bool { + super::storage::exists(key) + } + + /// Load the bytes of a key from storage. Can panic if the type is incorrect. + fn get(&self, key: &[u8]) -> Option { + super::storage::get(key) + } + + /// Put a value in under a key. + fn put(&self, key: &[u8], val: &T) { + super::storage::put(key, val) + } + + /// Remove the bytes of a key from storage. + fn kill(&self, key: &[u8]) { + super::storage::kill(key) + } + + /// Take a value from storage, deleting it after reading. + fn take(&self, key: &[u8]) -> Option { + super::storage::take(key) + } +} + +/// A trait for working with macro-generated storage values under the substrate storage API. +pub trait StorageValue { + /// The type that get/take return. + type Query; + + /// Get the storage key. + fn key() -> &'static [u8]; + + /// Does the value (explicitly) exist in storage? + fn exists() -> bool; + + /// Load the value from the provided storage instance. + fn get() -> Self::Query; + + /// Store a value under this key into the provided storage instance. + fn put>(val: Arg); + + /// Mutate the value + fn mutate(f: F); + + /// Clear the storage value. + fn kill(); + + /// Take a value from storage, removing it afterwards. + fn take() -> Self::Query; +} + +impl StorageValue for U where U: generator::StorageValue { + type Query = U::Query; + + fn key() -> &'static [u8] { + >::key() + } + fn exists() -> bool { + U::exists(&RuntimeStorage) + } + fn get() -> Self::Query { + U::get(&RuntimeStorage) + } + fn put>(val: Arg) { + U::put(val.borrow(), &RuntimeStorage) + } + fn mutate(f: F) { + U::mutate(f, &RuntimeStorage) + } + fn kill() { + U::kill(&RuntimeStorage) + } + fn take() -> Self::Query { + U::take(&RuntimeStorage) + } +} + +/// A strongly-typed list in storage. +pub trait StorageList { + /// Get the prefix key in storage. + fn prefix() -> &'static [u8]; + + /// Get the key used to store the length field. + fn len_key() -> Vec; + + /// Get the storage key used to fetch a value at a given index. + fn key_for(index: u32) -> Vec; + + /// Read out all the items. + fn items() -> Vec; + + /// Set the current set of items. + fn set_items(items: &[T]); + + /// Set the item at the given index. + fn set_item>(index: u32, val: Arg); + + /// Load the value at given index. Returns `None` if the index is out-of-bounds. + fn get(index: u32) -> Option; + + /// Load the length of the list + fn len() -> u32; + + /// Clear the list. + fn clear(); +} + +impl StorageList for U where U: generator::StorageList { + fn prefix() -> &'static [u8] { + >::prefix() + } + + fn len_key() -> Vec { + >::len_key() + } + + fn key_for(index: u32) -> Vec { + >::key_for(index) + } + + fn items() -> Vec { + U::items(&RuntimeStorage) + } + + fn set_items(items: &[T]) { + U::set_items(items, &RuntimeStorage) + } + + fn set_item>(index: u32, val: Arg) { + U::set_item(index, val.borrow(), &RuntimeStorage) + } + + fn get(index: u32) -> Option { + U::get(index, &RuntimeStorage) + } + + fn len() -> u32 { + U::len(&RuntimeStorage) + } + + fn clear() { + U::clear(&RuntimeStorage) + } +} + +/// A strongly-typed map in storage. +pub trait StorageMap { + /// The type that get/take return. + 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>(key: KeyArg) -> Vec; + + /// Does the value (explicitly) exist in storage? + fn exists>(key: KeyArg) -> bool; + + /// Load the value associated with the given key from the map. + fn get>(key: KeyArg) -> Self::Query; + + /// Store a value to be associated with the given key from the map. + fn insert, ValArg: Borrow>(key: KeyArg, val: ValArg); + + /// Remove the value under a key. + fn remove>(key: KeyArg); + + /// Mutate the value under a key. + fn mutate, F: FnOnce(&mut Self::Query)>(key: KeyArg, f: F); + + /// Take the value under a key. + fn take>(key: KeyArg) -> Self::Query; +} + +impl StorageMap for U where U: generator::StorageMap { + type Query = U::Query; + + fn prefix() -> &'static [u8] { + >::prefix() + } + + fn key_for>(key: KeyArg) -> Vec { + >::key_for(key.borrow()) + } + + fn exists>(key: KeyArg) -> bool { + U::exists(key.borrow(), &RuntimeStorage) + } + + fn get>(key: KeyArg) -> Self::Query { + U::get(key.borrow(), &RuntimeStorage) + } + + fn insert, ValArg: Borrow>(key: KeyArg, val: ValArg) { + U::insert(key.borrow(), val.borrow(), &RuntimeStorage) + } + + fn remove>(key: KeyArg) { + U::remove(key.borrow(), &RuntimeStorage) + } + + fn mutate, F: FnOnce(&mut Self::Query)>(key: KeyArg, f: F) { + U::mutate(key.borrow(), f, &RuntimeStorage) + } + + fn take>(key: KeyArg) -> Self::Query { + U::take(key.borrow(), &RuntimeStorage) + } +} + +/// A trait to conveniently store a vector of storable data. +pub trait StorageVec { + type Item: Default + Sized + Codec; + const PREFIX: &'static [u8]; + + /// Get the current set of items. + fn items() -> Vec { + (0..Self::count()).into_iter().map(Self::item).collect() + } + + /// Set the current set of items. + fn set_items(items: I) + where + I: IntoIterator, + T: Borrow, + { + let mut count: u32 = 0; + + for i in items.into_iter() { + put(&count.to_keyed_vec(Self::PREFIX), i.borrow()); + count = count.checked_add(1).expect("exceeded runtime storage capacity"); + } + + Self::set_count(count); + } + + /// Push an item. + fn push(item: &Self::Item) { + let len = Self::count(); + put(&len.to_keyed_vec(Self::PREFIX), item); + Self::set_count(len + 1); + } + + fn set_item(index: u32, item: &Self::Item) { + if index < Self::count() { + put(&index.to_keyed_vec(Self::PREFIX), item); + } + } + + fn clear_item(index: u32) { + if index < Self::count() { + kill(&index.to_keyed_vec(Self::PREFIX)); + } + } + + fn item(index: u32) -> Self::Item { + get_or_default(&index.to_keyed_vec(Self::PREFIX)) + } + + fn set_count(count: u32) { + (count..Self::count()).for_each(Self::clear_item); + put(&b"len".to_keyed_vec(Self::PREFIX), &count); + } + + fn count() -> u32 { + get_or_default(&b"len".to_keyed_vec(Self::PREFIX)) + } +} + +pub mod unhashed { + use rstd::borrow::Borrow; + use super::{runtime_io, Codec, Decode, KeyedVec, Vec, IncrementalInput}; + + /// Return the value of the item in storage under `key`, or `None` if there is no explicit entry. + pub fn get(key: &[u8]) -> Option { + runtime_io::read_storage(key, &mut [0; 0][..], 0).map(|_| { + let mut input = IncrementalInput { + key, + pos: 0, + }; + Decode::decode(&mut input).expect("storage is not null, therefore must be a valid type") + }) + } + + /// Return the value of the item in storage under `key`, or the type's default if there is no + /// explicit entry. + pub fn get_or_default(key: &[u8]) -> T { + get(key).unwrap_or_else(Default::default) + } + + /// Return the value of the item in storage under `key`, or `default_value` if there is no + /// explicit entry. + pub fn get_or(key: &[u8], default_value: T) -> T { + get(key).unwrap_or(default_value) + } + + /// Return the value of the item in storage under `key`, or `default_value()` if there is no + /// explicit entry. + pub fn get_or_else T>(key: &[u8], default_value: F) -> T { + get(key).unwrap_or_else(default_value) + } + + /// Put `value` in storage under `key`. + pub fn put(key: &[u8], value: &T) { + value.using_encoded(|slice| runtime_io::set_storage(key, slice)); + } + + /// Remove `key` from storage, returning its value if it had an explicit entry or `None` otherwise. + pub fn take(key: &[u8]) -> Option { + let r = get(key); + if r.is_some() { + kill(key); + } + r + } + + /// Remove `key` from storage, returning its value, or, if there was no explicit entry in storage, + /// the default for its type. + pub fn take_or_default(key: &[u8]) -> T { + take(key).unwrap_or_else(Default::default) + } + + /// Return the value of the item in storage under `key`, or `default_value` if there is no + /// explicit entry. Ensure there is no explicit entry on return. + pub fn take_or(key: &[u8], default_value: T) -> T { + take(key).unwrap_or(default_value) + } + + /// Return the value of the item in storage under `key`, or `default_value()` if there is no + /// explicit entry. Ensure there is no explicit entry on return. + pub fn take_or_else T>(key: &[u8], default_value: F) -> T { + take(key).unwrap_or_else(default_value) + } + + /// Check to see if `key` has an explicit entry in storage. + pub fn exists(key: &[u8]) -> bool { + runtime_io::read_storage(key, &mut [0;0][..], 0).is_some() + } + + /// Ensure `key` has no explicit entry in storage. + pub fn kill(key: &[u8]) { + runtime_io::clear_storage(key); + } + + /// Ensure keys with the given `prefix` have no entries in storage. + pub fn kill_prefix(prefix: &[u8]) { + runtime_io::clear_prefix(prefix); + } + + /// Get a Vec of bytes from storage. + pub fn get_raw(key: &[u8]) -> Option> { + runtime_io::storage(key) + } + + /// Put a raw byte slice into storage. + pub fn put_raw(key: &[u8], value: &[u8]) { + runtime_io::set_storage(key, value) + } + + /// A trait to conveniently store a vector of storable data. + pub trait StorageVec { + type Item: Default + Sized + Codec; + const PREFIX: &'static [u8]; + + /// Get the current set of items. + fn items() -> Vec { + (0..Self::count()).into_iter().map(Self::item).collect() + } + + /// Set the current set of items. + fn set_items(items: I) + where + I: IntoIterator, + T: Borrow, + { + let mut count: u32 = 0; + + for i in items.into_iter() { + put(&count.to_keyed_vec(Self::PREFIX), i.borrow()); + count = count.checked_add(1).expect("exceeded runtime storage capacity"); + } + + Self::set_count(count); + } + + fn set_item(index: u32, item: &Self::Item) { + if index < Self::count() { + put(&index.to_keyed_vec(Self::PREFIX), item); + } + } + + fn clear_item(index: u32) { + if index < Self::count() { + kill(&index.to_keyed_vec(Self::PREFIX)); + } + } + + fn item(index: u32) -> Self::Item { + get_or_default(&index.to_keyed_vec(Self::PREFIX)) + } + + fn set_count(count: u32) { + (count..Self::count()).for_each(Self::clear_item); + put(&b"len".to_keyed_vec(Self::PREFIX), &count); + } + + fn count() -> u32 { + get_or_default(&b"len".to_keyed_vec(Self::PREFIX)) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use runtime_io::{twox_128, TestExternalities, with_externalities}; + + #[test] + fn integers_can_be_stored() { + let mut t = TestExternalities::new(); + with_externalities(&mut t, || { + let x = 69u32; + put(b":test", &x); + let y: u32 = get(b":test").unwrap(); + assert_eq!(x, y); + }); + with_externalities(&mut t, || { + let x = 69426942i64; + put(b":test", &x); + let y: i64 = get(b":test").unwrap(); + assert_eq!(x, y); + }); + } + + #[test] + fn bools_can_be_stored() { + let mut t = TestExternalities::new(); + with_externalities(&mut t, || { + let x = true; + put(b":test", &x); + let y: bool = get(b":test").unwrap(); + assert_eq!(x, y); + }); + + with_externalities(&mut t, || { + let x = false; + put(b":test", &x); + let y: bool = get(b":test").unwrap(); + assert_eq!(x, y); + }); + } + + #[test] + fn vecs_can_be_retrieved() { + let mut t = TestExternalities::new(); + with_externalities(&mut t, || { + runtime_io::set_storage(&twox_128(b":test"), b"\x0b\0\0\0Hello world"); + let x = b"Hello world".to_vec(); + let y = get::>(b":test").unwrap(); + assert_eq!(x, y); + + }); + } + + #[test] + fn vecs_can_be_stored() { + let mut t = TestExternalities::new(); + let x = b"Hello world".to_vec(); + + with_externalities(&mut t, || { + put(b":test", &x); + }); + + with_externalities(&mut t, || { + let y: Vec = get(b":test").unwrap(); + assert_eq!(x, y); + }); + } +} diff --git a/runtime/system/Cargo.toml b/runtime/system/Cargo.toml new file mode 100644 index 0000000000000..2991c2a00fe08 --- /dev/null +++ b/runtime/system/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "substrate-runtime-system" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +hex-literal = "0.1.0" +serde = { version = "1.0", default_features = false } +serde_derive = { version = "1.0", optional = true } +safe-mix = { version = "1.0", default_features = false} +substrate-codec = { path = "../../core/codec", default_features = false } +substrate-codec-derive = { path = "../../core/codec/derive", default_features = false } +substrate-primitives = { path = "../../core/primitives", default_features = false } +substrate-runtime-std = { path = "../../core/runtime-std", default_features = false } +substrate-runtime-io = { path = "../../core/runtime-io", default_features = false } +substrate-runtime-support = { path = "../support", default_features = false } +substrate-runtime-primitives = { path = "../primitives", default_features = false } + +[features] +default = ["std"] +std = [ + "serde/std", + "serde_derive", + "safe-mix/std", + "substrate-codec/std", + "substrate-codec-derive/std", + "substrate-primitives/std", + "substrate-runtime-std/std", + "substrate-runtime-io/std", + "substrate-runtime-support/std", + "substrate-runtime-primitives/std", +] diff --git a/runtime/system/src/lib.rs b/runtime/system/src/lib.rs new file mode 100644 index 0000000000000..ae8ceff6a6047 --- /dev/null +++ b/runtime/system/src/lib.rs @@ -0,0 +1,426 @@ +// Copyright 2017 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 . + +//! System manager: Handles lowest level stuff like depositing logs, basic set up and take down of +//! temporary storage entries, access to old block hashes. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(any(feature = "std", test))] +extern crate substrate_primitives; + +#[cfg_attr(any(feature = "std", test), macro_use)] +extern crate substrate_runtime_std as rstd; + +#[macro_use] +extern crate substrate_runtime_support as runtime_support; + +#[cfg(feature = "std")] +extern crate serde; + +#[cfg(feature = "std")] +#[macro_use] +extern crate serde_derive; + +#[macro_use] +extern crate substrate_codec_derive; + +extern crate substrate_codec as codec; +extern crate substrate_runtime_io as runtime_io; +extern crate substrate_runtime_primitives as primitives; +extern crate safe_mix; + +use rstd::prelude::*; +use primitives::traits::{self, CheckEqual, SimpleArithmetic, SimpleBitOps, Zero, One, Bounded, + Hash, Member, MaybeDisplay, EnsureOrigin}; +use runtime_support::{StorageValue, StorageMap, Parameter}; +use safe_mix::TripletMix; + +#[cfg(any(feature = "std", test))] +use rstd::marker::PhantomData; +#[cfg(any(feature = "std", test))] +use codec::Encode; + +#[cfg(any(feature = "std", test))] +use runtime_io::{twox_128, TestExternalities, Blake2Hasher}; + +/// Compute the extrinsics root of a list of extrinsics. +pub fn extrinsics_root(extrinsics: &[E]) -> H::Output { + extrinsics_data_root::(extrinsics.iter().map(codec::Encode::encode).collect()) +} + +/// Compute the extrinsics root of a list of extrinsics. +pub fn extrinsics_data_root(xts: Vec>) -> H::Output { + let xts = xts.iter().map(Vec::as_slice).collect::>(); + H::enumerated_trie_root(&xts) +} + +pub trait Trait: Eq + Clone { + type Origin: Into>> + From>; + type Index: Parameter + Member + Default + MaybeDisplay + SimpleArithmetic + Copy; + type BlockNumber: Parameter + Member + MaybeDisplay + SimpleArithmetic + Default + Bounded + Copy + rstd::hash::Hash; + type Hash: Parameter + Member + MaybeDisplay + SimpleBitOps + Default + Copy + CheckEqual + rstd::hash::Hash + AsRef<[u8]>; + type Hashing: Hash; + type Digest: Parameter + Member + Default + traits::Digest; + type AccountId: Parameter + Member + MaybeDisplay + Ord + Default; + type Header: Parameter + traits::Header< + Number = Self::BlockNumber, + Hash = Self::Hash, + Digest = Self::Digest + >; + type Event: Parameter + Member + From; +} + +pub type DigestItemOf = <::Digest as traits::Digest>::Item; + +decl_module! { + pub struct Module for enum Call where origin: T::Origin {} +} + +/// A phase of a block's execution. +#[derive(Encode, Decode)] +#[cfg_attr(feature = "std", derive(Serialize, PartialEq, Eq, Clone, Debug))] +pub enum Phase { + /// Applying an extrinsic. + ApplyExtrinsic(u32), + /// The end. + Finalization, +} + +/// Record of an event happening. +#[derive(Encode, Decode)] +#[cfg_attr(feature = "std", derive(Serialize, PartialEq, Eq, Clone, Debug))] +pub struct EventRecord { + /// The phase of the block it happened in. + pub phase: Phase, + /// The event itself. + pub event: E, +} + +/// Event for the system module. +decl_event!( + pub enum Event { + /// An extrinsic completed successfully. + ExtrinsicSuccess, + /// An extrinsic failed. + ExtrinsicFailed, + } +); + +/// Origin for the system module. +#[derive(PartialEq, Eq, Clone)] +#[cfg_attr(feature = "std", derive(Debug))] +pub enum RawOrigin { + /// The system itself ordained this dispatch to happen: this is the highest privilege level. + Root, + /// It is signed by some public key and we provide the AccountId. + Signed(AccountId), + /// It is signed by nobody but included and agreed upon by the validators anyway: it's "inherently" true. + Inherent, +} + +impl From> for RawOrigin { + fn from(s: Option) -> RawOrigin { + match s { + Some(who) => RawOrigin::Signed(who), + None => RawOrigin::Inherent, + } + } +} + +/// Exposed trait-generic origin type. +pub type Origin = RawOrigin<::AccountId>; + +decl_storage! { + trait Store for Module as System { + + pub AccountNonce get(account_nonce): default map [ T::AccountId => T::Index ]; + + ExtrinsicCount: u32; + pub BlockHash get(block_hash): required map [ T::BlockNumber => T::Hash ]; + pub ExtrinsicIndex get(extrinsic_index): u32; + ExtrinsicData get(extrinsic_data): required map [ u32 => Vec ]; + RandomSeed get(random_seed): required T::Hash; + /// The current block number being processed. Set by `execute_block`. + Number get(block_number): required T::BlockNumber; + ParentHash get(parent_hash): required T::Hash; + ExtrinsicsRoot get(extrinsics_root): required T::Hash; + Digest get(digest): default T::Digest; + + Events get(events): default Vec>; + } +} + +pub struct EnsureRoot(::rstd::marker::PhantomData); +impl>>, AccountId> EnsureOrigin for EnsureRoot { + type Success = (); + fn ensure_origin(o: O) -> Result { + ensure_root(o) + } +} + +/// Ensure that the origin `o` represents a signed extrinsic (i.e. transaction). +/// Returns `Ok` with the account that signed the extrinsic or an `Err` otherwise. +pub fn ensure_signed(o: OuterOrigin) -> Result + where OuterOrigin: Into>> +{ + match o.into() { + Some(RawOrigin::Signed(t)) => Ok(t), + _ => Err("bad origin: expected to be a signed origin"), + } +} + +/// Ensure that the origin `o` represents the root. Returns `Ok` or an `Err` otherwise. +pub fn ensure_root(o: OuterOrigin) -> Result<(), &'static str> + where OuterOrigin: Into>> +{ + match o.into() { + Some(RawOrigin::Root) => Ok(()), + _ => Err("bad origin: expected to be a root origin"), + } +} + +/// Ensure that the origin `o` represents an unsigned extrinsic. Returns `Ok` or an `Err` otherwise. +pub fn ensure_inherent(o: OuterOrigin) -> Result<(), &'static str> + where OuterOrigin: Into>> +{ + match o.into() { + Some(RawOrigin::Inherent) => Ok(()), + _ => Err("bad origin: expected to be an inherent origin"), + } +} + +impl Module { + /// Start the execution of a particular block. + pub fn initialise(number: &T::BlockNumber, parent_hash: &T::Hash, txs_root: &T::Hash) { + // populate environment. + >::put(number); + >::put(parent_hash); + >::insert(*number - One::one(), parent_hash); + >::put(txs_root); + >::put(Self::calculate_random()); + >::put(0u32); + >::kill(); + } + + /// Remove temporary "environment" entries in storage. + pub fn finalise() -> T::Header { + >::kill(); + >::kill(); + + let number = >::take(); + let parent_hash = >::take(); + let digest = >::take(); + let extrinsics_root = >::take(); + let storage_root = T::Hashing::storage_root(); + + // > stays to be inspected by the client. + + ::new(number, extrinsics_root, storage_root, parent_hash, digest) + } + + /// Deposits a log and ensures it matches the blocks log data. + pub fn deposit_log(item: ::Item) { + let mut l = >::get(); + traits::Digest::push(&mut l, item); + >::put(l); + } + + /// Deposits an event onto this block's event record. + pub fn deposit_event(event: T::Event) { + let phase = >::get().map_or(Phase::Finalization, |c| Phase::ApplyExtrinsic(c)); + let mut events = Self::events(); + events.push(EventRecord { phase, event }); + >::put(events); + } + + /// Calculate the current block's random seed. + fn calculate_random() -> T::Hash { + assert!(Self::block_number() > Zero::zero(), "Block number may never be zero"); + (0..81) + .scan( + Self::block_number() - One::one(), + |c, _| { if *c > Zero::zero() { *c -= One::one() }; Some(*c) + }) + .map(Self::block_hash) + .triplet_mix() + } + + /// Get the basic externalities for this module, useful for tests. + #[cfg(any(feature = "std", test))] + pub fn externalities() -> TestExternalities { + map![ + twox_128(&>::key_for(T::BlockNumber::zero())).to_vec() => [69u8; 32].encode(), // TODO: replace with Hash::default().encode + twox_128(>::key()).to_vec() => T::BlockNumber::one().encode(), + twox_128(>::key()).to_vec() => [69u8; 32].encode(), // TODO: replace with Hash::default().encode + twox_128(>::key()).to_vec() => T::Hash::default().encode() + ] + } + + /// Set the block number to something in particular. Can be used as an alternative to + /// `initialise` for tests that don't need to bother with the other environment entries. + #[cfg(any(feature = "std", test))] + pub fn set_block_number(n: T::BlockNumber) { + >::put(n); + } + + /// Set the parent hash number to something in particular. Can be used as an alternative to + /// `initialise` for tests that don't need to bother with the other environment entries. + #[cfg(any(feature = "std", test))] + pub fn set_parent_hash(n: T::Hash) { + >::put(n); + } + + /// Set the random seed to something in particular. Can be used as an alternative to + /// `initialise` for tests that don't need to bother with the other environment entries. + #[cfg(any(feature = "std", test))] + pub fn set_random_seed(seed: T::Hash) { + >::put(seed); + } + + /// Increment a particular account's nonce by 1. + pub fn inc_account_nonce(who: &T::AccountId) { + >::insert(who, Self::account_nonce(who) + T::Index::one()); + } + + /// Note what the extrinsic data of the current extrinsic index is. If this is called, then + /// ensure `derive_extrinsics` is also called before block-building is completed. + pub fn note_extrinsic(encoded_xt: Vec) { + >::insert(>::get().unwrap_or_default(), encoded_xt); + } + + /// To be called immediately after an extrinsic has been applied. + pub fn note_applied_extrinsic(r: &Result<(), &'static str>) { + Self::deposit_event(match r { + Ok(_) => Event::ExtrinsicSuccess, + Err(_) => Event::ExtrinsicFailed, + }.into()); + >::put(>::get().unwrap_or_default() + 1u32); + } + + /// To be called immediately after `note_applied_extrinsic` of the last extrinsic of the block + /// has been called. + pub fn note_finished_extrinsics() { + >::put(>::get().unwrap_or_default()); + >::kill(); + } + + /// Remove all extrinsics data and save the extrinsics trie root. + pub fn derive_extrinsics() { + let extrinsics = (0..>::get().unwrap_or_default()).map(>::take).collect(); + let xts_root = extrinsics_data_root::(extrinsics); + >::put(xts_root); + } +} + +#[cfg(any(feature = "std", test))] +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +#[serde(deny_unknown_fields)] +pub struct GenesisConfig(PhantomData); + +#[cfg(any(feature = "std", test))] +impl Default for GenesisConfig { + fn default() -> Self { + GenesisConfig(PhantomData) + } +} + +#[cfg(any(feature = "std", test))] +impl primitives::BuildStorage for GenesisConfig +{ + fn build_storage(self) -> Result { + use codec::Encode; + + Ok(map![ + Self::hash(&>::key_for(T::BlockNumber::zero())).to_vec() => [69u8; 32].encode(), + Self::hash(>::key()).to_vec() => 1u64.encode(), + Self::hash(>::key()).to_vec() => [69u8; 32].encode(), + Self::hash(>::key()).to_vec() => [0u8; 32].encode(), + Self::hash(>::key()).to_vec() => [0u8; 4].encode() + ]) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use runtime_io::with_externalities; + use substrate_primitives::H256; + use primitives::BuildStorage; + use primitives::traits::BlakeTwo256; + use primitives::testing::{Digest, Header}; + + impl_outer_origin!{ + pub enum Origin for Test where system = super {} + } + + #[derive(Clone, Eq, PartialEq)] + pub struct Test; + impl Trait for Test { + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type Digest = Digest; + type AccountId = u64; + type Header = Header; + type Event = u16; + } + + impl From for u16 { + fn from(e: Event) -> u16 { + match e { + Event::ExtrinsicSuccess => 100, + Event::ExtrinsicFailed => 101, + } + } + } + + type System = Module; + + + + fn new_test_ext() -> runtime_io::TestExternalities { + GenesisConfig::::default().build_storage().unwrap().into() + } + + #[test] + fn deposit_event_should_work() { + with_externalities(&mut new_test_ext(), || { + System::initialise(&1, &[0u8; 32].into(), &[0u8; 32].into()); + System::note_finished_extrinsics(); + System::deposit_event(1u16); + System::finalise(); + assert_eq!(System::events(), vec![EventRecord { phase: Phase::Finalization, event: 1u16 }]); + + System::initialise(&2, &[0u8; 32].into(), &[0u8; 32].into()); + System::deposit_event(42u16); + System::note_applied_extrinsic(&Ok(())); + System::note_applied_extrinsic(&Err("")); + System::note_finished_extrinsics(); + System::deposit_event(3u16); + System::finalise(); + assert_eq!(System::events(), vec![ + EventRecord { phase: Phase::ApplyExtrinsic(0), event: 42u16 }, + EventRecord { phase: Phase::ApplyExtrinsic(0), event: 100u16 }, + EventRecord { phase: Phase::ApplyExtrinsic(1), event: 101u16 }, + EventRecord { phase: Phase::Finalization, event: 3u16 } + ]); + }); + } +} diff --git a/runtime/timestamp/Cargo.toml b/runtime/timestamp/Cargo.toml new file mode 100644 index 0000000000000..b8c42308a79ba --- /dev/null +++ b/runtime/timestamp/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "substrate-runtime-timestamp" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +hex-literal = "0.1.0" +serde = { version = "1.0", default_features = false } +serde_derive = { version = "1.0", optional = true } +substrate-runtime-std = { path = "../../core/runtime-std", default_features = false } +substrate-runtime-io = { path = "../../core/runtime-io", default_features = false } +substrate-runtime-support = { path = "../support", default_features = false } +substrate-runtime-primitives = { path = "../primitives", default_features = false } +substrate-codec = { path = "../../core/codec", default_features = false } +substrate-primitives = { path = "../../core/primitives", default_features = false } +substrate-runtime-system = { path = "../system", default_features = false } +substrate-runtime-consensus = { path = "../consensus", default_features = false } + +[features] +default = ["std"] +std = [ + "substrate-runtime-std/std", + "substrate-runtime-io/std", + "substrate-runtime-support/std", + "substrate-runtime-primitives/std", + "substrate-runtime-consensus/std", + "serde/std", + "serde_derive", + "substrate-codec/std", + "substrate-primitives/std", + "substrate-runtime-system/std", +] diff --git a/runtime/timestamp/src/lib.rs b/runtime/timestamp/src/lib.rs new file mode 100644 index 0000000000000..b586dae19b914 --- /dev/null +++ b/runtime/timestamp/src/lib.rs @@ -0,0 +1,210 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Timestamp manager: just handles the current timestamp. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg_attr(any(feature = "std", test), macro_use)] +extern crate substrate_runtime_std as rstd; + +#[macro_use] +extern crate substrate_runtime_support as runtime_support; + +#[cfg(any(feature = "std", test))] +extern crate substrate_runtime_io as runtime_io; + +#[cfg(feature = "std")] +#[macro_use] +extern crate serde_derive; + +#[cfg(test)] +extern crate substrate_primitives; +extern crate substrate_runtime_primitives as runtime_primitives; +extern crate substrate_runtime_system as system; +extern crate substrate_runtime_consensus as consensus; +extern crate substrate_codec as codec; + +use runtime_support::{StorageValue, Parameter}; +use runtime_support::dispatch::Result; +use runtime_primitives::traits::{OnFinalise, SimpleArithmetic, As, Zero}; +use system::ensure_inherent; + +pub trait Trait: consensus::Trait + system::Trait { + // the position of the required timestamp-set extrinsic. + const TIMESTAMP_SET_POSITION: u32; + + type Moment: Parameter + Default + SimpleArithmetic + As; +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + fn set(origin, now: T::Moment) -> Result; + } +} + +decl_storage! { + trait Store for Module as Timestamp { + pub Now get(now): required T::Moment; + /// The minimum (and advised) period between blocks. + pub BlockPeriod get(block_period): required T::Moment; + + /// Did the timestamp get updated in this block? + DidUpdate: default bool; + } +} + +impl Module { + pub fn get() -> T::Moment { + Self::now() + } + + /// Set the current time. + fn set(origin: T::Origin, now: T::Moment) -> Result { + ensure_inherent(origin)?; + assert!(!::DidUpdate::exists(), "Timestamp must be updated only once in the block"); + assert!( + >::extrinsic_index() == Some(T::TIMESTAMP_SET_POSITION), + "Timestamp extrinsic must be at position {} in the block", + T::TIMESTAMP_SET_POSITION + ); + assert!( + Self::now().is_zero() || now >= Self::now() + Self::block_period(), + "Timestamp but increment by at least between sequential blocks" + ); + ::Now::put(now); + ::DidUpdate::put(true); + Ok(()) + } + + /// Set the timestamp to something in particular. Only used for tests. + #[cfg(any(feature = "std", test))] + pub fn set_timestamp(now: T::Moment) { + ::Now::put(now); + } +} + +impl OnFinalise for Module { + fn on_finalise(_n: T::BlockNumber) { + assert!(::DidUpdate::take(), "Timestamp must be updated once in the block"); + } +} + +#[cfg(any(feature = "std", test))] +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +#[serde(deny_unknown_fields)] +pub struct GenesisConfig { + pub period: T::Moment, +} + +#[cfg(any(feature = "std", test))] +impl Default for GenesisConfig { + fn default() -> Self { + GenesisConfig { + period: T::Moment::sa(5), + } + } +} + +#[cfg(any(feature = "std", test))] +impl runtime_primitives::BuildStorage for GenesisConfig +{ + fn build_storage(self) -> ::std::result::Result { + use codec::Encode; + Ok(map![ + Self::hash(>::key()).to_vec() => self.period.encode(), + Self::hash(>::key()).to_vec() => T::Moment::sa(0).encode() + ]) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use runtime_io::with_externalities; + use substrate_primitives::H256; + use runtime_primitives::BuildStorage; + use runtime_primitives::traits::{BlakeTwo256}; + use runtime_primitives::testing::{Digest, Header}; + + impl_outer_origin! { + pub enum Origin for Test {} + } + + #[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 Digest = Digest; + type AccountId = u64; + type Header = Header; + type Event = (); + } + impl consensus::Trait for Test { + const NOTE_OFFLINE_POSITION: u32 = 1; + type Log = u64; + type SessionKey = u64; + type OnOfflineValidator = (); + } + impl Trait for Test { + const TIMESTAMP_SET_POSITION: u32 = 0; + type Moment = u64; + } + type Timestamp = Module; + + #[test] + fn timestamp_works() { + let mut t = system::GenesisConfig::::default().build_storage().unwrap(); + t.extend(GenesisConfig:: { period: 0 }.build_storage().unwrap()); + let mut t = runtime_io::TestExternalities::from(t); + with_externalities(&mut t, || { + Timestamp::set_timestamp(42); + assert_ok!(Timestamp::dispatch(Call::set(69), Origin::INHERENT)); + assert_eq!(Timestamp::now(), 69); + }); + } + + #[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(); + t.extend(GenesisConfig:: { period: 5 }.build_storage().unwrap()); + let mut t = runtime_io::TestExternalities::from(t); + with_externalities(&mut t, || { + Timestamp::set_timestamp(42); + assert_ok!(Timestamp::dispatch(Call::set(69), Origin::INHERENT)); + let _ = Timestamp::dispatch(Call::set(70), Origin::INHERENT); + }); + } + + #[test] + #[should_panic(expected = "Timestamp but increment by at least between sequential blocks")] + fn block_period_is_enforced() { + let mut t = system::GenesisConfig::::default().build_storage().unwrap(); + t.extend(GenesisConfig:: { period: 5 }.build_storage().unwrap()); + let mut t = runtime_io::TestExternalities::from(t); + with_externalities(&mut t, || { + Timestamp::set_timestamp(42); + let _ = Timestamp::dispatch(Call::set(46), Origin::INHERENT); + }); + } +} diff --git a/runtime/treasury/Cargo.toml b/runtime/treasury/Cargo.toml new file mode 100644 index 0000000000000..221439917b928 --- /dev/null +++ b/runtime/treasury/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "substrate-runtime-treasury" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +hex-literal = "0.1.0" +serde = { version = "1.0", default_features = false } +serde_derive = { version = "1.0", optional = true } +substrate-runtime-std = { path = "../../core/runtime-std", default_features = false } +substrate-runtime-io = { path = "../../core/runtime-io", default_features = false } +substrate-runtime-support = { path = "../support", default_features = false } +substrate-runtime-primitives = { path = "../primitives", default_features = false } +substrate-codec = { path = "../../core/codec", default_features = false } +substrate-codec-derive = { path = "../../core/codec/derive", default_features = false } +substrate-primitives = { path = "../../core/primitives", default_features = false } +substrate-runtime-system = { path = "../system", default_features = false } +substrate-runtime-balances = { path = "../balances", default_features = false } + +[features] +default = ["std"] +std = [ + "substrate-runtime-std/std", + "substrate-runtime-io/std", + "substrate-runtime-support/std", + "substrate-runtime-primitives/std", + "substrate-runtime-balances/std", + "serde/std", + "serde_derive", + "substrate-codec/std", + "substrate-codec-derive/std", + "substrate-primitives/std", + "substrate-runtime-system/std", +] diff --git a/runtime/treasury/src/lib.rs b/runtime/treasury/src/lib.rs new file mode 100644 index 0000000000000..1edf33a563066 --- /dev/null +++ b/runtime/treasury/src/lib.rs @@ -0,0 +1,546 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! The Treasury: Keeps account of the taxed cash and handles its deployment. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg_attr(feature = "std", macro_use)] +extern crate substrate_runtime_std as rstd; + +#[macro_use] +extern crate substrate_runtime_support as runtime_support; + +#[cfg(feature = "std")] +extern crate substrate_runtime_io as runtime_io; + +#[cfg(feature = "std")] +#[macro_use] +extern crate serde_derive; + +#[macro_use] +extern crate substrate_codec_derive; + +extern crate substrate_codec as codec; +#[cfg(test)] +extern crate substrate_primitives; +extern crate substrate_runtime_primitives as runtime_primitives; +extern crate substrate_runtime_system as system; +extern crate substrate_runtime_balances as balances; + +use rstd::prelude::*; +use runtime_support::{StorageValue, StorageMap}; +use runtime_support::dispatch::Result; +use runtime_primitives::{Permill, traits::{OnFinalise, Zero, EnsureOrigin}}; +use balances::OnDilution; +use system::{ensure_signed, ensure_root}; + +/// 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 +/// should be added to our implied traits list. +/// +/// `system::Trait` should always be included in our implied traits. +pub trait Trait: balances::Trait { + /// Origin from which approvals must come. + type ApproveOrigin: EnsureOrigin; + + /// Origin from which rejections must come. + type RejectOrigin: EnsureOrigin; + + /// The overarching event type. + type Event: From> + Into<::Event>; +} + +type ProposalIndex = u32; + +// The module declaration. This states the entry points that we handle. The +// macro takes care of the marshalling of arguments and dispatch. +decl_module! { + // Simple declaration of the `Module` type. Lets the macro know what its working on. + pub struct Module for enum Call where origin: T::Origin { + // 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 + // proposal is awarded. + fn propose_spend(origin, value: T::Balance, beneficiary: T::AccountId) -> Result; + + // Set the balance of funds available to spend. + fn set_pot(origin, new_pot: T::Balance) -> Result; + + // (Re-)configure this module. + fn configure(origin, proposal_bond: Permill, proposal_bond_minimum: T::Balance, spend_period: T::BlockNumber, burn: Permill) -> Result; + + // Reject a proposed spend. The original deposit will be slashed. + fn reject_proposal(origin, roposal_id: ProposalIndex) -> Result; + + // Approve a proposal. At a later time, the proposal will be allocated to the beneficiary + // and the original deposit will be returned. + fn approve_proposal(origin, proposal_id: ProposalIndex) -> Result; + } +} + +/// A spending proposal. +#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] +#[derive(Encode, Decode, Clone, PartialEq, Eq)] +pub struct Proposal { + proposer: AccountId, + value: Balance, + beneficiary: AccountId, + bond: Balance, +} + +decl_storage! { + trait Store for Module as Treasury { + // Config... + + /// Proportion of funds that should be bonded in order to place a proposal. An accepted + /// proposal gets these back. A rejected proposal doesn't. + ProposalBond get(proposal_bond): required Permill; + + /// Minimum amount of funds that should be placed ina deposit for making a proposal. + ProposalBondMinimum get(proposal_bond_minimum): required T::Balance; + + /// Period between successive spends. + SpendPeriod get(spend_period): required T::BlockNumber; + + /// Percentage of spare funds (if any) that are burnt per spend period. + Burn get(burn): required Permill; + + // State... + + /// Total funds available to this module for spending. + Pot get(pot): default T::Balance; + + /// Number of proposals that have been made. + ProposalCount get(proposal_count): default ProposalIndex; + + /// Proposals that have been made. + Proposals get(proposals): map [ ProposalIndex => Proposal ]; + + /// Proposal indices that have been approved but not yet awarded. + Approvals get(approvals): default Vec; + } +} + +/// An event in this module. +decl_event!( + pub enum Event with RawEvent + where ::Balance, ::AccountId + { + /// New proposal. + Proposed(ProposalIndex), + /// We have ended a spend period and will now allocate funds. + Spending(Balance), + /// Some funds have been allocated. + Awarded(ProposalIndex, Balance, AccountId), + /// Some of our funds have been burnt. + Burnt(Balance), + /// Spending has finished; this is the amount that rolls over until next spend. + Rollover(Balance), + } +); + +impl Module { + /// Deposit one of this module's events. + fn deposit_event(event: Event) { + >::deposit_event(::Event::from(event).into()); + } + + // Implement Calls and add public immutables and private mutables. + + fn propose_spend(origin: T::Origin, value: T::Balance, beneficiary: T::AccountId) -> Result { + let proposer = ensure_signed(origin)?; + + let bond = Self::calculate_bond(value); + >::reserve(&proposer, bond) + .map_err(|_| "Proposer's balance too low")?; + + let c = Self::proposal_count(); + >::put(c + 1); + >::insert(c, Proposal { proposer, value, beneficiary, bond }); + + Self::deposit_event(RawEvent::Proposed(c)); + + Ok(()) + } + + fn reject_proposal(origin: T::Origin, proposal_id: ProposalIndex) -> Result { + T::RejectOrigin::ensure_origin(origin)?; + + let proposal = >::take(proposal_id).ok_or("No proposal at that index")?; + + let value = proposal.bond; + let _ = >::slash_reserved(&proposal.proposer, value); + + Ok(()) + } + + fn approve_proposal(origin: T::Origin, proposal_id: ProposalIndex) -> Result { + T::ApproveOrigin::ensure_origin(origin)?; + + ensure!(>::exists(proposal_id), "No proposal at that index"); + + >::mutate(|v| v.push(proposal_id)); + + Ok(()) + } + + fn set_pot(origin: T::Origin, new_pot: T::Balance) -> Result { + ensure_root(origin)?; + // Put the new value into storage. + >::put(new_pot); + + // All good. + Ok(()) + } + + fn configure( + origin: T::Origin, + proposal_bond: Permill, + proposal_bond_minimum: T::Balance, + spend_period: T::BlockNumber, + burn: Permill + ) -> Result { + ensure_root(origin)?; + >::put(proposal_bond); + >::put(proposal_bond_minimum); + >::put(spend_period); + >::put(burn); + Ok(()) + } + + /// The needed bond for a proposal whose spend is `value`. + fn calculate_bond(value: T::Balance) -> T::Balance { + Self::proposal_bond_minimum().max(Self::proposal_bond().times(value)) + } + + // Spend some money! + fn spend_funds() { + let mut budget_remaining = Self::pot(); + Self::deposit_event(RawEvent::Spending(budget_remaining)); + + let mut missed_any = false; + let remaining_approvals: Vec<_> = >::get().into_iter().filter(|&index| { + // Should always be true, but shouldn't panic if false or we're screwed. + if let Some(p) = Self::proposals(index) { + if p.value <= budget_remaining { + budget_remaining -= p.value; + >::remove(index); + + // return their deposit. + let _ = >::unreserve(&p.proposer, p.bond); + + // provide the allocation. + >::increase_free_balance_creating(&p.beneficiary, p.value); + + Self::deposit_event(RawEvent::Awarded(index, p.value, p.beneficiary)); + false + } else { + missed_any = true; + true + } + } else { + false + } + }).collect(); + >::put(remaining_approvals); + + if !missed_any { + // burn some proportion of the remaining budget if we run a surplus. + let burn = Self::burn().times(budget_remaining); + budget_remaining -= burn; + Self::deposit_event(RawEvent::Burnt(burn)) + } + + Self::deposit_event(RawEvent::Rollover(budget_remaining)); + + >::put(budget_remaining); + } +} + +impl OnDilution for Module { + fn on_dilution(minted: T::Balance, portion: T::Balance) { + // Mint extra funds for the treasury to keep the ratio of portion to total_issuance equal + // pre dilution and post-dilution. + if !minted.is_zero() && !portion.is_zero() { + let total_issuance = >::total_issuance(); + let funding = (total_issuance - portion) / portion * minted; + >::mutate(|x| *x += funding); + } + } +} + +impl OnFinalise for Module { + fn on_finalise(n: T::BlockNumber) { + // Check to see if we should spend some funds! + if (n % Self::spend_period()).is_zero() { + Self::spend_funds(); + } + } +} + +#[cfg(feature = "std")] +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +#[serde(deny_unknown_fields)] +/// The genesis block configuration type. This is a simple default-capable struct that +/// contains any fields with which this module can be configured at genesis time. +pub struct GenesisConfig { + pub proposal_bond: Permill, + pub proposal_bond_minimum: T::Balance, + pub spend_period: T::BlockNumber, + pub burn: Permill, +} + +#[cfg(feature = "std")] +impl Default for GenesisConfig { + fn default() -> Self { + GenesisConfig { + proposal_bond: Default::default(), + proposal_bond_minimum: Default::default(), + spend_period: runtime_primitives::traits::One::one(), + burn: Default::default(), + } + } +} + +#[cfg(feature = "std")] +impl runtime_primitives::BuildStorage for GenesisConfig +{ + fn build_storage(self) -> ::std::result::Result { + use codec::Encode; + Ok(map![ + Self::hash(>::key()).to_vec() => self.proposal_bond.encode(), + Self::hash(>::key()).to_vec() => self.proposal_bond_minimum.encode(), + Self::hash(>::key()).to_vec() => self.spend_period.encode(), + Self::hash(>::key()).to_vec() => self.burn.encode() + ]) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use runtime_io::with_externalities; + use substrate_primitives::{H256, Blake2Hasher}; + use runtime_primitives::BuildStorage; + use runtime_primitives::traits::{BlakeTwo256}; + use runtime_primitives::testing::{Digest, Header}; + + impl_outer_origin! { + pub enum Origin for Test {} + } + + #[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 Digest = Digest; + type AccountId = u64; + type Header = Header; + type Event = (); + } + impl balances::Trait for Test { + type Balance = u64; + type AccountIndex = u64; + type OnFreeBalanceZero = (); + type EnsureAccountLiquid = (); + type Event = (); + } + impl Trait for Test { + type ApproveOrigin = system::EnsureRoot; + type RejectOrigin = system::EnsureRoot; + type Event = (); + } + type Balances = balances::Module; + type Treasury = Module; + + fn new_test_ext() -> runtime_io::TestExternalities { + let mut t = system::GenesisConfig::::default().build_storage().unwrap(); + 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, + reclaim_rebate: 0, + }.build_storage().unwrap()); + t.extend(GenesisConfig::{ + proposal_bond: Permill::from_percent(5), + proposal_bond_minimum: 1, + spend_period: 2, + burn: Permill::from_percent(50), + }.build_storage().unwrap()); + 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); + }); + } + + #[test] + fn minting_works() { + with_externalities(&mut new_test_ext(), || { + // Check that accumulate works when we have Some value in Dummy already. + Treasury::on_dilution(100, 100); + assert_eq!(Treasury::pot(), 100); + }); + } + + #[test] + fn spend_proposal_takes_min_deposit() { + with_externalities(&mut new_test_ext(), || { + assert_ok!(Treasury::propose_spend(Origin::signed(0), 1, 3)); + assert_eq!(Balances::free_balance(&0), 99); + assert_eq!(Balances::reserved_balance(&0), 1); + }); + } + + #[test] + fn spend_proposal_takes_proportional_deposit() { + with_externalities(&mut new_test_ext(), || { + assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3)); + assert_eq!(Balances::free_balance(&0), 95); + assert_eq!(Balances::reserved_balance(&0), 5); + }); + } + + #[test] + fn spend_proposal_fails_when_proposer_poor() { + with_externalities(&mut new_test_ext(), || { + assert_noop!(Treasury::propose_spend(Origin::signed(2), 100, 3), "Proposer's balance too low"); + }); + } + + #[test] + fn accepted_spend_proposal_ignored_outside_spend_period() { + with_externalities(&mut new_test_ext(), || { + Treasury::on_dilution(100, 100); + + assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3)); + assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); + + >::on_finalise(1); + assert_eq!(Balances::free_balance(&3), 0); + assert_eq!(Treasury::pot(), 100); + }); + } + + #[test] + fn unused_pot_should_diminish() { + with_externalities(&mut new_test_ext(), || { + Treasury::on_dilution(100, 100); + + >::on_finalise(2); + assert_eq!(Treasury::pot(), 50); + }); + } + + #[test] + fn rejected_spend_proposal_ignored_on_spend_period() { + with_externalities(&mut new_test_ext(), || { + Treasury::on_dilution(100, 100); + + assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3)); + assert_ok!(Treasury::reject_proposal(Origin::ROOT, 0)); + + >::on_finalise(2); + assert_eq!(Balances::free_balance(&3), 0); + assert_eq!(Treasury::pot(), 50); + }); + } + + #[test] + fn reject_already_rejected_spend_proposal_fails() { + with_externalities(&mut new_test_ext(), || { + Treasury::on_dilution(100, 100); + + assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3)); + assert_ok!(Treasury::reject_proposal(Origin::ROOT, 0)); + assert_noop!(Treasury::reject_proposal(Origin::ROOT, 0), "No proposal at that index"); + }); + } + + #[test] + fn reject_non_existant_spend_proposal_fails() { + with_externalities(&mut new_test_ext(), || { + assert_noop!(Treasury::reject_proposal(Origin::ROOT, 0), "No proposal at that index"); + }); + } + + #[test] + fn accept_non_existant_spend_proposal_fails() { + with_externalities(&mut new_test_ext(), || { + assert_noop!(Treasury::approve_proposal(Origin::ROOT, 0), "No proposal at that index"); + }); + } + + #[test] + fn accept_already_rejected_spend_proposal_fails() { + with_externalities(&mut new_test_ext(), || { + Treasury::on_dilution(100, 100); + + assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3)); + assert_ok!(Treasury::reject_proposal(Origin::ROOT, 0)); + assert_noop!(Treasury::approve_proposal(Origin::ROOT, 0), "No proposal at that index"); + }); + } + + #[test] + fn accepted_spend_proposal_enacted_on_spend_period() { + with_externalities(&mut new_test_ext(), || { + Treasury::on_dilution(100, 100); + + assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3)); + assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); + + >::on_finalise(2); + assert_eq!(Balances::free_balance(&3), 100); + assert_eq!(Treasury::pot(), 0); + }); + } + + #[test] + fn pot_underflow_should_not_diminish() { + with_externalities(&mut new_test_ext(), || { + Treasury::on_dilution(100, 100); + + assert_ok!(Treasury::propose_spend(Origin::signed(0), 150, 3)); + assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); + + >::on_finalise(2); + assert_eq!(Treasury::pot(), 100); + + Treasury::on_dilution(100, 100); + >::on_finalise(4); + assert_eq!(Balances::free_balance(&3), 150); + assert_eq!(Treasury::pot(), 25); + }); + } +} diff --git a/runtime/version/Cargo.toml b/runtime/version/Cargo.toml new file mode 100644 index 0000000000000..10f91e9677c71 --- /dev/null +++ b/runtime/version/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "substrate-runtime-version" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +serde = { version = "1.0", default_features = false } +serde_derive = { version = "1.0", optional = true } +substrate-codec = { path = "../../core/codec", default_features = false } +substrate-codec-derive = { path = "../../core/codec/derive", default_features = false } +substrate-runtime-std = { path = "../../core/runtime-std", default_features = false } +substrate-runtime-support = { path = "../support", default_features = false } + +[features] +default = ["std"] +std = [ + "serde/std", + "serde_derive", + "substrate-codec/std", + "substrate-runtime-std/std", + "substrate-runtime-support/std", +] diff --git a/runtime/version/src/lib.rs b/runtime/version/src/lib.rs new file mode 100644 index 0000000000000..632351b76314a --- /dev/null +++ b/runtime/version/src/lib.rs @@ -0,0 +1,130 @@ +// Copyright 2017 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 . + +//! Version module for runtime; Provide a function that returns runtime version. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(feature = "std")] +extern crate serde; + +#[cfg(feature = "std")] +#[macro_use] +extern crate serde_derive; + +#[allow(unused_imports)] +#[macro_use] +extern crate substrate_runtime_std as rstd; + +#[macro_use] +extern crate substrate_codec_derive; + +extern crate substrate_codec as codec; + +#[cfg(feature = "std")] +use std::fmt; + +#[cfg(feature = "std")] +pub type VersionString = ::std::borrow::Cow<'static, str>; +#[cfg(not(feature = "std"))] +pub type VersionString = &'static str; + +#[cfg(feature = "std")] +#[macro_export] +macro_rules! ver_str { + ( $y:expr ) => {{ ::std::borrow::Cow::Borrowed($y) }} +} + +#[cfg(not(feature = "std"))] +#[macro_export] +macro_rules! ver_str { + ( $y:expr ) => {{ $y }} +} + +/// Runtime version. +/// This should not be thought of as classic Semver (major/minor/tiny). +/// This triplet have different semantics and mis-interpretation could cause problems. +/// In particular: bug fixes should result in an increment of `spec_version` and possibly `authoring_version`, +/// absolutely not `impl_version` since they change the semantics of the runtime. +#[derive(Clone, PartialEq, Eq, Encode)] +#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize, Decode))] +pub struct RuntimeVersion { + /// Identifies the different Substrate runtimes. There'll be at least polkadot and node. + /// A different on-chain spec_name to that of the native runtime would normally result + /// in node not attempting to sync or author blocks. + pub spec_name: VersionString, + + /// Name of the implementation of the spec. This is of little consequence for the node + /// and serves only to differentiate code of different implementation teams. For this + /// codebase, it will be parity-polkadot. If there were a non-Rust implementation of the + /// Polkadot runtime (e.g. C++), then it would identify itself with an accordingly different + /// `impl_name`. + pub impl_name: VersionString, + + /// `authoring_version` is the version of the authorship interface. An authoring node + /// will not attempt to author blocks unless this is equal to its native runtime. + pub authoring_version: u32, + + /// Version of the runtime specification. A full-node will not attempt to use its native + /// runtime in substitute for the on-chain Wasm runtime unless all of `spec_name`, + /// `spec_version` and `authoring_version` are the same between Wasm and native. + pub spec_version: u32, + + /// Version of the implementation of the specification. Nodes are free to ignore this; it + /// serves only as an indication that the code is different; as long as the other two versions + /// are the same then while the actual code may be different, it is nonetheless required to + /// do the same thing. + /// Non-consensus-breaking optimisations are about the only changes that could be made which + /// would result in only the `impl_version` changing. + pub impl_version: u32, +} + +// TODO: remove this after PoC-2 +#[cfg(feature = "std")] +impl Default for RuntimeVersion { + fn default() -> RuntimeVersion { + RuntimeVersion { + spec_name: ver_str!("polkadot"), + impl_name: ver_str!("parity-polkadot"), + authoring_version: 0, + spec_version: 0, + impl_version: 0, + } + } +} + +#[cfg(feature = "std")] +impl fmt::Display for RuntimeVersion { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}-{}:{}({}-{})", self.spec_name, self.spec_version, self.authoring_version, self.impl_name, self.impl_version) + } +} + +#[cfg(feature = "std")] +impl RuntimeVersion { + /// Check if this version matches other version for calling into runtime. + pub fn can_call_with(&self, other: &RuntimeVersion) -> bool { + self.spec_version == other.spec_version && + self.spec_name == other.spec_name && + self.authoring_version == other.authoring_version + } + + /// Check if this version matches other version for authoring blocks. + pub fn can_author_with(&self, other: &RuntimeVersion) -> bool { + self.authoring_version == other.authoring_version && + self.spec_name == other.spec_name + } +}

for &'a [usize] { - fn mul_and_add(&self) -> usize { - self.iter().fold(1, |a, c| a * c) + P::plus42() - } - } - - let numbers = vec![1, 2, 3]; - let mut numbers = &numbers[..]; - let out = foo::::using(&mut numbers, || { - foo::::with(|x| x.mul_and_add() ) - }).unwrap(); - - assert_eq!(out, 6 + 42); - environmental!(foo: trait Multiplier); - } -} diff --git a/substrate/environmental/with_std.rs b/substrate/environmental/with_std.rs deleted file mode 100644 index 63ec5a71af317..0000000000000 --- a/substrate/environmental/with_std.rs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2018 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 . - -#[doc(hidden)] -pub mod imp { - pub use std::cell::RefCell; - pub use std::thread::LocalKey; - pub use std::mem::transmute; - pub use std::mem::replace; - pub use std::marker::PhantomData; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! thread_local_impl { - ($(#[$attr:meta])* static $name:ident: $t:ty = $init:expr) => ( - thread_local!($(#[$attr])* static $name: $t = $init); - ); -} diff --git a/substrate/environmental/without_std.rs b/substrate/environmental/without_std.rs deleted file mode 100644 index 0576419d1d3f5..0000000000000 --- a/substrate/environmental/without_std.rs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2018 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 . - -#[doc(hidden)] -pub mod imp { - pub use core::cell::RefCell; - pub use core::mem::transmute; - pub use core::mem::replace; - pub use core::marker::PhantomData; - - // This code is a simplified version of [`LocalKey`] and it's wasm32 specialization: [`statik::Key`]. - // [`LocalKey`]: https://github.com/alexcrichton/rust/blob/98931165a23a1c2860d99759385f45d6807c8982/src/libstd/thread/local.rs#L89 - // [`statik::Key`]: https://github.com/alexcrichton/rust/blob/98931165a23a1c2860d99759385f45d6807c8982/src/libstd/thread/local.rs#L310-L312 - - pub struct LocalKey { - pub init: fn() -> T, - pub inner: RefCell>, - } - - // This is safe as long there is no threads in wasm32. - unsafe impl ::core::marker::Sync for LocalKey { } - - impl LocalKey { - pub const fn new(init: fn() -> T) -> LocalKey { - LocalKey { - init, - inner: RefCell::new(None), - } - } - - pub fn with(&'static self, f: F) -> R - where F: FnOnce(&T) -> R - { - if self.inner.borrow().is_none() { - let v = (self.init)(); - *self.inner.borrow_mut() = Some(v); - } - // This code can't panic because: - // 1. `inner` can be borrowed mutably only once at the initialization time. - // 2. After the initialization `inner` is always `Some`. - f(&*self.inner.borrow().as_ref().unwrap()) - } - } -} - -#[doc(hidden)] -#[macro_export] -macro_rules! thread_local_impl { - ($(#[$attr:meta])* static $name:ident: $t:ty = $init:expr) => ( - $(#[$attr])* - static $name: $crate::imp::LocalKey<$t> = { - fn __init() -> $t { $init } - - $crate::imp::LocalKey::new(__init) - }; - ); -} diff --git a/substrate/executor/Cargo.toml b/substrate/executor/Cargo.toml deleted file mode 100644 index 0c4e658740517..0000000000000 --- a/substrate/executor/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "substrate-executor" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -error-chain = "0.12" -substrate-codec = { path = "../codec" } -substrate-runtime-io = { path = "../runtime-io" } -substrate-primitives = { path = "../primitives" } -substrate-serializer = { path = "../serializer" } -substrate-state-machine = { path = "../state-machine" } -substrate-runtime-version = { path = "../runtime/version" } -ed25519 = { path = "../ed25519" } -serde = "1.0" -serde_derive = "1.0" -wasmi = "0.4" -byteorder = "1.1" -triehash = "0.2" -twox-hash = "1.1.0" -lazy_static = "1.0" -parking_lot = "*" -log = "0.3" -hashdb = "0.2.1" - -[dev-dependencies] -assert_matches = "1.1" -wabt = "0.4" -hex-literal = "0.1.0" - -[features] -default = [] -wasm-extern-trace = [] diff --git a/substrate/executor/README.adoc b/substrate/executor/README.adoc deleted file mode 100644 index 6a0ee23565a70..0000000000000 --- a/substrate/executor/README.adoc +++ /dev/null @@ -1,13 +0,0 @@ - -= Executor - -.Summary -[source, toml] ----- -include::Cargo.toml[lines=2..5] ----- - -.Description ----- -include::src/lib.rs[tag=description] ----- diff --git a/substrate/executor/src/error.rs b/substrate/executor/src/error.rs deleted file mode 100644 index 1729ed2bafa11..0000000000000 --- a/substrate/executor/src/error.rs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2017 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 . - -//! Rust executor possible errors. - -use state_machine; -use serializer; -use wasmi; - -error_chain! { - foreign_links { - InvalidData(serializer::Error) #[doc = "Unserializable Data"]; - Trap(wasmi::Trap) #[doc = "Trap occured during execution"]; - Wasmi(wasmi::Error) #[doc = "Wasmi loading/instantiating error"]; - } - - errors { - /// Method is not found - MethodNotFound(t: String) { - description("method not found"), - display("Method not found: '{}'", t), - } - - /// Code is invalid (expected single byte) - InvalidCode(c: Vec) { - description("invalid code"), - display("Invalid Code: {:?}", c), - } - - /// Could not get runtime version. - VersionInvalid { - description("Runtime version error"), - display("On-chain runtime does not specify version"), - } - - /// Externalities have failed. - Externalities { - description("externalities failure"), - display("Externalities error"), - } - - /// Invalid index. - InvalidIndex { - description("index given was not in range"), - display("Invalid index provided"), - } - - /// Invalid return type. - InvalidReturn { - description("u64 was not returned"), - display("Invalid type returned (should be u64)"), - } - - /// Runtime failed. - Runtime { - description("runtime failure"), - display("Runtime error"), - } - - /// Runtime failed. - InvalidMemoryReference { - description("invalid memory reference"), - display("Invalid memory reference"), - } - } -} - -impl state_machine::Error for Error {} diff --git a/substrate/executor/src/lib.rs b/substrate/executor/src/lib.rs deleted file mode 100644 index 7898411be47d4..0000000000000 --- a/substrate/executor/src/lib.rs +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -// tag::description[] -//! Temporary crate for contracts implementations. -//! -//! This will be replaced with WASM contracts stored on-chain. -//! ** NOTE *** -//! This is entirely deprecated with the idea of a single-module Wasm module for state transition. -//! The dispatch table should be replaced with the specific functions needed: -//! - execute_block(bytes) -//! - init_block(PrevBlock?) -> InProgressBlock -//! - add_transaction(InProgressBlock) -> InProgressBlock -//! I leave it as is for now as it might be removed before this is ever done. -// end::description[] - -#![warn(missing_docs)] -#![recursion_limit="128"] - -extern crate substrate_codec as codec; -extern crate substrate_runtime_io as runtime_io; -extern crate substrate_primitives as primitives; -extern crate substrate_serializer as serializer; -extern crate substrate_state_machine as state_machine; -extern crate substrate_runtime_version as runtime_version; -extern crate ed25519; - -extern crate serde; -extern crate wasmi; -extern crate byteorder; -extern crate triehash; -extern crate parking_lot; -extern crate twox_hash; -extern crate hashdb; - -#[macro_use] extern crate log; - -#[macro_use] -extern crate lazy_static; - -#[macro_use] -extern crate error_chain; - -#[cfg(test)] -extern crate assert_matches; - -#[cfg(test)] -extern crate wabt; - -#[cfg(test)] -#[macro_use] -extern crate hex_literal; - -#[macro_use] -mod wasm_utils; -mod wasm_executor; -#[macro_use] -mod native_executor; -mod sandbox; - -pub mod error; -pub use wasm_executor::WasmExecutor; -pub use native_executor::{with_native_environment, NativeExecutor, NativeExecutionDispatch}; -pub use state_machine::Externalities; -pub use runtime_version::RuntimeVersion; -pub use codec::Codec; -use primitives::Blake2Hasher; - -/// Provides runtime information. -pub trait RuntimeInfo { - /// Native runtime information if any. - const NATIVE_VERSION: Option; - - /// Extract RuntimeVersion of given :code block - fn runtime_version> ( - &self, - ext: &mut E, - heap_pages: usize, - code: &[u8] - ) -> Option; -} diff --git a/substrate/executor/src/native_executor.rs b/substrate/executor/src/native_executor.rs deleted file mode 100644 index bc928364a607a..0000000000000 --- a/substrate/executor/src/native_executor.rs +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright 2017 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 error::{Error, ErrorKind, Result}; -use state_machine::{CodeExecutor, Externalities}; -use wasm_executor::WasmExecutor; -use wasmi::Module as WasmModule; -use runtime_version::RuntimeVersion; -use std::collections::HashMap; -use codec::Decode; -use primitives::hashing::blake2_256; -use parking_lot::{Mutex, MutexGuard}; -use RuntimeInfo; -use primitives::Blake2Hasher; - -// For the internal Runtime Cache: -// Is it compatible enough to run this natively or do we need to fall back on the WasmModule - -enum RuntimePreproc { - InvalidCode, - ValidCode(WasmModule, Option), -} - -type CacheType = HashMap<[u8; 32], RuntimePreproc>; - -lazy_static! { - static ref RUNTIMES_CACHE: Mutex = Mutex::new(HashMap::new()); -} - -// helper function to generate low-over-head caching_keys -// it is asserted that part of the audit process that any potential on-chain code change -// will have done is to ensure that the two-x hash is different to that of any other -// :code value from the same chain -fn gen_cache_key(code: &[u8]) -> [u8; 32] { - blake2_256(code) -} - -/// fetch a runtime version from the cache or if there is no cached version yet, create -/// the runtime version entry for `code`, determines whether `Compatibility::IsCompatible` -/// can be used by comparing returned RuntimeVersion to `ref_version` -fn fetch_cached_runtime_version<'a, E: Externalities>( - wasm_executor: &WasmExecutor, - cache: &'a mut MutexGuard, - ext: &mut E, - heap_pages: usize, - code: &[u8] -) -> Result<(&'a WasmModule, &'a Option)> { - let maybe_runtime_preproc = cache.entry(gen_cache_key(code)) - .or_insert_with(|| match WasmModule::from_buffer(code) { - Ok(module) => { - let version = wasm_executor.call_in_wasm_module(ext, heap_pages, &module, "version", &[]) - .ok() - .and_then(|v| RuntimeVersion::decode(&mut v.as_slice())); - RuntimePreproc::ValidCode(module, version) - } - Err(e) => { - trace!(target: "executor", "Invalid code presented to executor ({:?})", e); - RuntimePreproc::InvalidCode - } - }); - match maybe_runtime_preproc { - RuntimePreproc::InvalidCode => Err(ErrorKind::InvalidCode(code.into()).into()), - RuntimePreproc::ValidCode(m, v) => Ok((m, v)), - } -} - -fn safe_call(f: F) -> Result - where F: ::std::panic::UnwindSafe + FnOnce() -> U -{ - // Substrate uses custom panic hook that terminates process on panic. Disable it for the native call. - let hook = ::std::panic::take_hook(); - let result = ::std::panic::catch_unwind(f).map_err(|_| ErrorKind::Runtime.into()); - ::std::panic::set_hook(hook); - result -} - -/// Set up the externalities and safe calling environment to execute calls to a native runtime. -/// -/// If the inner closure panics, it will be caught and return an error. -pub fn with_native_environment(ext: &mut Externalities, f: F) -> Result -where F: ::std::panic::UnwindSafe + FnOnce() -> U -{ - ::runtime_io::with_externalities(ext, move || safe_call(f)) -} - -/// Delegate for dispatching a CodeExecutor call to native code. -pub trait NativeExecutionDispatch: Send + Sync { - /// Get the wasm code that the native dispatch will be equivalent to. - fn native_equivalent() -> &'static [u8]; - - /// Dispatch a method and input data to be executed natively. Returns `Some` result or `None` - /// if the `method` is unknown. Panics if there's an unrecoverable error. - // fn dispatch(ext: &mut Externalities, method: &str, data: &[u8]) -> Result>; - fn dispatch(ext: &mut Externalities, method: &str, data: &[u8]) -> Result>; - - /// Get native runtime version. - const VERSION: RuntimeVersion; - - /// Construct corresponding `NativeExecutor` - fn new() -> NativeExecutor where Self: Sized { - NativeExecutor::new() - } -} - -/// A generic `CodeExecutor` implementation that uses a delegate to determine wasm code equivalence -/// and dispatch to native code when possible, falling back on `WasmExecutor` when not. -#[derive(Debug)] -pub struct NativeExecutor { - /// Dummy field to avoid the compiler complaining about us not using `D`. - _dummy: ::std::marker::PhantomData, - /// The fallback executor in case native isn't available. - fallback: WasmExecutor, -} - -impl NativeExecutor { - /// Create new instance. - pub fn new() -> Self { - NativeExecutor { - _dummy: Default::default(), - fallback: WasmExecutor::new(), - } - } -} - -impl Clone for NativeExecutor { - fn clone(&self) -> Self { - NativeExecutor { - _dummy: Default::default(), - fallback: self.fallback.clone(), - } - } -} - -impl RuntimeInfo for NativeExecutor { - const NATIVE_VERSION: Option = Some(D::VERSION); - - fn runtime_version>( - &self, - ext: &mut E, - heap_pages: usize, - code: &[u8], - ) -> Option { - fetch_cached_runtime_version(&self.fallback, &mut RUNTIMES_CACHE.lock(), ext, heap_pages, code).ok()?.1.clone() - } -} - -impl CodeExecutor for NativeExecutor { - type Error = Error; - - fn call>( - &self, - ext: &mut E, - heap_pages: usize, - code: &[u8], - method: &str, - data: &[u8], - use_native: bool, - ) -> (Result>, bool) { - let mut c = RUNTIMES_CACHE.lock(); - let (module, onchain_version) = match fetch_cached_runtime_version(&self.fallback, &mut c, ext, heap_pages, code) { - Ok((module, onchain_version)) => (module, onchain_version), - Err(_) => return (Err(ErrorKind::InvalidCode(code.into()).into()), false), - }; - match (use_native, onchain_version.as_ref().map_or(false, |v| v.can_call_with(&D::VERSION))) { - (_, false) => { - trace!(target: "executor", "Request for native execution failed (native: {}, chain: {})", D::VERSION, onchain_version.as_ref().map_or_else(||"".into(), |v| format!("{}", v))); - (self.fallback.call_in_wasm_module(ext, heap_pages, module, method, data), false) - } - (false, _) => { - (self.fallback.call_in_wasm_module(ext, heap_pages, module, method, data), false) - } - _ => { - trace!(target: "executor", "Request for native execution succeeded (native: {}, chain: {})", D::VERSION, onchain_version.as_ref().map_or_else(||"".into(), |v| format!("{}", v))); - (D::dispatch(ext, method, data), true) - } - } - } -} - -#[macro_export] -macro_rules! native_executor_instance { - (pub $name:ident, $dispatcher:path, $version:path, $code:expr) => { - pub struct $name; - native_executor_instance!(IMPL $name, $dispatcher, $version, $code); - }; - ($name:ident, $dispatcher:path, $version:path, $code:expr) => { - /// A unit struct which implements `NativeExecutionDispatch` feeding in the hard-coded runtime. - struct $name; - native_executor_instance!(IMPL $name, $dispatcher, $version, $code); - }; - (IMPL $name:ident, $dispatcher:path, $version:path, $code:expr) => { - // TODO: this is not so great – I think I should go back to have dispatch take a type param and modify this macro to accept a type param and then pass it in from the test-client instead - use primitives::Blake2Hasher as _Blake2Hasher; - impl $crate::NativeExecutionDispatch for $name { - const VERSION: $crate::RuntimeVersion = $version; - fn native_equivalent() -> &'static [u8] { - // WARNING!!! This assumes that the runtime was built *before* the main project. Until we - // get a proper build script, this must be strictly adhered to or things will go wrong. - $code - } - fn dispatch(ext: &mut $crate::Externalities<_Blake2Hasher>, method: &str, data: &[u8]) -> $crate::error::Result> { - $crate::with_native_environment(ext, move || $dispatcher(method, data))? - .ok_or_else(|| $crate::error::ErrorKind::MethodNotFound(method.to_owned()).into()) - } - - fn new() -> $crate::NativeExecutor<$name> { - $crate::NativeExecutor::new() - } - } - } -} diff --git a/substrate/executor/src/sandbox.rs b/substrate/executor/src/sandbox.rs deleted file mode 100644 index 7bedde70dc367..0000000000000 --- a/substrate/executor/src/sandbox.rs +++ /dev/null @@ -1,676 +0,0 @@ -// Copyright 2018 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 . - -#![warn(missing_docs)] - -//! This module implements sandboxing support in the runtime. - -use std::collections::HashMap; -use std::rc::Rc; -use codec::{Decode, Encode}; -use primitives::sandbox as sandbox_primitives; -use wasm_utils::UserError; -use wasmi; -use wasmi::memory_units::Pages; -use wasmi::{ - Externals, FuncRef, ImportResolver, MemoryInstance, MemoryRef, Module, ModuleInstance, - ModuleRef, RuntimeArgs, RuntimeValue, Trap, TrapKind -}; - -/// Index of a function inside the supervisor. -/// -/// This is a typically an index in the default table of the supervisor, however -/// the exact meaning of this index is depends on the implementation of dispatch function. -#[derive(Copy, Clone, Debug, PartialEq)] -struct SupervisorFuncIndex(usize); - -/// Index of a function within guest index space. -/// -/// This index is supposed to be used with as index for `Externals`. -#[derive(Copy, Clone, Debug, PartialEq)] -struct GuestFuncIndex(usize); - -/// This struct holds a mapping from guest index space to supervisor. -struct GuestToSupervisorFunctionMapping { - funcs: Vec, -} - -impl GuestToSupervisorFunctionMapping { - fn new() -> GuestToSupervisorFunctionMapping { - GuestToSupervisorFunctionMapping { funcs: Vec::new() } - } - - fn define(&mut self, supervisor_func: SupervisorFuncIndex) -> GuestFuncIndex { - let idx = self.funcs.len(); - self.funcs.push(supervisor_func); - GuestFuncIndex(idx) - } - - fn func_by_guest_index(&self, guest_func_idx: GuestFuncIndex) -> Option { - self.funcs.get(guest_func_idx.0).cloned() - } -} - -struct Imports { - func_map: HashMap<(Vec, Vec), GuestFuncIndex>, - memories_map: HashMap<(Vec, Vec), MemoryRef>, -} - -impl ImportResolver for Imports { - fn resolve_func( - &self, - module_name: &str, - field_name: &str, - signature: &::wasmi::Signature, - ) -> Result { - let key = ( - module_name.as_bytes().to_owned(), - field_name.as_bytes().to_owned(), - ); - let idx = *self.func_map.get(&key).ok_or_else(|| { - ::wasmi::Error::Instantiation(format!( - "Export {}:{} not found", - module_name, field_name - )) - })?; - Ok(::wasmi::FuncInstance::alloc_host(signature.clone(), idx.0)) - } - - fn resolve_memory( - &self, - module_name: &str, - field_name: &str, - _memory_type: &::wasmi::MemoryDescriptor, - ) -> Result { - let key = ( - module_name.as_bytes().to_vec(), - field_name.as_bytes().to_vec(), - ); - let mem = self.memories_map - .get(&key) - .ok_or_else(|| { - ::wasmi::Error::Instantiation(format!( - "Export {}:{} not found", - module_name, field_name - )) - })? - .clone(); - Ok(mem) - } - - fn resolve_global( - &self, - module_name: &str, - field_name: &str, - _global_type: &::wasmi::GlobalDescriptor, - ) -> Result<::wasmi::GlobalRef, ::wasmi::Error> { - Err(::wasmi::Error::Instantiation(format!( - "Export {}:{} not found", - module_name, field_name - ))) - } - - fn resolve_table( - &self, - module_name: &str, - field_name: &str, - _table_type: &::wasmi::TableDescriptor, - ) -> Result<::wasmi::TableRef, ::wasmi::Error> { - Err(::wasmi::Error::Instantiation(format!( - "Export {}:{} not found", - module_name, field_name - ))) - } -} - -/// This trait encapsulates sandboxing capabilities. -/// -/// Note that this functions are only called in the `supervisor` context. -pub trait SandboxCapabilities { - /// Returns a reference to an associated sandbox `Store`. - fn store(&self) -> &Store; - - /// Returns a mutable reference to an associated sandbox `Store`. - fn store_mut(&mut self) -> &mut Store; - - /// Allocate space of the specified length in the supervisor memory. - /// - /// Returns pointer to the allocated block. - fn allocate(&mut self, len: u32) -> u32; - - /// Deallocate space specified by the pointer that was previously returned by [`allocate`]. - /// - /// [`allocate`]: #tymethod.allocate - fn deallocate(&mut self, ptr: u32); - - /// Write `data` into the supervisor memory at offset specified by `ptr`. - /// - /// # Errors - /// - /// Returns `Err` if `ptr + data.len()` is out of bounds. - fn write_memory(&mut self, ptr: u32, data: &[u8]) -> Result<(), UserError>; - - /// Read `len` bytes from the supervisor memory. - /// - /// # Errors - /// - /// Returns `Err` if `ptr + len` is out of bounds. - fn read_memory(&self, ptr: u32, len: u32) -> Result, UserError>; -} - -/// Implementation of [`Externals`] that allows execution of guest module with -/// [externals][`Externals`] that might refer functions defined by supervisor. -/// -/// [`Externals`]: ../../wasmi/trait.Externals.html -pub struct GuestExternals<'a, FE: SandboxCapabilities + Externals + 'a> { - supervisor_externals: &'a mut FE, - sandbox_instance: &'a SandboxInstance, - state: u32, -} - -fn trap() -> Trap { - TrapKind::Host(Box::new(UserError("Sandbox error"))).into() -} - -fn deserialize_result(serialized_result: &[u8]) -> Result, Trap> { - use self::sandbox_primitives::{HostError, ReturnValue}; - let result_val = Result::::decode(&mut &serialized_result[..]) - .ok_or_else(|| trap())?; - - match result_val { - Ok(return_value) => Ok(match return_value { - ReturnValue::Unit => None, - ReturnValue::Value(typed_value) => Some(RuntimeValue::from(typed_value)), - }), - Err(HostError) => Err(trap()), - } -} - -impl<'a, FE: SandboxCapabilities + Externals + 'a> Externals for GuestExternals<'a, FE> { - fn invoke_index( - &mut self, - index: usize, - args: RuntimeArgs, - ) -> Result, Trap> { - // Make `index` typesafe again. - let index = GuestFuncIndex(index); - - let dispatch_thunk = self.sandbox_instance.dispatch_thunk.clone(); - let func_idx = self.sandbox_instance - .guest_to_supervisor_mapping - .func_by_guest_index(index) - .expect( - "`invoke_index` is called with indexes registered via `FuncInstance::alloc_host`; - `FuncInstance::alloc_host` is called with indexes that was obtained from `guest_to_supervisor_mapping`; - `func_by_guest_index` called with `index` can't return `None`; - qed" - ); - - // Serialize arguments into a byte vector. - let invoke_args_data: Vec = args.as_ref() - .iter() - .cloned() - .map(sandbox_primitives::TypedValue::from) - .collect::>() - .encode(); - - let state = self.state; - - // Move serialized arguments inside the memory and invoke dispatch thunk and - // then free allocated memory. - let invoke_args_ptr = self.supervisor_externals - .allocate(invoke_args_data.len() as u32); - self.supervisor_externals - .write_memory(invoke_args_ptr, &invoke_args_data)?; - let result = ::wasmi::FuncInstance::invoke( - &dispatch_thunk, - &[ - RuntimeValue::I32(invoke_args_ptr as i32), - RuntimeValue::I32(invoke_args_data.len() as i32), - RuntimeValue::I32(state as i32), - RuntimeValue::I32(func_idx.0 as i32), - ], - self.supervisor_externals, - ); - self.supervisor_externals.deallocate(invoke_args_ptr); - - // dispatch_thunk returns pointer to serialized arguments. - let (serialized_result_val_ptr, serialized_result_val_len) = match result { - // Unpack pointer and len of the serialized result data. - Ok(Some(RuntimeValue::I64(v))) => { - // Cast to u64 to use zero-extension. - let v = v as u64; - let ptr = (v as u64 >> 32) as u32; - let len = (v & 0xFFFFFFFF) as u32; - (ptr, len) - } - _ => return Err(trap()), - }; - - let serialized_result_val = self.supervisor_externals - .read_memory(serialized_result_val_ptr, serialized_result_val_len)?; - self.supervisor_externals - .deallocate(serialized_result_val_ptr); - - // We do not have to check the signature here, because it's automatically - // checked by wasmi. - - deserialize_result(&serialized_result_val) - } -} - -fn with_guest_externals( - supervisor_externals: &mut FE, - sandbox_instance: &SandboxInstance, - state: u32, - f: F, -) -> R -where - FE: SandboxCapabilities + Externals, - F: FnOnce(&mut GuestExternals) -> R, -{ - let mut guest_externals = GuestExternals { - supervisor_externals, - sandbox_instance, - state, - }; - f(&mut guest_externals) -} - -/// Sandboxed instance of a wasm module. -/// -/// It's primary purpose is to [`invoke`] exported functions on it. -/// -/// All imports of this instance are specified at the creation time and -/// imports are implemented by the supervisor. -/// -/// Hence, in order to invoke an exported function on a sandboxed module instance, -/// it's required to provide supervisor externals: it will be used to execute -/// code in the supervisor context. -/// -/// [`invoke`]: #method.invoke -pub struct SandboxInstance { - instance: ModuleRef, - dispatch_thunk: FuncRef, - guest_to_supervisor_mapping: GuestToSupervisorFunctionMapping, -} - -impl SandboxInstance { - /// Invoke an exported function by a name. - /// - /// `supervisor_externals` is required to execute the implementations - /// of the syscalls that published to a sandboxed module instance. - /// - /// The `state` parameter can be used to provide custom data for - /// these syscall implementations. - pub fn invoke( - &self, - export_name: &str, - args: &[RuntimeValue], - supervisor_externals: &mut FE, - state: u32, - ) -> Result, wasmi::Error> { - with_guest_externals( - supervisor_externals, - self, - state, - |guest_externals| { - self.instance - .invoke_export(export_name, args, guest_externals) - }, - ) - } -} - -fn decode_environment_definition( - raw_env_def: &[u8], - memories: &[Option], -) -> Result<(Imports, GuestToSupervisorFunctionMapping), UserError> { - let env_def = sandbox_primitives::EnvironmentDefinition::decode(&mut &raw_env_def[..]).ok_or_else(|| UserError("Sandbox error"))?; - - let mut func_map = HashMap::new(); - let mut memories_map = HashMap::new(); - let mut guest_to_supervisor_mapping = GuestToSupervisorFunctionMapping::new(); - - for entry in &env_def.entries { - let module = entry.module_name.clone(); - let field = entry.field_name.clone(); - - match entry.entity { - sandbox_primitives::ExternEntity::Function(func_idx) => { - let externals_idx = - guest_to_supervisor_mapping.define(SupervisorFuncIndex(func_idx as usize)); - func_map.insert((module, field), externals_idx); - } - sandbox_primitives::ExternEntity::Memory(memory_idx) => { - let memory_ref = memories - .get(memory_idx as usize) - .cloned() - .ok_or_else(|| UserError("Sandbox error"))? - .ok_or_else(|| UserError("Sandbox error"))?; - memories_map.insert((module, field), memory_ref); - } - } - } - - Ok(( - Imports { - func_map, - memories_map, - }, - guest_to_supervisor_mapping, - )) -} - -/// Instantiate a guest module and return it's index in the store. -/// -/// The guest module's code is specified in `wasm`. Environment that will be available to -/// guest module is specified in `raw_env_def` (serialized version of [`EnvironmentDefinition`]). -/// `dispatch_thunk` is used as function that handle calls from guests. -/// -/// # Errors -/// -/// Returns `Err` if any of the following conditions happens: -/// -/// - `raw_env_def` can't be deserialized as a [`EnvironmentDefinition`]. -/// - Module in `wasm` is invalid or couldn't be instantiated. -/// -/// [`EnvironmentDefinition`]: ../../sandbox/struct.EnvironmentDefinition.html -pub fn instantiate( - supervisor_externals: &mut FE, - dispatch_thunk: FuncRef, - wasm: &[u8], - raw_env_def: &[u8], - state: u32, -) -> Result { - let (imports, guest_to_supervisor_mapping) = - decode_environment_definition(raw_env_def, &supervisor_externals.store().memories)?; - - let module = Module::from_buffer(wasm).map_err(|_| UserError("Sandbox error"))?; - let instance = ModuleInstance::new(&module, &imports).map_err(|_| UserError("Sandbox error"))?; - - let sandbox_instance = Rc::new(SandboxInstance { - // In general, it's not a very good idea to use `.not_started_instance()` for anything - // but for extracting memory and tables. But in this particular case, we are extracting - // for the purpose of running `start` function which should be ok. - instance: instance.not_started_instance().clone(), - dispatch_thunk, - guest_to_supervisor_mapping, - }); - - with_guest_externals( - supervisor_externals, - &sandbox_instance, - state, - |guest_externals| { - instance - .run_start(guest_externals) - .map_err(|_| UserError("Sandbox error")) - }, - )?; - - let instance_idx = supervisor_externals - .store_mut() - .register_sandbox_instance(sandbox_instance); - - Ok(instance_idx) -} - -/// This struct keeps track of all sandboxed components. -pub struct Store { - // Memories and instances are `Some` untill torndown. - instances: Vec>>, - memories: Vec>, -} - -impl Store { - /// Create a new empty sandbox store. - pub fn new() -> Store { - Store { - instances: Vec::new(), - memories: Vec::new(), - } - } - - /// Create a new memory instance and return it's index. - /// - /// # Errors - /// - /// Returns `Err` if the memory couldn't be created. - /// Typically happens if `initial` is more than `maximum`. - pub fn new_memory(&mut self, initial: u32, maximum: u32) -> Result { - let maximum = match maximum { - sandbox_primitives::MEM_UNLIMITED => None, - specified_limit => Some(Pages(specified_limit as usize)), - }; - - let mem = - MemoryInstance::alloc(Pages(initial as usize), maximum).map_err(|_| UserError("Sandbox error"))?; - let mem_idx = self.memories.len(); - self.memories.push(Some(mem)); - Ok(mem_idx as u32) - } - - /// Returns `SandboxInstance` by `instance_idx`. - /// - /// # Errors - /// - /// Returns `Err` If `instance_idx` isn't a valid index of an instance or - /// instance is already torndown. - pub fn instance(&self, instance_idx: u32) -> Result, UserError> { - self.instances - .get(instance_idx as usize) - .cloned() - .ok_or_else(|| UserError("Sandbox error"))? - .ok_or_else(|| UserError("Sandbox error")) - } - - /// Returns reference to a memory instance by `memory_idx`. - /// - /// # Errors - /// - /// Returns `Err` If `memory_idx` isn't a valid index of an memory or - /// memory is already torndown. - pub fn memory(&self, memory_idx: u32) -> Result { - self.memories - .get(memory_idx as usize) - .cloned() - .ok_or_else(|| UserError("Sandbox error"))? - .ok_or_else(|| UserError("Sandbox error")) - } - - /// Teardown the memory at the specified index. - /// - /// # Errors - /// - /// Returns `Err` if `memory_idx` isn't a valid index of an memory. - pub fn memory_teardown(&mut self, memory_idx: u32) -> Result<(), UserError> { - if memory_idx as usize >= self.memories.len() { - return Err(UserError("Sandbox error")); - } - self.memories[memory_idx as usize] = None; - Ok(()) - } - - /// Teardown the instance at the specified index. - pub fn instance_teardown(&mut self, instance_idx: u32) -> Result<(), UserError> { - if instance_idx as usize >= self.instances.len() { - return Err(UserError("Sandbox error")); - } - self.instances[instance_idx as usize] = None; - Ok(()) - } - - fn register_sandbox_instance(&mut self, sandbox_instance: Rc) -> u32 { - let instance_idx = self.instances.len(); - self.instances.push(Some(sandbox_instance)); - instance_idx as u32 - } -} - -#[cfg(test)] -mod tests { - use wasm_executor::WasmExecutor; - use state_machine::TestExternalities; - use wabt; - - #[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 code = wabt::wat2wasm(r#" - (module - (import "env" "assert" (func $assert (param i32))) - (import "env" "inc_counter" (func $inc_counter (param i32) (result i32))) - (func (export "call") - (drop - (call $inc_counter (i32.const 5)) - ) - - (call $inc_counter (i32.const 3)) - ;; current counter value is on the stack - - ;; check whether current == 8 - i32.const 8 - i32.eq - - call $assert - ) - ) - "#).unwrap(); - - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_sandbox", &code).unwrap(), - vec![1], - ); - } - - #[test] - fn sandbox_trap() { - let mut ext = TestExternalities::default(); - let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); - - let code = wabt::wat2wasm(r#" - (module - (import "env" "assert" (func $assert (param i32))) - (func (export "call") - i32.const 0 - call $assert - ) - ) - "#).unwrap(); - - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_sandbox", &code).unwrap(), - vec![0], - ); - } - - #[test] - fn start_called() { - let mut ext = TestExternalities::default(); - let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); - - let code = wabt::wat2wasm(r#" - (module - (import "env" "assert" (func $assert (param i32))) - (import "env" "inc_counter" (func $inc_counter (param i32) (result i32))) - - ;; Start function - (start $start) - (func $start - ;; Increment counter by 1 - (drop - (call $inc_counter (i32.const 1)) - ) - ) - - (func (export "call") - ;; Increment counter by 1. The current value is placed on the stack. - (call $inc_counter (i32.const 1)) - - ;; Counter is incremented twice by 1, once there and once in `start` func. - ;; So check the returned value is equal to 2. - i32.const 2 - i32.eq - call $assert - ) - ) - "#).unwrap(); - - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_sandbox", &code).unwrap(), - vec![1], - ); - } - - #[test] - fn invoke_args() { - let mut ext = TestExternalities::default(); - let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); - - let code = wabt::wat2wasm(r#" - (module - (import "env" "assert" (func $assert (param i32))) - - (func (export "call") (param $x i32) (param $y i64) - ;; assert that $x = 0x12345678 - (call $assert - (i32.eq - (get_local $x) - (i32.const 0x12345678) - ) - ) - - (call $assert - (i64.eq - (get_local $y) - (i64.const 0x1234567887654321) - ) - ) - ) - ) - "#).unwrap(); - - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_sandbox_args", &code).unwrap(), - vec![1], - ); - } - - #[test] - fn return_val() { - let mut ext = TestExternalities::default(); - let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); - - let code = wabt::wat2wasm(r#" - (module - (func (export "call") (param $x i32) (result i32) - (i32.add - (get_local $x) - (i32.const 1) - ) - ) - ) - "#).unwrap(); - - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_sandbox_return_val", &code).unwrap(), - vec![1], - ); - } -} diff --git a/substrate/executor/src/wasm_executor.rs b/substrate/executor/src/wasm_executor.rs deleted file mode 100644 index 50488ac285087..0000000000000 --- a/substrate/executor/src/wasm_executor.rs +++ /dev/null @@ -1,720 +0,0 @@ -// Copyright 2017 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 . - -//! Rust implementation of Substrate contracts. - -use std::cmp::Ordering; -use std::collections::HashMap; - -use wasmi::{ - Module, ModuleInstance, MemoryInstance, MemoryRef, TableRef, ImportsBuilder -}; -use wasmi::RuntimeValue::{I32, I64}; -use wasmi::memory_units::{Pages, Bytes}; -use state_machine::Externalities; -use error::{Error, ErrorKind, Result}; -use wasm_utils::UserError; -use primitives::{blake2_256, twox_128, twox_256}; -use primitives::hexdisplay::HexDisplay; -use primitives::sandbox as sandbox_primitives; -use primitives::Blake2Hasher; -use triehash::ordered_trie_root; -use sandbox; - - -struct Heap { - end: u32, -} - -impl Heap { - /// Construct new `Heap` struct with a given number of pages. - /// - /// Returns `Err` if the heap couldn't allocate required - /// number of pages. - /// - /// This could mean that wasm binary specifies memory - /// limit and we are trying to allocate beyond that limit. - fn new(memory: &MemoryRef, pages: usize) -> Result { - let prev_page_count = memory.initial(); - memory.grow(Pages(pages)).map_err(|_| Error::from(ErrorKind::Runtime))?; - Ok(Heap { - end: Bytes::from(prev_page_count).0 as u32, - }) - } - - fn allocate(&mut self, size: u32) -> u32 { - let r = self.end; - self.end += size; - r - } - - fn deallocate(&mut self, _offset: u32) { - } -} - -#[cfg(feature="wasm-extern-trace")] -macro_rules! debug_trace { - ( $( $x:tt )* ) => ( trace!( $( $x )* ) ) -} -#[cfg(not(feature="wasm-extern-trace"))] -macro_rules! debug_trace { - ( $( $x:tt )* ) => () -} - -struct FunctionExecutor<'e, E: Externalities + 'e> { - sandbox_store: sandbox::Store, - heap: Heap, - memory: MemoryRef, - table: Option, - ext: &'e mut E, - hash_lookup: HashMap, Vec>, -} - -impl<'e, E: Externalities> FunctionExecutor<'e, E> { - fn new(m: MemoryRef, heap_pages: usize, t: Option, e: &'e mut E) -> Result { - Ok(FunctionExecutor { - sandbox_store: sandbox::Store::new(), - heap: Heap::new(&m, heap_pages)?, - memory: m, - table: t, - ext: e, - hash_lookup: HashMap::new(), - }) - } -} - -impl<'e, E: Externalities> sandbox::SandboxCapabilities for FunctionExecutor<'e, E> { - fn store(&self) -> &sandbox::Store { - &self.sandbox_store - } - fn store_mut(&mut self) -> &mut sandbox::Store { - &mut self.sandbox_store - } - fn allocate(&mut self, len: u32) -> u32 { - self.heap.allocate(len) - } - fn deallocate(&mut self, ptr: u32) { - self.heap.deallocate(ptr) - } - fn write_memory(&mut self, ptr: u32, data: &[u8]) -> ::std::result::Result<(), UserError> { - self.memory.set(ptr, data).map_err(|_| UserError("Invalid attempt to write_memory")) - } - fn read_memory(&self, ptr: u32, len: u32) -> ::std::result::Result, UserError> { - self.memory.get(ptr, len as usize).map_err(|_| UserError("Invalid attempt to write_memory")) - } -} - -trait WritePrimitive { - fn write_primitive(&self, offset: u32, t: T) -> ::std::result::Result<(), UserError>; -} - -impl WritePrimitive for MemoryInstance { - fn write_primitive(&self, offset: u32, t: u32) -> ::std::result::Result<(), UserError> { - use byteorder::{LittleEndian, ByteOrder}; - let mut r = [0u8; 4]; - LittleEndian::write_u32(&mut r, t); - self.set(offset, &r).map_err(|_| UserError("Invalid attempt to write_primitive")) - } -} - -trait ReadPrimitive { - fn read_primitive(&self, offset: u32) -> ::std::result::Result; -} - -impl ReadPrimitive for MemoryInstance { - fn read_primitive(&self, offset: u32) -> ::std::result::Result { - use byteorder::{LittleEndian, ByteOrder}; - Ok(LittleEndian::read_u32(&self.get(offset, 4).map_err(|_| UserError("Invalid attempt to read_primitive"))?)) - } -} - -// TODO: this macro does not support `where` clauses and that seems somewhat tricky to add -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) { - if let Ok(message) = String::from_utf8(utf8) { - println!("{}", message); - } - } - Ok(()) - }, - ext_print_hex(data: *const u8, len: u32) => { - if let Ok(hex) = this.memory.get(data, len as usize) { - println!("{}", HexDisplay::from(&hex)); - } - Ok(()) - }, - ext_print_num(number: u64) => { - println!("{}", number); - Ok(()) - }, - ext_memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32 => { - let sl1 = this.memory.get(s1, n as usize).map_err(|_| UserError("Invalid attempt to read from memory in first arg of ext_memcmp"))?; - let sl2 = this.memory.get(s2, n as usize).map_err(|_| UserError("Invalid attempt to read from memory in second arg of ext_memcmp"))?; - Ok(match sl1.cmp(&sl2) { - Ordering::Greater => 1, - Ordering::Less => -1, - Ordering::Equal => 0, - }) - }, - ext_memcpy(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => { - this.memory.copy_nonoverlapping(src as usize, dest as usize, count as usize) - .map_err(|_| UserError("Invalid attempt to copy_nonoverlapping in ext_memcpy"))?; - debug_trace!(target: "runtime-io", "memcpy {} from {}, {} bytes", dest, src, count); - Ok(dest) - }, - ext_memmove(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => { - this.memory.copy(src as usize, dest as usize, count as usize) - .map_err(|_| UserError("Invalid attempt to copy in ext_memmove"))?; - debug_trace!(target: "runtime-io", "memmove {} from {}, {} bytes", dest, src, count); - Ok(dest) - }, - ext_memset(dest: *mut u8, val: u32, count: usize) -> *mut u8 => { - debug_trace!(target: "runtime-io", "memset {} with {}, {} bytes", dest, val, count); - this.memory.clear(dest as usize, val as u8, count as usize) - .map_err(|_| UserError("Invalid attempt to clear in ext_memset"))?; - Ok(dest) - }, - ext_malloc(size: usize) -> *mut u8 => { - let r = this.heap.allocate(size); - debug_trace!(target: "runtime-io", "malloc {} bytes at {}", size, r); - Ok(r) - }, - ext_free(addr: *mut u8) => { - this.heap.deallocate(addr); - debug_trace!(target: "runtime-io", "free {}", addr); - Ok(()) - }, - ext_set_storage(key_data: *const u8, key_len: u32, value_data: *const u8, value_len: u32) => { - let key = this.memory.get(key_data, key_len as usize).map_err(|_| UserError("Invalid attempt to determine key in ext_set_storage"))?; - let value = this.memory.get(value_data, value_len as usize).map_err(|_| UserError("Invalid attempt to determine value in ext_set_storage"))?; - if let Some(_preimage) = this.hash_lookup.get(&key) { - debug_trace!(target: "wasm-trace", "*** Setting storage: %{} -> {} [k={}]", ::primitives::hexdisplay::ascii_format(&_preimage), HexDisplay::from(&value), HexDisplay::from(&key)); - } else { - debug_trace!(target: "wasm-trace", "*** Setting storage: {} -> {} [k={}]", ::primitives::hexdisplay::ascii_format(&key), HexDisplay::from(&value), HexDisplay::from(&key)); - } - this.ext.set_storage(key, value); - Ok(()) - }, - ext_clear_storage(key_data: *const u8, key_len: u32) => { - let key = this.memory.get(key_data, key_len as usize).map_err(|_| UserError("Invalid attempt to determine key in ext_clear_storage"))?; - debug_trace!(target: "wasm-trace", "*** Clearing storage: {} [k={}]", - if let Some(_preimage) = this.hash_lookup.get(&key) { - format!("%{}", ::primitives::hexdisplay::ascii_format(&_preimage)) - } else { - format!(" {}", ::primitives::hexdisplay::ascii_format(&key)) - }, HexDisplay::from(&key)); - this.ext.clear_storage(&key); - Ok(()) - }, - ext_exists_storage(key_data: *const u8, key_len: u32) -> u32 => { - let key = this.memory.get(key_data, key_len as usize).map_err(|_| UserError("Invalid attempt to determine key in ext_exists_storage"))?; - Ok(if this.ext.exists_storage(&key) { 1 } else { 0 }) - }, - ext_clear_prefix(prefix_data: *const u8, prefix_len: u32) => { - let prefix = this.memory.get(prefix_data, prefix_len as usize).map_err(|_| UserError("Invalid attempt to determine prefix in ext_clear_prefix"))?; - this.ext.clear_prefix(&prefix); - Ok(()) - }, - // return 0 and place u32::max_value() into written_out if no value exists for the key. - ext_get_allocated_storage(key_data: *const u8, key_len: u32, written_out: *mut u32) -> *mut u8 => { - let key = this.memory.get(key_data, key_len as usize).map_err(|_| UserError("Invalid attempt to determine key in ext_get_allocated_storage"))?; - let maybe_value = this.ext.storage(&key); - - debug_trace!(target: "wasm-trace", "*** Getting storage: {} == {} [k={}]", - if let Some(_preimage) = this.hash_lookup.get(&key) { - format!("%{}", ::primitives::hexdisplay::ascii_format(&_preimage)) - } else { - format!(" {}", ::primitives::hexdisplay::ascii_format(&key)) - }, - if let Some(ref b) = maybe_value { - format!("{}", HexDisplay::from(b)) - } else { - "".to_owned() - }, - HexDisplay::from(&key) - ); - - if let Some(value) = maybe_value { - let offset = this.heap.allocate(value.len() as u32) as u32; - this.memory.set(offset, &value).map_err(|_| UserError("Invalid attempt to set memory in ext_get_allocated_storage"))?; - this.memory.write_primitive(written_out, value.len() as u32) - .map_err(|_| UserError("Invalid attempt to write written_out in ext_get_allocated_storage"))?; - Ok(offset) - } else { - this.memory.write_primitive(written_out, u32::max_value()) - .map_err(|_| UserError("Invalid attempt to write failed written_out in ext_get_allocated_storage"))?; - Ok(0) - } - }, - // return u32::max_value() if no value exists for the key. - ext_get_storage_into(key_data: *const u8, key_len: u32, value_data: *mut u8, value_len: u32, value_offset: u32) -> u32 => { - let key = this.memory.get(key_data, key_len as usize).map_err(|_| UserError("Invalid attempt to get key in ext_get_storage_into"))?; - let maybe_value = this.ext.storage(&key); - debug_trace!(target: "wasm-trace", "*** Getting storage: {} == {} [k={}]", - if let Some(_preimage) = this.hash_lookup.get(&key) { - format!("%{}", ::primitives::hexdisplay::ascii_format(&_preimage)) - } else { - format!(" {}", ::primitives::hexdisplay::ascii_format(&key)) - }, - if let Some(ref b) = maybe_value { - format!("{}", HexDisplay::from(b)) - } else { - "".to_owned() - }, - HexDisplay::from(&key) - ); - - if let Some(value) = maybe_value { - let value = &value[value_offset as usize..]; - let written = ::std::cmp::min(value_len as usize, value.len()); - this.memory.set(value_data, &value[..written]).map_err(|_| UserError("Invalid attempt to set value in ext_get_storage_into"))?; - Ok(written as u32) - } else { - Ok(u32::max_value()) - } - }, - ext_storage_root(result: *mut u8) => { - let r = this.ext.storage_root(); - this.memory.set(result, r.as_ref()).map_err(|_| UserError("Invalid attempt to set memory in ext_storage_root"))?; - Ok(()) - }, - ext_blake2_256_enumerated_trie_root(values_data: *const u8, lens_data: *const u32, lens_len: u32, result: *mut u8) => { - let values = (0..lens_len) - .map(|i| this.memory.read_primitive(lens_data + i * 4)) - .collect::<::std::result::Result, UserError>>()? - .into_iter() - .scan(0u32, |acc, v| { let o = *acc; *acc += v; Some((o, v)) }) - .map(|(offset, len)| - this.memory.get(values_data + offset, len as usize) - .map_err(|_| UserError("Invalid attempt to get memory in ext_blake2_256_enumerated_trie_root")) - ) - .collect::<::std::result::Result, UserError>>()?; - let r = ordered_trie_root::(values.into_iter()); - this.memory.set(result, &r[..]).map_err(|_| UserError("Invalid attempt to set memory in ext_blake2_256_enumerated_trie_root"))?; - Ok(()) - }, - ext_chain_id() -> u64 => { - Ok(this.ext.chain_id()) - }, - ext_twox_128(data: *const u8, len: u32, out: *mut u8) => { - let result = if len == 0 { - let hashed = twox_128(&[0u8; 0]); - debug_trace!(target: "xxhash", "XXhash: '' -> {}", HexDisplay::from(&hashed)); - this.hash_lookup.insert(hashed.to_vec(), vec![]); - hashed - } else { - let key = this.memory.get(data, len as usize).map_err(|_| UserError("Invalid attempt to get key in ext_twox_128"))?; - let hashed_key = twox_128(&key); - debug_trace!(target: "xxhash", "XXhash: {} -> {}", - if let Ok(_skey) = ::std::str::from_utf8(&key) { - _skey.to_owned() - } else { - format!("{}", HexDisplay::from(&key)) - }, - HexDisplay::from(&hashed_key) - ); - this.hash_lookup.insert(hashed_key.to_vec(), key); - hashed_key - }; - - this.memory.set(out, &result).map_err(|_| UserError("Invalid attempt to set result in ext_twox_128"))?; - Ok(()) - }, - ext_twox_256(data: *const u8, len: u32, out: *mut u8) => { - let result = if len == 0 { - twox_256(&[0u8; 0]) - } else { - twox_256(&this.memory.get(data, len as usize).map_err(|_| UserError("Invalid attempt to get data in ext_twox_256"))?) - }; - this.memory.set(out, &result).map_err(|_| UserError("Invalid attempt to set result in ext_twox_256"))?; - Ok(()) - }, - ext_blake2_256(data: *const u8, len: u32, out: *mut u8) => { - let result = if len == 0 { - blake2_256(&[0u8; 0]) - } else { - blake2_256(&this.memory.get(data, len as usize).map_err(|_| UserError("Invalid attempt to get data in ext_blake2_256"))?) - }; - this.memory.set(out, &result).map_err(|_| UserError("Invalid attempt to set result in ext_blake2_256"))?; - Ok(()) - }, - ext_ed25519_verify(msg_data: *const u8, msg_len: u32, sig_data: *const u8, pubkey_data: *const u8) -> u32 => { - let mut sig = [0u8; 64]; - this.memory.get_into(sig_data, &mut sig[..]).map_err(|_| UserError("Invalid attempt to get signature in ext_ed25519_verify"))?; - let mut pubkey = [0u8; 32]; - this.memory.get_into(pubkey_data, &mut pubkey[..]).map_err(|_| UserError("Invalid attempt to get pubkey in ext_ed25519_verify"))?; - let msg = this.memory.get(msg_data, msg_len as usize).map_err(|_| UserError("Invalid attempt to get message in ext_ed25519_verify"))?; - - Ok(if ::ed25519::verify(&sig, &msg, &pubkey) { - 0 - } else { - 5 - }) - }, - ext_sandbox_instantiate(dispatch_thunk_idx: usize, wasm_ptr: *const u8, wasm_len: usize, imports_ptr: *const u8, imports_len: usize, state: usize) -> u32 => { - let wasm = this.memory.get(wasm_ptr, wasm_len as usize).map_err(|_| UserError("Sandbox error"))?; - let raw_env_def = this.memory.get(imports_ptr, imports_len as usize).map_err(|_| UserError("Sandbox error"))?; - - // Extract a dispatch thunk from instance's table by the specified index. - let dispatch_thunk = { - let table = this.table.as_ref().ok_or_else(|| UserError("Sandbox error"))?; - table.get(dispatch_thunk_idx) - .map_err(|_| UserError("Sandbox error"))? - .ok_or_else(|| UserError("Sandbox error"))? - .clone() - }; - - let instance_idx = sandbox::instantiate(this, dispatch_thunk, &wasm, &raw_env_def, state)?; - - Ok(instance_idx as u32) - }, - ext_sandbox_instance_teardown(instance_idx: u32) => { - this.sandbox_store.instance_teardown(instance_idx)?; - Ok(()) - }, - ext_sandbox_invoke(instance_idx: u32, export_ptr: *const u8, export_len: usize, args_ptr: *const u8, args_len: usize, return_val_ptr: *const u8, return_val_len: usize, state: usize) -> u32 => { - use codec::{Decode, Encode}; - - trace!(target: "runtime-sandbox", "invoke, instance_idx={}", instance_idx); - let export = this.memory.get(export_ptr, export_len as usize) - .map_err(|_| UserError("Sandbox error")) - .and_then(|b| - String::from_utf8(b) - .map_err(|_| UserError("Sandbox error")) - )?; - - // Deserialize arguments and convert them into wasmi types. - let serialized_args = this.memory.get(args_ptr, args_len as usize) - .map_err(|_| UserError("Sandbox error"))?; - let args = Vec::::decode(&mut &serialized_args[..]) - .ok_or_else(|| UserError("Sandbox error"))? - .into_iter() - .map(Into::into) - .collect::>(); - - let instance = this.sandbox_store.instance(instance_idx)?; - let result = instance.invoke(&export, &args, this, state); - - match result { - Ok(None) => Ok(sandbox_primitives::ERR_OK), - Ok(Some(val)) => { - // Serialize return value and write it back into the memory. - sandbox_primitives::ReturnValue::Value(val.into()).using_encoded(|val| { - if val.len() > return_val_len as usize { - Err(UserError("Sandbox error"))?; - } - this.memory - .set(return_val_ptr, val) - .map_err(|_| UserError("Sandbox error"))?; - Ok(sandbox_primitives::ERR_OK) - }) - } - Err(_) => Ok(sandbox_primitives::ERR_EXECUTION), - } - }, - ext_sandbox_memory_new(initial: u32, maximum: u32) -> u32 => { - let mem_idx = this.sandbox_store.new_memory(initial, maximum)?; - Ok(mem_idx) - }, - ext_sandbox_memory_get(memory_idx: u32, offset: u32, buf_ptr: *mut u8, buf_len: usize) -> u32 => { - let dst_memory = this.sandbox_store.memory(memory_idx)?; - - let data: Vec = match dst_memory.get(offset, buf_len as usize) { - Ok(data) => data, - Err(_) => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS), - }; - match this.memory.set(buf_ptr, &data) { - Err(_) => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS), - _ => {}, - } - - Ok(sandbox_primitives::ERR_OK) - }, - ext_sandbox_memory_set(memory_idx: u32, offset: u32, val_ptr: *const u8, val_len: usize) -> u32 => { - let dst_memory = this.sandbox_store.memory(memory_idx)?; - - let data = match this.memory.get(offset, val_len as usize) { - Ok(data) => data, - Err(_) => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS), - }; - match dst_memory.set(val_ptr, &data) { - Err(_) => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS), - _ => {}, - } - - Ok(sandbox_primitives::ERR_OK) - }, - ext_sandbox_memory_teardown(memory_idx: u32) => { - this.sandbox_store.memory_teardown(memory_idx)?; - Ok(()) - }, - => <'e, E: Externalities + 'e> -); - -/// Wasm rust executor for contracts. -/// -/// Executes the provided code in a sandboxed wasm runtime. -#[derive(Debug, Clone)] -pub struct WasmExecutor { -} - -impl WasmExecutor { - - /// Create a new instance. - pub fn new() -> Self { - WasmExecutor{} - } - - /// Call a given method in the given code. - /// This should be used for tests only. - pub fn call>( - &self, - ext: &mut E, - heap_pages: usize, - code: &[u8], - method: &str, - data: &[u8], - ) -> Result> { - let module = ::wasmi::Module::from_buffer(code).expect("all modules compiled with rustc are valid wasm code; qed"); - self.call_in_wasm_module(ext, heap_pages, &module, method, data) - } - - /// Call a given method in the given wasm-module runtime. - pub fn call_in_wasm_module>( - &self, - ext: &mut E, - heap_pages: usize, - module: &Module, - method: &str, - data: &[u8], - ) -> Result> { - // start module instantiation. Don't run 'start' function yet. - let intermediate_instance = ModuleInstance::new( - module, - &ImportsBuilder::new() - .with_resolver("env", FunctionExecutor::::resolver()) - )?; - - // extract a reference to a linear memory, optional reference to a table - // and then initialize FunctionExecutor. - let memory = intermediate_instance - .not_started_instance() - .export_by_name("memory") - // TODO: with code coming from the blockchain it isn't strictly been compiled with rustc anymore. - // these assumptions are probably not true anymore - .expect("all modules compiled with rustc should have an export named 'memory'; qed") - .as_memory() - .expect("in module generated by rustc export named 'memory' should be a memory; qed") - .clone(); - let table: Option = intermediate_instance - .not_started_instance() - .export_by_name("__indirect_function_table") - .and_then(|e| e.as_table().cloned()); - - let mut fec = FunctionExecutor::new(memory.clone(), heap_pages, table, ext)?; - - // finish instantiation by running 'start' function (if any). - let instance = intermediate_instance.run_start(&mut fec)?; - - let size = data.len() as u32; - let offset = fec.heap.allocate(size); - memory.set(offset, &data)?; - - let result = instance.invoke_export( - method, - &[ - I32(offset as i32), - I32(size as i32) - ], - &mut fec - ); - - let returned = match result { - Ok(x) => x, - Err(e) => { - trace!(target: "wasm-executor", "Failed to execute code with {} pages", heap_pages); - return Err(e.into()) - }, - }; - - if let Some(I64(r)) = returned { - let offset = r as u32; - let length = (r >> 32) as u32 as usize; - memory.get(offset, length) - .map_err(|_| ErrorKind::Runtime.into()) - } else { - Err(ErrorKind::InvalidReturn.into()) - } - } -} - - -#[cfg(test)] -mod tests { - use super::*; - use codec::Encode; - use state_machine::TestExternalities; - - // TODO: move into own crate. - macro_rules! map { - ($( $name:expr => $value:expr ),*) => ( - vec![ $( ( $name, $value ) ),* ].into_iter().collect() - ) - } - - #[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 output = WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_empty_return", &[]).unwrap(); - assert_eq!(output, vec![0u8; 0]); - } - - #[test] - fn panicking_should_work() { - let mut ext = TestExternalities::default(); - let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); - - let output = WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_panic", &[]); - assert!(output.is_err()); - - let output = WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_conditional_panic", &[2]); - assert!(output.is_err()); - } - - #[test] - fn storage_should_work() { - let mut ext = TestExternalities::default(); - ext.set_storage(b"foo".to_vec(), b"bar".to_vec()); - let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); - - let output = WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_data_in", b"Hello world").unwrap(); - - assert_eq!(output, b"all ok!".to_vec()); - - let expected : TestExternalities<_> = map![ - b"input".to_vec() => b"Hello world".to_vec(), - b"foo".to_vec() => b"bar".to_vec(), - b"baz".to_vec() => b"bar".to_vec() - ]; - assert_eq!(expected, ext); - } - - #[test] - fn clear_prefix_should_work() { - let mut ext = TestExternalities::default(); - ext.set_storage(b"aaa".to_vec(), b"1".to_vec()); - ext.set_storage(b"aab".to_vec(), b"2".to_vec()); - ext.set_storage(b"aba".to_vec(), b"3".to_vec()); - ext.set_storage(b"abb".to_vec(), b"4".to_vec()); - ext.set_storage(b"bbb".to_vec(), b"5".to_vec()); - let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); - - // This will clear all entries which prefix is "ab". - let output = WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_clear_prefix", b"ab").unwrap(); - - assert_eq!(output, b"all ok!".to_vec()); - - let expected: TestExternalities<_> = map![ - b"aaa".to_vec() => b"1".to_vec(), - b"aab".to_vec() => b"2".to_vec(), - b"bbb".to_vec() => b"5".to_vec() - ]; - assert_eq!(expected, ext); - } - - #[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"); - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_blake2_256", &[]).unwrap(), - blake2_256(&b""[..]).encode() - ); - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_blake2_256", b"Hello world!").unwrap(), - blake2_256(&b"Hello world!"[..]).encode() - ); - } - - #[test] - fn twox_256_should_work() { - let mut ext = TestExternalities::default(); - let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_twox_256", &[]).unwrap(), - hex!("99e9d85137db46ef4bbea33613baafd56f963c64b1f3685a4eb4abd67ff6203a") - ); - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_twox_256", b"Hello world!").unwrap(), - hex!("b27dfd7f223f177f2a13647b533599af0c07f68bda23d96d059da2b451a35a74") - ); - } - - #[test] - fn twox_128_should_work() { - let mut ext = TestExternalities::default(); - let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_twox_128", &[]).unwrap(), - hex!("99e9d85137db46ef4bbea33613baafd5") - ); - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_twox_128", b"Hello world!").unwrap(), - hex!("b27dfd7f223f177f2a13647b533599af") - ); - } - - #[test] - fn ed25519_verify_should_work() { - let mut ext = TestExternalities::default(); - let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); - let key = ::ed25519::Pair::from_seed(&blake2_256(b"test")); - let sig = key.sign(b"all ok!"); - let mut calldata = vec![]; - calldata.extend_from_slice(key.public().as_ref()); - calldata.extend_from_slice(sig.as_ref()); - - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_ed25519_verify", &calldata).unwrap(), - vec![1] - ); - - let other_sig = key.sign(b"all is not ok!"); - let mut calldata = vec![]; - calldata.extend_from_slice(key.public().as_ref()); - calldata.extend_from_slice(other_sig.as_ref()); - - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_ed25519_verify", &calldata).unwrap(), - vec![0] - ); - } - - #[test] - fn 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"); - 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()]).0.encode() - ); - } - - -} diff --git a/substrate/executor/src/wasm_utils.rs b/substrate/executor/src/wasm_utils.rs deleted file mode 100644 index 5459266ba46c0..0000000000000 --- a/substrate/executor/src/wasm_utils.rs +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright 2017 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 . - -//! Rust implementation of Substrate contracts. - -use wasmi::{ValueType, RuntimeValue, HostError}; -use wasmi::nan_preserving_float::{F32, F64}; -use std::fmt; - -#[derive(Debug)] -pub struct UserError(pub &'static str); -impl fmt::Display for UserError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "UserError: {}", self.0) - } -} -impl HostError for UserError { -} - -pub trait ConvertibleToWasm { const VALUE_TYPE: ValueType; type NativeType; fn to_runtime_value(self) -> RuntimeValue; } -impl ConvertibleToWasm for i32 { type NativeType = i32; const VALUE_TYPE: ValueType = ValueType::I32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self) } } -impl ConvertibleToWasm for u32 { type NativeType = u32; const VALUE_TYPE: ValueType = ValueType::I32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self as i32) } } -impl ConvertibleToWasm for i64 { type NativeType = i64; const VALUE_TYPE: ValueType = ValueType::I64; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I64(self) } } -impl ConvertibleToWasm for u64 { type NativeType = u64; const VALUE_TYPE: ValueType = ValueType::I64; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I64(self as i64) } } -impl ConvertibleToWasm for F32 { type NativeType = F32; const VALUE_TYPE: ValueType = ValueType::F32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::F32(self) } } -impl ConvertibleToWasm for F64 { type NativeType = F64; const VALUE_TYPE: ValueType = ValueType::F64; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::F64(self) } } -impl ConvertibleToWasm for isize { type NativeType = i32; const VALUE_TYPE: ValueType = ValueType::I32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self as i32) } } -impl ConvertibleToWasm for usize { type NativeType = u32; const VALUE_TYPE: ValueType = ValueType::I32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self as u32 as i32) } } -impl ConvertibleToWasm for *const T { type NativeType = u32; const VALUE_TYPE: ValueType = ValueType::I32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self as isize as i32) } } -impl ConvertibleToWasm for *mut T { type NativeType = u32; const VALUE_TYPE: ValueType = ValueType::I32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self as isize as i32) } } - -#[macro_export] -macro_rules! convert_args { - () => ([]); - ( $( $t:ty ),* ) => ( [ $( { use $crate::wasm_utils::ConvertibleToWasm; <$t>::VALUE_TYPE }, )* ] ); -} - -#[macro_export] -macro_rules! gen_signature { - ( ( $( $params: ty ),* ) ) => ( - { - $crate::wasmi::Signature::new(&convert_args!($($params),*)[..], None) - } - ); - - ( ( $( $params: ty ),* ) -> $returns: ty ) => ( - { - $crate::wasmi::Signature::new(&convert_args!($($params),*)[..], Some({ - use $crate::wasm_utils::ConvertibleToWasm; <$returns>::VALUE_TYPE - })) - } - ); -} - -macro_rules! resolve_fn { - (@iter $index:expr, $sig_var:ident, $name_var:ident) => (); - (@iter $index:expr, $sig_var:ident, $name_var:ident $name:ident ( $( $params:ty ),* ) $( -> $returns:ty )* => $($tail:tt)* ) => ( - if $name_var == stringify!($name) { - let signature = gen_signature!( ( $( $params ),* ) $( -> $returns )* ); - if $sig_var != &signature { - return Err($crate::wasmi::Error::Instantiation( - format!("Export {} has different signature {:?}", $name_var, $sig_var), - )); - } - return Ok($crate::wasmi::FuncInstance::alloc_host(signature, $index)); - } - resolve_fn!(@iter $index + 1, $sig_var, $name_var $($tail)*) - ); - - ($sig_var:ident, $name_var:ident, $($tail:tt)* ) => ( - resolve_fn!(@iter 0, $sig_var, $name_var $($tail)*); - ); -} - -#[macro_export] -macro_rules! unmarshall_args { - ( $body:tt, $objectname:ident, $args_iter:ident, $( $names:ident : $params:ty ),*) => ({ - $( - let $names : <$params as $crate::wasm_utils::ConvertibleToWasm>::NativeType = - $args_iter.next() - .and_then(|rt_val| rt_val.try_into()) - .expect( - "`$args_iter` comes from an argument of Externals::invoke_index; - args to an external call always matches the signature of the external; - external signatures are built with count and types and in order defined by `$params`; - here, we iterating on `$params`; - qed; - " - ); - )* - $body - }) -} - -/// Since we can't specify the type of closure directly at binding site: -/// -/// ```rust,ignore -/// let f: FnOnce() -> Result<::NativeType, _> = || { /* ... */ }; -/// ``` -/// -/// we use this function to constrain the type of the closure. -#[inline(always)] -pub fn constrain_closure(f: F) -> F -where - F: FnOnce() -> Result -{ - f -} - -#[macro_export] -macro_rules! marshall { - ( $args_iter:ident, $objectname:ident, ( $( $names:ident : $params:ty ),* ) -> $returns:ty => $body:tt ) => ({ - let body = $crate::wasm_utils::constrain_closure::< - <$returns as $crate::wasm_utils::ConvertibleToWasm>::NativeType, _ - >(|| { - unmarshall_args!($body, $objectname, $args_iter, $( $names : $params ),*) - }); - let r = body()?; - return Ok(Some({ use $crate::wasm_utils::ConvertibleToWasm; r.to_runtime_value() })) - }); - ( $args_iter:ident, $objectname:ident, ( $( $names:ident : $params:ty ),* ) => $body:tt ) => ({ - let body = $crate::wasm_utils::constrain_closure::<(), _>(|| { - unmarshall_args!($body, $objectname, $args_iter, $( $names : $params ),*) - }); - body()?; - return Ok(None) - }) -} - -macro_rules! dispatch_fn { - ( @iter $index:expr, $index_ident:ident, $objectname:ident, $args_iter:ident) => { - // `$index` comes from an argument of Externals::invoke_index; - // externals are always invoked with index given by resolve_fn! at resolve time; - // For each next function resolve_fn! gives new index, starting from 0; - // Both dispatch_fn! and resolve_fn! are called with the same list of functions; - // qed; - panic!("fn with index {} is undefined", $index); - }; - - ( @iter $index:expr, $index_ident:ident, $objectname:ident, $args_iter:ident, $name:ident ( $( $names:ident : $params:ty ),* ) $( -> $returns:ty )* => $body:tt $($tail:tt)*) => ( - if $index_ident == $index { - { marshall!($args_iter, $objectname, ( $( $names : $params ),* ) $( -> $returns )* => $body) } - } - dispatch_fn!( @iter $index + 1, $index_ident, $objectname, $args_iter $($tail)*) - ); - - ( $index_ident:ident, $objectname:ident, $args_iter:ident, $($tail:tt)* ) => ( - dispatch_fn!( @iter 0, $index_ident, $objectname, $args_iter, $($tail)*); - ); -} - -#[macro_export] -macro_rules! impl_function_executor { - ( $objectname:ident : $structname:ty, - $( $name:ident ( $( $names:ident : $params:ty ),* ) $( -> $returns:ty )* => $body:tt , )* - => $($pre:tt)+ ) => ( - impl $( $pre ) + $structname { - #[allow(unused)] - fn resolver() -> &'static $crate::wasmi::ModuleImportResolver { - struct Resolver; - impl $crate::wasmi::ModuleImportResolver for Resolver { - fn resolve_func(&self, name: &str, signature: &$crate::wasmi::Signature) -> ::std::result::Result<$crate::wasmi::FuncRef, $crate::wasmi::Error> { - resolve_fn!(signature, name, $( $name( $( $params ),* ) $( -> $returns )* => )*); - - Err($crate::wasmi::Error::Instantiation( - format!("Export {} not found", name), - )) - } - } - &Resolver - } - } - - impl $( $pre ) + $crate::wasmi::Externals for $structname { - fn invoke_index( - &mut self, - index: usize, - args: $crate::wasmi::RuntimeArgs, - ) -> ::std::result::Result, $crate::wasmi::Trap> { - let $objectname = self; - let mut args = args.as_ref().iter(); - dispatch_fn!(index, $objectname, args, $( $name( $( $names : $params ),* ) $( -> $returns )* => $body ),*); - } - } - ); -} diff --git a/substrate/executor/wasm/Cargo.lock b/substrate/executor/wasm/Cargo.lock deleted file mode 100644 index ed26df817ed7b..0000000000000 --- a/substrate/executor/wasm/Cargo.lock +++ /dev/null @@ -1,216 +0,0 @@ -[[package]] -name = "arrayvec" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "byteorder" -version = "1.2.1" -source = "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 = "fixed-hash" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "hashdb" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "nodrop" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "plain_hasher" -version = "0.2.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)", -] - -[[package]] -name = "proc-macro2" -version = "0.4.9" -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 = "pwasm-alloc" -version = "0.1.0" -dependencies = [ - "pwasm-libc 0.1.0", - "rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "pwasm-libc" -version = "0.1.0" - -[[package]] -name = "quote" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "runtime-test" -version = "0.1.0" -dependencies = [ - "substrate-primitives 0.1.0", - "substrate-runtime-io 0.1.0", - "substrate-runtime-sandbox 0.1.0", -] - -[[package]] -name = "rustc-hex" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "rustc_version" -version = "0.2.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 = "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-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "serde" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "substrate-codec" -version = "0.1.0" -dependencies = [ - "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-codec-derive" -version = "0.1.0" -dependencies = [ - "proc-macro2 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.14.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-primitives" -version = "0.1.0" -dependencies = [ - "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "fixed-hash 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hashdb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "plain_hasher 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-codec-derive 0.1.0", - "substrate-runtime-std 0.1.0", - "uint 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-runtime-io" -version = "0.1.0" -dependencies = [ - "hashdb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-std 0.1.0", -] - -[[package]] -name = "substrate-runtime-sandbox" -version = "0.1.0" -dependencies = [ - "rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-io 0.1.0", - "substrate-runtime-std 0.1.0", -] - -[[package]] -name = "substrate-runtime-std" -version = "0.1.0" -dependencies = [ - "pwasm-alloc 0.1.0", - "pwasm-libc 0.1.0", - "rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "syn" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.5 (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 = "uint" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.0.0 (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.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" -"checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23" -"checksum crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" -"checksum fixed-hash 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0d5ec8112f00ea8a483e04748a85522184418fd1cf02890b626d8fc28683f7de" -"checksum hashdb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f1c71fc577cde89b3345d5f2880fecaf462a32e96c619f431279bdaf1ba5ddb1" -"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" -"checksum plain_hasher 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "95fa6386b1d34aaf0adb9b7dd2885dbe7c34190e6263785e5a7ec2b19044a90f" -"checksum proc-macro2 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "cccdc7557a98fe98453030f077df7f3a042052fae465bb61d2c2c41435cfd9b6" -"checksum quote 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3372dc35766b36a99ce2352bd1b6ea0137c38d215cc0c8780bf6de6df7842ba9" -"checksum rustc-hex 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d2b03280c2813907a030785570c577fb27d3deec8da4c18566751ade94de0ace" -"checksum rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9743a7670d88d5d52950408ecdb7c71d8986251ab604d4689dd2ca25c9bca69" -"checksum semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537" -"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "db99f3919e20faa51bb2996057f5031d8685019b5a06139b1ce761da671b8526" -"checksum syn 0.14.7 (registry+https://github.com/rust-lang/crates.io-index)" = "e2e13df71f29f9440b50261a5882c86eac334f1badb3134ec26f0de2f1418e44" -"checksum uint 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "754ba11732b9161b94c41798e5197e5e75388d012f760c42adb5000353e98646" -"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" diff --git a/substrate/executor/wasm/Cargo.toml b/substrate/executor/wasm/Cargo.toml deleted file mode 100644 index 44e0dafe5d995..0000000000000 --- a/substrate/executor/wasm/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "runtime-test" -version = "0.1.0" -authors = ["Parity Technologies "] - -[lib] -crate-type = ["cdylib"] - -[dependencies] -substrate-runtime-io = { path = "../../runtime-io", version = "0.1", default_features = false } -substrate-runtime-sandbox = { path = "../../runtime-sandbox", version = "0.1", default_features = false } -substrate-primitives = { path = "../../primitives", default_features = false } - -[profile.release] -panic = "abort" -lto = true - -[workspace] -members = [] diff --git a/substrate/executor/wasm/build.sh b/substrate/executor/wasm/build.sh deleted file mode 100755 index ab71864481782..0000000000000 --- a/substrate/executor/wasm/build.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -set -e - -cargo +nightly 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/substrate/executor/wasm/src/lib.rs b/substrate/executor/wasm/src/lib.rs deleted file mode 100644 index 3046fd1f166ca..0000000000000 --- a/substrate/executor/wasm/src/lib.rs +++ /dev/null @@ -1,122 +0,0 @@ -#![no_std] -#![feature(panic_handler)] -#![cfg_attr(feature = "strict", deny(warnings))] - -#![feature(alloc)] -extern crate alloc; -use alloc::vec::Vec; - -#[macro_use] -extern crate substrate_runtime_io as runtime_io; -extern crate substrate_runtime_sandbox as sandbox; -extern crate substrate_primitives; - -use runtime_io::{ - set_storage, storage, clear_prefix, print, blake2_256, - twox_128, twox_256, ed25519_verify, enumerated_trie_root -}; - -impl_stubs!( - test_data_in NO_DECODE => |input| { - print("set_storage"); - set_storage(b"input", input); - - print("storage"); - let foo = storage(b"foo").unwrap(); - - print("set_storage"); - set_storage(b"baz", &foo); - - print("finished!"); - b"all ok!".to_vec() - }, - test_clear_prefix NO_DECODE => |input| { - clear_prefix(input); - b"all ok!".to_vec() - }, - test_empty_return NO_DECODE => |_| Vec::new(), - test_panic NO_DECODE => |_| panic!("test panic"), - test_conditional_panic NO_DECODE => |input: &[u8]| { - if input.len() > 0 { - panic!("test panic") - } - input.to_vec() - }, - test_blake2_256 NO_DECODE => |input| blake2_256(input).to_vec(), - test_twox_256 NO_DECODE => |input| twox_256(input).to_vec(), - test_twox_128 NO_DECODE => |input| twox_128(input).to_vec(), - test_ed25519_verify NO_DECODE => |input: &[u8]| { - let mut pubkey = [0; 32]; - let mut sig = [0; 64]; - - pubkey.copy_from_slice(&input[0..32]); - sig.copy_from_slice(&input[32..96]); - - let msg = b"all ok!"; - [ed25519_verify(&sig, &msg[..], &pubkey) as u8].to_vec() - }, - test_enumerated_trie_root NO_DECODE => |_| { - enumerated_trie_root::(&[&b"zero"[..], &b"one"[..], &b"two"[..]]).to_vec() - }, - test_sandbox NO_DECODE => |code: &[u8]| { - let ok = execute_sandboxed(code, &[]).is_ok(); - [ok as u8].to_vec() - }, - test_sandbox_args NO_DECODE => |code: &[u8]| { - let ok = execute_sandboxed( - code, - &[ - sandbox::TypedValue::I32(0x12345678), - sandbox::TypedValue::I64(0x1234567887654321), - ] - ).is_ok(); - [ok as u8].to_vec() - }, - test_sandbox_return_val NO_DECODE => |code: &[u8]| { - let result = execute_sandboxed( - code, - &[ - sandbox::TypedValue::I32(0x1336), - ] - ); - let ok = if let Ok(sandbox::ReturnValue::Value(sandbox::TypedValue::I32(0x1337))) = result { true } else { false }; - [ok as u8].to_vec() - } -); - -fn execute_sandboxed(code: &[u8], args: &[sandbox::TypedValue]) -> Result { - struct State { - counter: u32, - } - - fn env_assert(_e: &mut State, args: &[sandbox::TypedValue]) -> Result { - if args.len() != 1 { - return Err(sandbox::HostError); - } - let condition = args[0].as_i32().ok_or_else(|| sandbox::HostError)?; - if condition != 0 { - Ok(sandbox::ReturnValue::Unit) - } else { - Err(sandbox::HostError) - } - } - fn env_inc_counter(e: &mut State, args: &[sandbox::TypedValue]) -> Result { - if args.len() != 1 { - return Err(sandbox::HostError); - } - let inc_by = args[0].as_i32().ok_or_else(|| sandbox::HostError)?; - e.counter += inc_by as u32; - Ok(sandbox::ReturnValue::Value(sandbox::TypedValue::I32(e.counter as i32))) - } - - let mut state = State { counter: 0 }; - - let mut env_builder = sandbox::EnvironmentDefinitionBuilder::new(); - env_builder.add_host_func("env", "assert", env_assert); - env_builder.add_host_func("env", "inc_counter", env_inc_counter); - - let mut instance = sandbox::Instance::new(code, &env_builder, &mut state)?; - let result = instance.invoke(b"call", args, &mut state); - - result.map_err(|_| sandbox::HostError) -} diff --git a/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm b/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm deleted file mode 100644 index 9feda33fa2347299ffba1b67593813392d89a588..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48108 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 CZ+l<> diff --git a/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.wasm b/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.wasm deleted file mode 100755 index 2e44e7cbb66248b6e4256896822b89d4e658b4dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48318 zcmdVD34k0&bvIr;-80*}v#Xtzd`Pxz^{!=kb*<(;79ndSt!?>|V#&4)IO92bq?Pva z?yL@9621u`7y=2WV~%iq*jynT#sotkm;?fZV9cNJk4em(d>BFqhd=!Ney_S`dUjW` zY~nA;O5I)Ot5>gHy?XWPb&o2~j2lWRawC%#039@OtZVkvbwv?MO~H=>Fu(t82-9=!(X3a#9~(1m@;fTY#ORB zvdlbc6ptNySVfPj;`{FoJUf0CIz2M(j!#Y>38`fz;9MJ>cB^29*@HKt?B2vQMII!17RpvBZX%aXS)$&+LXXJV% zqcvhG-y%X`h#1Z-aDg;r%QD|O?RPm zuso)=boLmkXhb^(&O3ke(BSH>zr7|NQqfDjJP}h-2ViWc!%|k)pbYsUtmxqX@ObiNw@^J<=0) zYzGksnNH-AGh?To;dI1|?od>~^?Su_$LfzT(W_-f9p%{BkP>v92tr5!K90de+X?S? zLRqlsX8jKh7zdmXu{4~}ejslU%LuUyOWR>!*`cxQh^hXt6CtkQk)DX-qZn~IG=jE9 zkdXQ<$LS$qQF){2I`yJB>p^v@M*lEBR?Z-~XvqL|-&>i9zv!Y$9 z_)$!jSig$)D493KfmleX=;sV0a#VLp-_+H9^Z|J9 z9{|yseL#Qk&%$tp^>DTNfT03LU@{IV$Blgm>oJSyF(bHsQ}@A=Vbe9F4}vcFTL#f% zD3e16`k@cQ%|3+nfPMbBgMlWz*R;A0q1sHG!cq8pW60`mox+ zL8%uga6<8x{TP&v;+s%tZl(!H>{jvVPknvECFohWA2oVZ6tEb>HZB5T1~ z$>DU1M2o5)BDtuBRWAy56fLg|W)u@JgOwBs^><=iY{yuyRPkZs`rdci zODUlMc{w3tR`n@G42urQYV-z*cbrsl4Ct_m17KX}@3#dJ$CTw7TitR$h=jJhwg*D! zUB3cRNx}ic!Mb2Mov61Q%qv3%+(7>B4iFV(4szK?QHDCeH|Qu5`a6g+#(KFPXKTPH zKCLd{I8!PvFB<)w-H<#;?Y)G{CPL?P^rnZ^SrUsJEUvg7LyCmuMGP}VZ>La+7=lW3 zNrZ*~?P=3%qzUe9A6z^9?t{hN>x++T_$J_UP65K=gT*H$u??pc5<{JKAVAVjIJ;=| zcM5MU?YG+~+C_>+LYrGOg<1i^3Qf_ci0%w&8Ujt5j6to0T*3M%GQb{#?J-y_NF6)C zdQC_y0WboDoKw`CBS95?nPC_$fro_-lM;)9BXTqZRzo{QnW!PGM2ukaa&BYfy%2I& zBH_zWG&os9Oz+0ysoS*F#qyaAIb21tCT2r}5^@k)iWI<6)FfQsx}H?#>JPVs<@^vU*jOY* zhMHKo?5INpCaRTuEL!^^BDt~?6nPpVoKhabXPx3$V&Su#j{YtT4R#A0RhB`wFgh$% zgubNuy8yQMG?s$LMPu)BIxfH*>GFz2F`XdSP)x)RX+ho3V-Jr2WP0g8G*BP_)v(8c z%tjEJcdLOX(ExSk71#H7JCIJrNMpLcQ&>t`Rnk^J9$w?7mXDw>%R1e~#SJj%lj{0u z$DH<9m7J&lak;?wIN<}Ld_YD)%?2GVfRbklamb@2O&3X{q+!MGqL$ZU4@!b6Q6fR2 z40H=wLK=}C^o9g!W$4z?8G#b@pe%C0StME%I6rAXdQl@${fneNleTjlyGV&HXOYt* zl+csQ2PFtQKE$X-0FtVkl;{djBI;8DD>VfE0Y_0of=JyzU_-V!og+OS`kh5GG)r0N zGw@LiQnrSj&ix=vaWj-BNL1{JnB%B3$*IUi08218Z0v?fh9a#Y!CBY@Sv3Wl8U^CI z0|AW!TKNGmjuy^XCJlgsV%`MD;IjmY@&_F3na~-kG;!=>Oka<{K$4L$Wq?e--HpAY zOyv`^?@5(G-wtBNv5SFa90^>;LoGpx_ADrtzO;mPMrp~V6BIpZ!MC72D!^6x>zq8)_hE!u$! zeo#eWrRY{$UiHJfpMK+K-hJ1#(Syw;TtPgm;*(s&@cjPmdPSxdr`K>*T-gj^QC^2a z$6l&eX`n%^R~0`awXhl>O#OFILW2l7NQvz)-i;buujktk49}=T6papkUBxw;?p17sCr821sPV8hG=*~xHYfJPK-6tTazAJ`JUbD(pJbN4%u+4)Xw4l_i; z8?q{pdR;<`4sI$s97F&m+!%;DppiCVbisrHP9()pN;dl3-U||941;1oiV+h@2UGz< zp4VMvVe}n5!h#`oP5@x@5^hG8V^=Knk!@g;;ucL+JHWA!4x!@FOT8tEn>XlgSjo`0 zkhY6Ji~v2;K|<`*evnCT9D$AC#h4Aa8&vcw5!1p5%GUj1bzndbAy9NceFs}AQ5GEo z)*f_)E4#IQAadq_qM;B`M;+jl;igof)3%=K`i^y8wZ)-9IRhOJ4A@(iVPUt};0^aZOD3@%tuAeW0amuk=(D*&j6?jSb;eS`P|AzMs%#I|Tn_jf@S!GMOn zBT7T$NH54pu{gqUVZaDhNSS3qWv2`3a?V6H(Tf(b`eEVVdIGZt~O)jDeik{pVAVy1F99Y10hBT4Jya% zGr*K&#=K*}+~f7XpC(aIP7sY0!?HuC5f*@8ufRZ~21vE#rN@pPLqq8;2A4X>5?}`X zfd#RUEl{us9;HePge^jan1WU+iUG?j^j9ntFhNlev5i~NhQl^OSfBxa!74y&fhxMI z9Pn5I;XLhUKomn9r>+uyV2g97W8dBnhSlQ%dXqj6D=O-o9JCM1NHHh21~E{YtX9NO zw>|wy=RL!Ubw6MQH&U3GpbEEmln)pzs3Le+uzjfu2g008(uo5Dki48$SOp;tF}}eG zq{CXd3awY7#92jb(Jl1CT4do=$O4{dOK&I977Un}wYb2OmNX;c9f}cohpHWSmgFMy zGAP0j6xdb}1q>t8^|Fh73nXn@JhTOK#_JFpv3WgXBORAPy85!urj!R?wZna`$OCQw zc}Tbb^8b0rieNr2QRWiqS;eDcBHLx=asM{}e&Xf>W`PStw3`>wFjll;%P?>xB36vp zQjCp(xivq{L>jk~juBfcg^WN#^id-eIvS$UKyUR$&KIMxQ1M51KCFtA9FHRa$&dPW z@ku6jz$z`wrHcQ-I8?Y;XTK7Rxu{c=bL+s00_s<~6rcda|6!yP4bW09wsxrBGE7@K zXl)J^@4QPIIl~EMU{XX1CoM&WUZgogHR&N6Fnz#gik=;PFKi)!ZaKkj zbcHrc;{r|$HlXgpjiYO9oLI@$6&e_tWP@HimfB)OG1ap)$rm5-u(-0lb)qgKs3#=F zEXPymqhek^kKfee9@MWIAxn3POEfJ!A+B~yVN2#L^rFz8tUwftSJ;d4;RGom^<$(a zG&IvwvDi~CA09*s4?jXp#X&=$pM$|`(a!<>AfZHQPaXQE?<&qS~x2AK%aAQK@vhlwcfA6NT5 zwkkf>3Vyj29NZ5K{33`f4Mt+Yi0wyMdXQp-6Q;Q0GRzsXMI`Kzm+oJ!gfF>zV27Y1 z6jG3aI)Y8Agm_xiwm5fdpv04CDpdTj!k$wP z@L@fpresDPGdRM58RZzUl@NiCQ_wQtX()de4s~g{@pw~ip!?lm$D+58ZRGKr7sdj= z4y}F*Qdo@5#cxX}j`@ypZ5#`9wz7jkFY1ZF%qgBiyZw!PWN$7X;C7qRBBatyW|6ab8t=wW=$LVzU4 z4z2~8q55$=WIVYnhhv7U3>aZDGuUA>m>Da>Hr5`gh+o6+a)JBJuw{I>JSCQB1E~VqzQL0 zw5=b<#3%X(8}(Ao0(~6O>vUcSF7M>tGv;(|@9AT>G0^M8@UIt>vzL<-sodrR{$@L0=DO3to&CJllzF@9B1WiEt-62*Cp~ z;H2Rqq;_FvjNa1n1l-y8KFsl6{Oi&b=!4}6m=F>v1yfZYOam?4x=)CkY}}s-H?+S1 zZk=;*>vF+f$D^NjwHHFFkW|0 zth-OlTs&8y?a69jZ30tLn)vd;pZOgqW07+61%Rf6clZJzz%fP~!ALT-VG8MmY!?Kk zGy=$gEZ{^3-8j*^^;AF`q)7~Ny{kV~1cwgv;-DxB!PO7(+;2x)=_5Tb{`X>W0a73y zT#4YQ7r!9bYxN_%It(g>R?uey&x5X>M9 z+0b;nN z5f#?8?{M0ubkB``_IBR=39g!zzlG-BE%!^)lauCp4;T}Vb zcLEJcw5AdsR`i+Gl4J0>hMng5R7+mo)Q#Znr_BU&bff!-gwaG|ggxmleq_}J*Ds_a zIZlW=Qge6T(vhIk>kU2)B;u?#Pe+P=tP2}A@i-s2VirE|nV72gUaCc0#7v?9*_8>Y*L;DBEpnuZfKJ#nKC8^2I79uz7ZKBy&l{(DHN zFrHt5W*5Qp>$06vYCF{ZQxJbuht6jn{|sD6Qr97$e;>t@@V;;Aa*x46J;)5CZ^vUL zDs6gC->8B$8d8BvY`Bm?^3gNRCNXIr)S-4d37nq~bzG``@Tpev3aqvFG zbbV~{kP;8a=p#lv9#6!R@l-q=&&0FwTs$8yB;tugBAG}f(uqtWo5&^di9#}-OeB-Z zR5G2+B(up}GM_A@;;BR`nM$S7sZ1)H%BAwDLOPyKq?74XI-Sm>v*}zqpDtwLnM5X; zNoCTROeUMjW%8LqHl9snli5@@oy}yk*<3cCE#%_4L@t?2<_bVqK1DC$Nb%2+I|gKdC(H117RY0g)R8mUCh7k+1E-{kCA&8fJ~)b!-R(Hhb#N1Vn!*O}H_{erH}&d%!O zG~byRJK_ZD^ZkfG{hI6NmGM4>RJ-1-!8&mV`aFm?ZQN|Y2o1K_; z%hi3X;#4PVF6PvoD8GHfm4G(0VQ=~1hOLv+?uMD!%1ncAqy$19hU>le4>rt9S2qj| zZ8$XAK&SY!O-<(4=Lzr>Og;1h)j22|#wT?iZIw~5t~{f`S9HWs91V|7OwBfMbk1@e z{_8tpAC8*!$w^hD5jbjA$~P!60Y^LQqZ6Yu``lW;61zyW12f*4Jb>sc?n2SERm9Qp z>vl}>m5$-z*@;8b<*C8d*Ey3DPT9Hc^2rJJItMRN%z_vnWtBc3_erUuJH-rnSH#h; zgcQFjDWn04OBtFvqSg)WG|;56BWeP)5^G1)#G4{Y4zr_tKS#+%E*dG%fb8rqi$_N% zs-@~A_`sc3qUl5}VvjN68#a!@-{Gs$i}~cAp&zP$t9zHE7b%dVB95{YY5w15cSMJ3 zZe?~a-o~jLFWoTO(pBUSkT12~hX88*As)(p9d3sGX52&e zJ8?HPgzqrf@XCBv%6wVMJjycm-eC47X4TH#OgVT7ONBc#Ug@CJ|ziqZqfwSoiq3e@h!Qz+j>%B#rrNQ)f8 zElOg)85fYd4G(3%h7siM=~k-SuluXBlu7}h@iQ!n>RMd$8D=k1PsA`iZ0h|4EiBL^ zUKfc6$`i9L4un!9!Tbj9Kn@#dVITPr!99rEqEF&hM5fWW7o_Ta1#Y$#`w~+B9I2M- zYaoRq9J2b3`oT!-HUwpC?AYDSpz0k!IB&%s^?+I`_Dux;f{<+0`xJmk%HyG8`;(HNTaz1iZx7S$~?ftZ__8o+Oi97&pAZ0P4EE@S&mPzU||Bi4L7l{zL z32CR?h8xPYsMO*(oWS!tOYYGgRXN5xWtQ`854JQiY}*69GOCt~VwN z*Z*Fy{=!u?%PAr=zx^=KZH?)7fOnKr;nxUYD*Ou`%KmrUk#@W;aLP5i@ElMmw$&#W zM#(3XOppXY$#h&4NzQV*Dk3{OQG0jK2f7Cz+pG@*M<44txax$bpCOQ1)LiVt)$vko}jqoAzgM zx9rd19=87)_lW%k+&k${TRs$#xj`3#b<{4;StzO0h^7ud4W~4M;1MRZ!7RyN3=-gz(;-`z-2Vw#7_8Q z@fzJDZ;F$lq}QYsdTrw2FLwxC061RPy!C{HgL|WN?fpo*52Xa@6aq3Do4f$PU5McO z&|dgGyoK7z9Y4}i43;@Pz{ZLl!wU3jzyd10x8jK;%Ed1O7bGpaPfDHf3FbI(66Wg8 zqMWg|)%U+3^A34Qx^^I`kAT)MLIu=l4YRD{yx?uX4w}7?>1dWqzSpdubOOrY<*R5OmBOZB5I zSM^rsC86&Rfs#wD*mXefWLtLq&q`n=4?L_?tQ)A2j3gt}bBB29@p?Ysb3}XsU zLv=PN2s-}}g6A=4sorAE#trnS7i;h-{j&Y&bpK3fv zM+wGaPujqNC@qb>m+%>MT8Iv+UK>nqTW4{t-n5*!okcFckU`+5MZB!bdr@B*r+iEv zDl!afK<~E@LC3i0pQ=0d=SY#DC2M|C_u_1SDvdz3x&@bUPsfTq7&-nKv@fXM!@wG_ zwgB$CiKgni5NVE7l7y5g*9^o)6?VXy^YYkhkdpArOss zTU&J?b|+!qj=&v&XvNXFvykGfKu`NQhW(pyQ`TEi!bFC84`aA+M-g8HzXjPX5iC;c zLl6~_hj{xeZUcXV+kTWc&NY69Api89;XZ`8GTZi?m3cAe`jU6z2GG4vpi;~had96~ zY}NMoL)*beYX~Jf+L*L-0W<6T1z;HGpKG zN0D(vPPra8@Zn$T$4WG&jB_qP_FYIZ7DcW?@PrCpZv_QCPCG;B5F@8i+=1mqdA|hl z@Jn#R4&?Xo2pMf8`JWk4cS>hLoi-j_j(IX~Az!%zRHPtK*8j}TJ{PPtSCh#iccc5F zH~cXI&>OU#cMl?WpDCNtP!&R9uH-I z19!vz7LyQdZ4j0E1&uZy@8RQsL2rk_c)JwNCub1u= zn)a6NPqL}O^G<<&u^pFjWyCmd7Xo)PayKF$>%;aKm+>bNV|mYF=()IzQ^Lmbmjoh% zr-0M{33T~GEXXzt{l*_ytAB>BY(qNmP{!-5)lgp)XM~8^H(P7EL5W)s$1%M!?zPsI zftG`?c~NN1-yr-JuWab9&eea0vd#F_h^&4wZm&lPEEdY#9#X3p0em-LGr{<%wU)YN zp5aehD|Vxsdl)`9ytWJBDTdDrulo^zyaX4Q-Hu1q>TiG>?nZnS^Vf#guA~ z=e>qwr1iWvGk9D*?_U5M_(Wcf!HaMdmY<5-7#ny94IK6(pSFzkcLLHe6#fDROiH}f zT1_hbHLkxUf^WwpI4^`V6*9B&aeVcFm@a=Au$1yqSRvLwiT%R=0>RH&0i!A|8h1q72bvH^tfnyD+{RnGEbnS<{w*A`8c(e8gQ39Qk8f^PqfWib) zX19hAM;rucw%?35A>>d3Kv-kNt-$ATw%NB4D(Aj<4ru@jS|vRk52DmZI4defdr}Cj5lll z*sqNmq|*k>rXZb$5f9R-8E--;qC}8R53_wuC$%Dl)*nLxm~72u2;YvvG|wA9x7NHq z5I4h}Yc_lF4ahga%v{KyB(QtzwKpPca~)V|ta&ZM?*bel%M%@IzKD9w_%Av{cKLDZ zdQ_6X)lZ=tA4fIzj?GgXK8Co6Lo2MsAxit=@Yl%mdrooKj7!fc4*$(Vg5prXodZGe zLV}NmMP6QjII%>LO9`fbg2e&9_y23|NWL;0p*?YUpCfq*vpzKV=NP z1-NpREmJ*$>V{O>Hvi!Wdd=`OlHE84}JO=U)3(V1ayY{lA4SQ!CJ$SSl7> z+9TM_BO6;PlCF=UrBq*FG0C?;0ou^4}nE(K19{ZX3&g zg+LY^F|AHREq^UQeV*XHk1qWJ*tl$e;Rq>yYOPX$`@%mPYrXEPtj11B?0V<^<(-d+g!Pa36T`5Gb zx>$-l&xbMZl*Z*~L4vKr7@jTSFouVK5MVqolf#&^y}@#{e)wPf;y0ABE!HdnG!s9LpKP z*O3k?_Dcj`z^3h32AK(_b`O;hd@HWdF!bq55x~;336DDk`M#|Pa*46*zKi@IXcBpX zZTt+kwsS7A+VQlWRy%5zi8@e5d8L5vCAh2^RGzPL&)+}%K-!o6g9NocPh$+QQIU@T zndC6kgFT47bcIqM#AR$wtwe!$p_9gTmAn$+S!BNw?UE+Oy3~t7t%sSN1`O)Wi7F68 zqIwAR{{@%%0wB2sjej3Cv@*H_F%ZyKMpGzmp!wkGnm7Qo9h+Gs;Ilhj`?Pd9sHR%f zlINbTY09(}I|!6SKYk4YcThjJAp2YTG3Q}FO_R#{AWF#Tnu;y*-1x@f@e5BrrC!tD z>m^OiRvJg9+?jWw3M%05Q^15W^a^)ocC4Ykf~1idcniB%;#=2n{oK{ycf@Z)iLLZG zRJ7F3=H>KtaJ9M+v7M7M@*QlkGps`515Fs)+);(9fk$ciW7 zLHXjh9Ca7RM)yvP!x>`|u6*$?xgLbDN%Ww(sp1y~vR*&yJ{OR5{aRC<#Mj-O zXN{0LhK_FcynTb1iX+^~LR}ERF98|eu`hx{>&BA>G?y})07IKdLh8Gi#+xPXr_fc& zPr-f~!&gVjR)hv|ZG|s8{}$m2sdop;Vk?c59|X#J?AI#34ijiIqxjPR#sVoNq=wY} zNTG`+mDN@BR>r8$jt>um8sNOi=}X{ouHJy6Z3&cL#hL2p=+=GZ>2ej$x?9KK$19FV z0d|BiLKBZ+3jZ!b?9Zi>lT&)aAR@@&B_ddczKNLTX-u3qA#%akWI2FAfX^RBt~Z|% zQeO$NjXrEYE(R^=b+PKjx*zrN#_(`)dM|!gV4`tV;#x&V0B;ZjC8km8V4_u0(UD~P z_|({_TT^tISTznW`>Ur%At^Sa35f>kUobg6UT)A|nVuTKnYD%$FPOm0+q6miJv;&n zVpmO$*2KTT!)O(J&BX^{K8CG6s4t_(yC%n7anotaCUV%SQHUCP>}Y&;O}SMOV8w=_ zZ$b-8y3cH%5wDe1FJ`OPsub#b!$v2c%kj$z;>w~qYGWHg=aTE%P2#^&pBS9myLHZS zNwwNpzX`tg#25I{&n`W5X2SC1=teaJA`2#_c`nXW9sVGj1Y)#N4>51=q39yi;; zB^v|13znvSB|HE$R0dZ_y%;*k`w*fC7@W6wJiH5Yps{f_zs?&f@CpROj7>eGNiduyd-aeTZTfHRe|66TbZhDm(@CnCIGUv$Zu*u2r;-w@ts<{3fyR%k1Hq zdAo2JLz4j!PJ`=l<&vV!w$+&8nQXAFHi^zNe@OtN@|Z&;5Kre%G|g!X7MVix?g>Rb zZNe?7%_9yG{Fey@YsFf5ifhgfTm5DKHz^)g1W`VobfWb+m?ucR&BvZJ<#F}%E7k++ z4!tHYC&(l(aI)8;lS#EdrMMpa>f;PamKV3-(hBw|(WAeZ#{uqf?Mxqf?30 z6;SJIGn+(aoP6my+mgYrcym9Ko!PU($1je4Sd z(R6i^$`?&k&+jbdcHZx@7_HjnD=kDK`e>%{kzPQ_!7QZhP^)L$`};7Vp3&&k##3`S z=34Q^Z|PvPH)?H+#siUEG@PAK^3GwMg+}zM#3n^ciI3WhqUE6_g=v1ebh29JYm|y6 zqo&A8Un|C^rUs`zZ1;F(33)qrhO+KrTvy`a#J&yJD{%RL^rgB37yYFE4441690Gm| zu2m4te}wB3xW10#DF2ThzJ7W!kWR;_Fmk^{PEK7LAxKt^-!RFIs`40elODL>_ZnI*75(IK$vbkbnXcS+TVrn z9$ZyiwLtpZG4tWP1!dlWi@q}t1j-*Toz64=h>MOfJ>W6_?>vNuaM7dXxG8=mLBludAR7p;{S1d`8nqW6pjeSk4|uJ2;^Ul^q1f&i|Mzgc3fBX; z{J%G%?)z{(i0kXPzK!cgxconwdtZ%zC=Fxi_9YXbl(|HmwZvQ$joxj57FMs?ju;L&R-(kF zXcT>m6bzM%V$i3nfNY2X)QL6gbPvIqaB*Z}k-2C%`hxKJ z%kDCO3xSiC{gQOf`+~JVm*Q}kjw+6K8eM=*kS{i;fS~&xDP~g$UkKX zN_#a=Wly5fO-Mcs1u>|n2O0{qp`~6;h~s6ax7s|T2LPkdo@jTJjh@M<*W~gc<#)qD z)>#33(&*FKL!kM*o~}4cC6-)y`kv@DXvjHLaOn>NC|=$Opu~|NR_91sOz0tKuEbEa zsCn)@!e0p|lvM#b5tH*;h@QWUs1D9W4a`lvh_A+M%~?h;x|UHN>ve&m0Jgpz80J~C zIeerYUJJTg{(#1HG_;i6OSEwV3MbouMLqUNHHn5I5H`#GxA4bCCtP?IDN8;qVi?A+ z7xmyonz{zhO7MR~a@R6GFW3L538qr(t0t_9m+xMIr}=MTBW!i`Lx35^iJRGsN|tup zJ8m-@0><$TVjEYAwsC&)fUTamh5w1UaiPi@+!rK1)w>PYZNMd5f7Rl*rNXPFy)7qf zPpneP|1brNsKM4fJfN+S2l$qm`O04MKxklz6$;ztw`T0c_=31)zOCD?dnxz(2@lRK za&Kq}dcIY#df^GN0u?sY#~R6+Tit*SIL?#y9>E(>)1x!9K6{)DF;O+xo#&CI-UnsR zQlB|tr$}6%(CuU=o?eds+#p+3zc|c-Z9QoXj2FT#VX84r+Fd{o^N?wgO4)#Ljdn3F9{?+*y+q11b#nI zPXP4mjy9}tIAy-`X8Wf=u-p8s`KWo&z9+mU5zc`i|2X?#`1FYR_O5%wOJ=PIsP)<- z4~AnejF_Lj>5=eh5%UxHnq=(u@VdxQICg7fdw4wp>l)$M#pb7Px)%^nK@lJlHoNS) zpavkQ_9<3--vZUzK>Wo`_eRXmp(vpO{AGTHi(2hOE@_3$kKA-CyAv`0Ibz>yb%5~y zcEh96ufMa0B>vt*_B}+){MU{lu)q_f_4gHV``YY~U-x3K>Vs1CF?%Rt{@tv7F)94H zPDDUq1RPmBz+co9&9{KR&g!yX7+w;wIV>B11_TO77BTyTJ8Saqc4!r*8V2DH57xs`ke85NcTlEB#C&ZS z(iIcP{O4Ql4|lvOY(*UNuA7E9XCmhN7hf!shEr^j`GcvUu=Qq{Rqr-7V%FYp57Ij# z=56>qo)x)Q*Lh&kP;;Vrb!LKfD8rXT%pahmJIq&yITh}>{!st|YA>@es&?dF^FMBE zVqh;8aQ6s|S4|HIDv#aL2wO7|^EH@|NwAk$5BHfMSkk=ee)MIH19V?yKo0ar%x~W? zWPyM$!>{R>;d9M5n2&%D|Dm@bV&MZfv-h_7rI0jee&A+}{l-9Zq)j8-@c?O~`}h&B zkL>^4KBB}a=BIloXFAOLufG_z9yI^YjTa+!n{`jvVx-wQ$LreyU_cP_XSdt~a@0YN zixGPns@se-tK;*Jlxe>eZ6K7)x8Y|>W05^pC*|OGAt>1RoxR}8pG-8&SK`AoKbhEw zr^C1vg3#-|7-hQ6_ZeEs-rnH2EH-}=-4k94p$RbGzZq#61vwM7)x3PAFX08mzKyY!HCxkz24`D8> z(BgMvcn774jN+rdDBja1#UYA8k1tv_I0ciKrB6bG-zHib&%jTpt1h6W)uzjPCnu;% z{I>e*9ssb^g!xltgbQ8w zODfW6q%hYS;ZwWp5|)!jxRd$>X5hO*Y-5%AjhpXfg85E~XArmVhlF}G)Jw1tfS`TD z{BDQAVqNB4Hv$c;nieO*MyYz|jraQr4|)mrdI?$!S9fpe@=U4FKC5H+*r9XjrB_|% z3{K;lmsJPfe4K$j)=3Pd;zRNE6Qg_g@w0)0Iede$lFa2YPBNazuaD=~C-bXUa~QlOn~5hSBW)1HU0b(rFI~Fh>X9q97I%%5E-UUN9%)YA6bd8?3zR^!_@rw# znNK9L$wDq&C?`ieNLTLM`3#WSO0Wa@IzQ_gPq@{3IT-B6M&5Gj< z7yyGaTH7|dceJ6Jll01on)h+sHdj9H7z}ipX&#i(@;kt9OUOxmlilaj}=r;6xIQrD*yG;GpyGwa&MyC4j;Cy!F z?hEoI&ZyGW+qdn$s3k8j;)%?hTyRsZo_FJwTDjoX(k?#U%!P|SpA&TFxVn5~p8Hgz zJaYic7@VuFaLctLYNtC<<5#)G--;entK=p=8QPuXa)`36lT#>4|ES9B=osef3!Zq*+}wBSk4O zbD4B1S;=K;wL~SIOpK`ASMNA^IC;M`hXzYWri`z9S2J!oSFVp}e0J}E^f?)v3eqf# z16b9~#of4@&AZu3ypjf9UM{5FvjLV$vXl8t70W`lp3P;-iF^w)4F9tMmQ4d#-Yrxz zm1-jCR^ru49gC!ocQSuc&=T=h1;9(@-EtzH$yUd9I)TLuLaX)H!pT)Aua%8|>jylmyR?Ypkkk;>QLJP#%> zs=b$j>sMO_UM7Hk+R$s_uEsQH-|orD%dn@St`0S}&C$s_NK#XyZWUUnIC6LkgoJ%} z9j{kgp}cO>y^F6zyDlcX_ohf)9Kx|mVDe)f-aDb{W97Y2Z1FwwQSMaKq0w4nU(hYc z_c!sEBt2iD*~342u6z13_{sPlI6)`)xiH|fUB;K#sl#DuFHKF49#o~t(aLC}H0_S_ zBl^d8vJzh#kLZG+B>1rllh(jInbB|ZOOpovhC@iR6D5vxI z6@z-Nn#rcqc_7iuRX5%^3WwpFZzh~VqO|hT-7CqzLqno)4GnQnnYeN`nOj+2xs`$V z%H1n3l%)8|@{Au@Mm=8| zoxykPCmN*+T;5T$lCIbCPl+#p72uaEcbyjZK3991ts21_E zll3}&J^;Y;{DF!>KAlPA@M{Xy%81fgLXw~!fX%o!$vk4E3(s!6K)OU4lCFWz=vVP` z2T)bee!5=IXDe>j#Sd(_Se&38wH5J)MsLa5m^7iew0sD5fD-rmrKvKxzLIy7$#lAw zuVw34*FkCF*Y(CYaVG+)pqRiI79IU&^YNcAO6%zGi9Y1{H&q4h{j(3>b!jJ2E&Pz=u=MV&W z1P@|P@TJTWX9oIR&nK(U!3y957b*|-=Yf3a-!LPgrQh^zLpGst%~RJZpwAhu`-C3fSOIzIfi*H7xbhVO=wpjv68a}^N|V#2I3QK979{JLx*N|Fs`(1knLvIM zn-p0sFY7c~Yg^9o<52h|s7xxJtd|q1tiPPM6>Ec&)LKb%JqKu!c}Yq}2r?OUV3tkS zv#E-k%;l5mT)cp#G+5xIKxV0W5Nd@lzm%ts0Boj~jK`@m!6ufg(Z8*Ky@ zFkqO~X^HrPYl^4d?c%Q+VMyJ>WLmFZ!amQiuI!#vTCpBGl@z8o6>SFfnVAx zU)fW|qp0npIawFIwb_>)r~Q&5k{aQIm>=%cwp8TL~A02(y=TFuAtKZhC# zcAzvnAqQe>wMLGE?rK?u@8b3Fqb~z%KeO*Mx3(|Hv1$5P%V`KF#UsCQ{^Lg z?Y+ih-?lePpxkT~f*BM}Wvi)FIa$kA^3bih?k-eNcaZ1Zt!YeM zt-1B}cD?{uuI6g>cqUWD591ZGIZAoi6tki}TgrL1nnbSZokW)WAO7QzSF7oIC7*I@ zsRT4r$o6@qnmBmVFD|o?QrJCQ!dXD6ULG9-D7TWx;ivon5kCc3t#i{%VK=8>6WWQi zPe7~I>e-}QPvKyqmdYidU;4y8QN?FurfNFprfSezYxydESP*M23ANzr4k0yHq8&$D ztW?XjbQv5PFXKlDGf-PlaAVtqDF}yfNOh=$5*)q)l*>AHx&@3wH3iu#<(S**FC*KuolVs;W$a=esp})jXaYFRoM1nGf zAB4fdB?mKJ$5B~6gP(bH)5$VKK_K5-90b+jVc()LJiHJu4-a2Hxl_;7miTB&l z+0Y&E&>!t+eVgVQX3d>%YBy4S)9EUfpvMHA_C44M1m!?j-ct>n2_qdZ_23FA|D!hB z5d+5b=OM<-1jc;VT%hvJIS=aD%+jCKc@ctMj}cOD!`1o%&N+w$+w1`myhHrenmp@K zLL3uTdUX05se(=nX@apS-GQG;QSFd0h_Lo*_%cE~OibcA=V)6#X5eAWH+2JOZbj+V z*IVMI6w!tLr2ZxnLQVLzy?M?!-zPZx zAIo7I^gQ?s+iIv!;z5Fc9?u}b+1diG2kN3``kXU;`G%4=MEPc+Hg5xUHrZ0MK3Ol& z)k_g=eQk$0awDZKU@82*CLToc5*1`^b3 zCWm0q4FOTSB#W!{NtQbi)7}0OLURt9MfAy_{ScYx+IR3^*VsjLttkrs9C6^M9>>#b z`+F_E_&HJoU2GuQ`sl}tT7syCpqx`*&NR4j;KA1T3t4E*!9xgnE&^<=gs0cqD1vgj zy#xV|vTK;;orxb^Nqr5%()`^Bc+Ikw2ZX#A z=;RoNHKhCxhz!~-IQ(|6w$1Xh?^yF?cfl{N&$qzG;Y#Tn9n{5$_=e2e@wT*wLkKlV z4X$<`F8_cent=J$U^F0~I|M!X1^nj81b>V|N3@A_DdHSzv40Gqq~;2K&wl~q`L6+4 z7=vGL51K*wy9{$IWv@h8Z!^D{)#g|O6n~OIj8OcYh7;On_#-IZ1bhuD`D&568|j*m z{@(L!qMHjIdG}C7!2dVYl`V}P=XZW zBIx^!v`hC}TQCyvq@W0w#uS&UfJ+2_Gs{Dqvfp!kc{_qKy0unmxQg&kOSqh)-()2s zGJ&5#li}>~nkgU{Aj%;`TVH)4#o4hN@$@|%{-jmm-3&Gq(c>tH@C~dVZ$P@&%C!i> z&Mt1Y{A#X8!GlO{ZUneyv_1gyI;%O@{BY?)TO@T0GMeJzKL88J{1gu&^J3)D!tt+c zg4+dQgoJt;>9PS+EAa;4LVG{{4rcp?=zi4Ef|DOf^$Z1%`+#KA`BPL9%74>O7Bj>O zw82S#8&Wx!yp7^>-U#xOw(~TUEZX+y{5X2+(Kdn9z`&|sF0r_kGk&sUtLowe9+wS= zVKZFEcY{KU7=oJy&XXx(%$`;pOkn9s?s#?TNVpjupNd2Um^)6Fjt)NqN5*z9GXg(7 z-?@wlB!jdJy{Cr5-ATM2;daHCRUN~(!GM8VA3YouY${{r18%Ye=b!FwmcWm@A7-ql z6~la5w3vBrEt$zA3MDv}jMk6zp2M_O{gR75a3MsaaMN*1)02~p*y$`ZQ=X_*0CyA? zCw#GV6lMAZVk_g|7J z$_G%P)(#Kvm~BkW!g@9}%P&do%hv1IkjFE*RH9O^yO~oX<3i`t(n2YklHswziu3ly zs6E#fb-K-07D|(o<4aE#fodX?hhe0gP8M9A-7VXAGMJ0o0T96XbfR9X9Y&fK9P3bsGY3C&$j>reYp&m{&arb0l~R7^`_zW zGdk}25^88@Wa8lH^rU>)8E^ZHPRLiIw`eckIr9rzY-t}IWa>B*Vb}6-38>ccIG3vw z;Fr1nlH(D`Tj}9bh0iw=>XrV9lb9-rdAeN4!G$3O{}~tO3+_Oz}4S9`; zF+BVXY?{dDV3n=K^Yt3Oc~gzY56z)AmcDTi^q>pZ6i@UI4e?E>n!G+0G;6ndOZNcC zH}A2{Wq{;Dk0p$r7V_l+#X3A}YKeG0lepnOzhTW@y5S&;!I`g8cdMy%vWkOf3|2S3 zjoK*jO(@VT)hDswx|`NXE"] - -[dependencies] -serde = "1.0" -serde_derive = "1.0" -error-chain = "0.12" -futures = "0.1" -log = "0.3" -parking_lot = "0.4" -transaction-pool = "1.13.2" -substrate-runtime-primitives = { path = "../../substrate/runtime/primitives" } - -[dev-dependencies] -substrate-test-client = { path = "../../substrate/test-client" } -substrate-keyring = { path = "../../substrate/keyring" } -substrate-codec = { path = "../../substrate/codec" } diff --git a/substrate/extrinsic-pool/README.adoc b/substrate/extrinsic-pool/README.adoc deleted file mode 100644 index e128d64c9bf8b..0000000000000 --- a/substrate/extrinsic-pool/README.adoc +++ /dev/null @@ -1,13 +0,0 @@ - -= extrinsic-pool - -.Summary -[source, toml] ----- -include::Cargo.toml[lines=2..5] ----- - -.Description ----- -include::src/lib.rs[tag=description] ----- diff --git a/substrate/extrinsic-pool/src/error.rs b/substrate/extrinsic-pool/src/error.rs deleted file mode 100644 index 047041d18d560..0000000000000 --- a/substrate/extrinsic-pool/src/error.rs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2018 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 . - -//! External Error trait for extrinsic pool. - -use txpool; - -/// Extrinsic pool error. -pub trait IntoPoolError: ::std::error::Error + Send + Sized { - /// Try to extract original `txpool::Error` - /// - /// This implementation is optional and used only to - /// provide more descriptive error messages for end users - /// of RPC API. - fn into_pool_error(self) -> Result { Err(self) } -} - -impl IntoPoolError for txpool::Error { - fn into_pool_error(self) -> Result { Ok(self) } -} diff --git a/substrate/extrinsic-pool/src/lib.rs b/substrate/extrinsic-pool/src/lib.rs deleted file mode 100644 index bcd2b03c80e00..0000000000000 --- a/substrate/extrinsic-pool/src/lib.rs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -// tag::description[] -//! Generic extrinsic pool. -// end::description[] - -#![warn(missing_docs)] -#![warn(unused_extern_crates)] - -extern crate futures; -extern crate parking_lot; -extern crate substrate_runtime_primitives as runtime_primitives; - -#[macro_use] -extern crate log; -extern crate serde; -#[macro_use] -extern crate serde_derive; -extern crate transaction_pool as txpool; -#[cfg(test)] extern crate substrate_test_client as test_client; -#[cfg(test)] extern crate substrate_keyring as keyring; -#[cfg(test)] extern crate substrate_codec as codec; - -pub mod watcher; -mod error; -mod listener; -mod pool; -mod rotator; - -pub use listener::Listener; -pub use pool::{Pool, ChainApi, EventStream, Verified, VerifiedFor, ExtrinsicFor, ExHash, AllExtrinsics}; -pub use txpool::scoring; -pub use txpool::{Error, ErrorKind}; -pub use error::IntoPoolError; -pub use txpool::{Options, Status, LightStatus, VerifiedTransaction, Readiness, Transaction}; diff --git a/substrate/extrinsic-pool/src/listener.rs b/substrate/extrinsic-pool/src/listener.rs deleted file mode 100644 index 8badb331a5b64..0000000000000 --- a/substrate/extrinsic-pool/src/listener.rs +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2018 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, - fmt, - collections::HashMap, -}; -use txpool; - -use watcher; - -/// Extrinsic pool default listener. -#[derive(Default)] -pub struct Listener { - watchers: HashMap> -} - -impl Listener { - /// Creates a new watcher for given verified extrinsic. - /// - /// The watcher can be used to subscribe to lifecycle events of that extrinsic. - pub fn create_watcher>(&mut self, xt: Arc) -> watcher::Watcher { - let sender = self.watchers.entry(*xt.hash()).or_insert_with(watcher::Sender::default); - sender.new_watcher() - } - - /// Notify the listeners about extrinsic broadcast. - pub fn broadcasted(&mut self, hash: &H, peers: Vec) { - self.fire(hash, |watcher| watcher.broadcast(peers)); - } - - fn fire(&mut self, hash: &H, fun: F) where F: FnOnce(&mut watcher::Sender) { - let clean = if let Some(h) = self.watchers.get_mut(hash) { - fun(h); - h.is_done() - } else { - false - }; - - if clean { - self.watchers.remove(hash); - } - } -} - -impl txpool::Listener for Listener where - H: ::std::hash::Hash + Eq + Copy + fmt::Debug + fmt::LowerHex + Default, - T: txpool::VerifiedTransaction, -{ - fn added(&mut self, tx: &Arc, old: Option<&Arc>) { - if let Some(old) = old { - let hash = tx.hash(); - self.fire(old.hash(), |watcher| watcher.usurped(*hash)); - } - } - - fn dropped(&mut self, tx: &Arc, by: Option<&T>) { - self.fire(tx.hash(), |watcher| match by { - Some(t) => watcher.usurped(*t.hash()), - None => watcher.dropped(), - }) - } - - fn rejected(&mut self, tx: &Arc, reason: &txpool::ErrorKind) { - warn!(target: "extrinsic-pool", "Extrinsic rejected ({}): {:?}", reason, tx); - } - - fn invalid(&mut self, tx: &Arc) { - warn!(target: "extrinsic-pool", "Extrinsic invalid: {:?}", tx); - } - - fn canceled(&mut self, tx: &Arc) { - debug!(target: "extrinsic-pool", "Extrinsic canceled: {:?}", tx); - } - - fn culled(&mut self, tx: &Arc) { - // TODO [ToDr] latest block number? - let header_hash = Default::default(); - self.fire(tx.hash(), |watcher| watcher.finalised(header_hash)) - } -} diff --git a/substrate/extrinsic-pool/src/pool.rs b/substrate/extrinsic-pool/src/pool.rs deleted file mode 100644 index 642171b376015..0000000000000 --- a/substrate/extrinsic-pool/src/pool.rs +++ /dev/null @@ -1,606 +0,0 @@ -// Copyright 2018 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::{ - collections::{BTreeMap, HashMap}, - fmt, - sync::Arc, - time, -}; -use futures::sync::mpsc; -use parking_lot::{Mutex, RwLock}; -use serde::{Serialize, de::DeserializeOwned}; -use txpool::{self, Scoring, Readiness}; - -use error::IntoPoolError; -use listener::Listener; -use rotator::PoolRotator; -use watcher::Watcher; - -use runtime_primitives::{generic::BlockId, traits::Block as BlockT}; - -/// Modification notification event stream type; -pub type EventStream = mpsc::UnboundedReceiver<()>; - -/// Extrinsic hash type for a pool. -pub type ExHash = ::Hash; -/// Extrinsic type for a pool. -pub type ExtrinsicFor = <::Block as BlockT>::Extrinsic; -/// Verified extrinsic data for `ChainApi`. -pub type VerifiedFor = Verified, ::VEx>; -/// A collection of all extrinsics. -pub type AllExtrinsics = BTreeMap<<::VEx as txpool::VerifiedTransaction>::Sender, Vec>>; - -/// Verified extrinsic struct. Wraps original extrinsic and verification info. -#[derive(Debug)] -pub struct Verified { - /// Original extrinsic. - pub original: Ex, - /// Verification data. - pub verified: VEx, - /// Pool deadline, after it's reached we remove the extrinsic from the pool. - pub valid_till: time::Instant, -} - -impl txpool::VerifiedTransaction for Verified -where - Ex: fmt::Debug, - VEx: txpool::VerifiedTransaction, -{ - type Hash = ::Hash; - type Sender = ::Sender; - - fn hash(&self) -> &Self::Hash { - self.verified.hash() - } - - fn sender(&self) -> &Self::Sender { - self.verified.sender() - } - - fn mem_usage(&self) -> usize { - // TODO: add `original` mem usage. - self.verified.mem_usage() - } -} - -/// Concrete extrinsic validation and query logic. -pub trait ChainApi: Send + Sync { - /// Block type. - type Block: BlockT; - /// Extrinsic hash type. - type Hash: ::std::hash::Hash + Eq + Copy + fmt::Debug + fmt::LowerHex + Serialize + DeserializeOwned + ::std::str::FromStr + Send + Sync + Default + 'static; - /// Extrinsic sender type. - type Sender: ::std::hash::Hash + fmt::Debug + Serialize + DeserializeOwned + Eq + Clone + Send + Sync + Ord + Default; - /// Unchecked extrinsic type. - /// Verified extrinsic type. - type VEx: txpool::VerifiedTransaction + Send + Sync + Clone; - /// Readiness evaluator - type Ready; - /// Error type. - type Error: From + IntoPoolError; - /// Score type. - type Score: ::std::cmp::Ord + Clone + Default + fmt::Debug + Send + Send + Sync + fmt::LowerHex; - /// Custom scoring update event type. - type Event: ::std::fmt::Debug; - /// Verify extrinsic at given block. - fn verify_transaction(&self, at: &BlockId, uxt: &ExtrinsicFor) -> Result; - - /// Create new readiness evaluator. - fn ready(&self) -> Self::Ready; - - /// Check readiness for verified extrinsic at given block. - fn is_ready(&self, at: &BlockId, context: &mut Self::Ready, xt: &VerifiedFor) -> Readiness; - - /// Decides on ordering of `T`s from a particular sender. - fn compare(old: &VerifiedFor, other: &VerifiedFor) -> ::std::cmp::Ordering; - - /// Decides how to deal with two transactions from a sender that seem to occupy the same slot in the queue. - fn choose(old: &VerifiedFor, new: &VerifiedFor) -> txpool::scoring::Choice; - - /// Updates the transaction scores given a list of transactions and a change to previous scoring. - /// NOTE: you can safely assume that both slices have the same length. - /// (i.e. score at index `i` represents transaction at the same index) - fn update_scores(xts: &[txpool::Transaction>], scores: &mut [Self::Score], change: txpool::scoring::Change); - - /// Decides if `new` should push out `old` transaction from the pool. - /// - /// NOTE returning `InsertNew` here can lead to some transactions being accepted above pool limits. - fn should_replace(old: &VerifiedFor, new: &VerifiedFor) -> txpool::scoring::Choice; -} - -pub struct Ready<'a, 'b, B: 'a + ChainApi> { - api: &'a B, - at: &'b BlockId, - context: B::Ready, - rotator: &'a PoolRotator, - now: time::Instant, -} - -impl<'a, 'b, B: ChainApi> txpool::Ready> for Ready<'a, 'b, B> { - fn is_ready(&mut self, xt: &VerifiedFor) -> Readiness { - if self.rotator.ban_if_stale(&self.now, xt) { - debug!(target: "extrinsic-pool", "[{:?}] Banning as stale.", txpool::VerifiedTransaction::hash(xt)); - return Readiness::Stale; - } - - self.api.is_ready(self.at, &mut self.context, xt) - } -} - -pub struct ScoringAdapter(::std::marker::PhantomData); - -impl ::std::fmt::Debug for ScoringAdapter { - fn fmt(&self, _f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - Ok(()) - } -} - -impl Scoring> for ScoringAdapter { - type Score = ::Score; - type Event = ::Event; - - fn compare(&self, old: &VerifiedFor, other: &VerifiedFor) -> ::std::cmp::Ordering { - T::compare(old, other) - } - - fn choose(&self, old: &VerifiedFor, new: &VerifiedFor) -> txpool::scoring::Choice { - T::choose(old, new) - } - - fn update_scores(&self, xts: &[txpool::Transaction>], scores: &mut [Self::Score], change: txpool::scoring::Change) { - T::update_scores(xts, scores, change) - } - - fn should_replace(&self, old: &VerifiedFor, new: &VerifiedFor) -> txpool::scoring::Choice { - T::should_replace(old, new) - } -} - -/// Maximum time the transaction will be kept in the pool. -/// -/// Transactions that don't get included within the limit are removed from the pool. -const POOL_TIME: time::Duration = time::Duration::from_secs(60 * 5); - -/// Extrinsics pool. -pub struct Pool { - api: B, - pool: RwLock, - ScoringAdapter, - Listener, - >>, - import_notification_sinks: Mutex>>, - rotator: PoolRotator, -} - -impl Pool { - /// Create a new transaction pool. - pub fn new(options: txpool::Options, api: B) -> Self { - Pool { - pool: RwLock::new(txpool::Pool::new(Listener::default(), ScoringAdapter::(Default::default()), options)), - import_notification_sinks: Default::default(), - api, - rotator: Default::default(), - } - } - - /// Imports a pre-verified extrinsic to the pool. - pub fn import(&self, xt: VerifiedFor) -> Result>, B::Error> { - let result = self.pool.write().import(xt)?; - - self.import_notification_sinks.lock() - .retain(|sink| sink.unbounded_send(()).is_ok()); - - Ok(result) - } - - /// Return an event stream of transactions imported to the pool. - pub fn import_notification_stream(&self) -> EventStream { - let (sink, stream) = mpsc::unbounded(); - self.import_notification_sinks.lock().push(sink); - stream - } - - /// Invoked when extrinsics are broadcasted. - pub fn on_broadcasted(&self, propagated: HashMap>) { - for (hash, peers) in propagated.into_iter() { - self.pool.write().listener_mut().broadcasted(&hash, peers); - } - } - - /// Imports a bunch of unverified extrinsics to the pool - pub fn submit_at(&self, at: &BlockId, xts: T) -> Result>>, B::Error> where - T: IntoIterator> - { - xts - .into_iter() - .map(|xt| { - match self.api.verify_transaction(at, &xt) { - Ok(ref verified) if self.rotator.is_banned(txpool::VerifiedTransaction::hash(verified)) => { - return (Err(txpool::Error::from("Temporarily Banned".to_owned()).into()), xt) - }, - result => (result, xt), - } - }) - .map(|(v, xt)| { - let xt = Verified { - original: xt, - verified: v?, - valid_till: time::Instant::now() + POOL_TIME, - }; - Ok(self.pool.write().import(xt)?) - }) - .collect() - } - - /// Imports one unverified extrinsic to the pool - pub fn submit_one(&self, at: &BlockId, xt: ExtrinsicFor) -> Result>, B::Error> { - Ok(self.submit_at(at, ::std::iter::once(xt))?.pop().expect("One extrinsic passed; one result returned; qed")) - } - - /// Import a single extrinsic and starts to watch their progress in the pool. - pub fn submit_and_watch(&self, at: &BlockId, xt: ExtrinsicFor) -> Result, B::Error> { - let xt = self.submit_at(at, Some(xt))?.pop().expect("One extrinsic passed; one result returned; qed"); - Ok(self.pool.write().listener_mut().create_watcher(xt)) - } - - /// Remove from the pool. - pub fn remove(&self, hashes: &[B::Hash], is_valid: bool) -> Vec>>> { - let mut pool = self.pool.write(); - let mut results = Vec::with_capacity(hashes.len()); - - // temporarily ban invalid transactions - if !is_valid { - debug!(target: "transaction-pool", "Banning invalid transactions: {:?}", hashes); - self.rotator.ban(&time::Instant::now(), hashes); - } - - for hash in hashes { - results.push(pool.remove(hash, is_valid)); - } - - results - } - - /// Cull transactions from the queue. - pub fn cull_from( - &self, - at: &BlockId, - senders: Option<&[::Sender]>, - ) -> usize - { - self.rotator.clear_timeouts(&time::Instant::now()); - let ready = self.ready(at); - self.pool.write().cull(senders, ready) - } - - /// Cull old transactions from the queue. - pub fn cull(&self, at: &BlockId) -> Result { - Ok(self.cull_from(at, None)) - } - - /// Cull transactions from the queue and then compute the pending set. - pub fn cull_and_get_pending(&self, at: &BlockId, f: F) -> Result where - F: FnOnce(txpool::PendingIterator, Ready, ScoringAdapter, Listener>) -> T, - { - self.cull_from(at, None); - Ok(self.pending(at, f)) - } - - /// Get the full status of the queue (including readiness) - pub fn status>>(&self, ready: R) -> txpool::Status { - self.pool.read().status(ready) - } - - /// Returns light status of the pool. - pub fn light_status(&self) -> txpool::LightStatus { - self.pool.read().light_status() - } - - /// Removes all transactions from given sender - pub fn remove_sender(&self, sender: ::Sender) -> Vec>> { - let mut pool = self.pool.write(); - let pending = pool.pending_from_sender(|_: &VerifiedFor| txpool::Readiness::Ready, &sender).collect(); - // remove all transactions from this sender - pool.cull(Some(&[sender]), |_: &VerifiedFor| txpool::Readiness::Stale); - pending - } - - /// Retrieve the pending set. Be careful to not leak the pool `ReadGuard` to prevent deadlocks. - pub fn pending(&self, at: &BlockId, f: F) -> T where - F: FnOnce(txpool::PendingIterator, Ready, ScoringAdapter, Listener>) -> T, - { - let ready = self.ready(at); - f(self.pool.read().pending(ready)) - } - - /// Retry to import all verified transactions from given sender. - pub fn retry_verification(&self, at: &BlockId, sender: ::Sender) -> Result<(), B::Error> { - let to_reverify = self.remove_sender(sender); - self.submit_at(at, to_reverify.into_iter().map(|ex| Arc::try_unwrap(ex).expect("Removed items have no references").original))?; - Ok(()) - } - - /// Reverify transaction that has been reported incorrect. - /// - /// Returns `Ok(None)` in case the hash is missing, `Err(e)` in case of verification error and new transaction - /// reference otherwise. - /// - /// TODO [ToDr] That method is currently unused, should be used together with BlockBuilder - /// when we detect that particular transaction has failed. - /// In such case we will attempt to remove or re-verify it. - pub fn reverify_transaction(&self, at: &BlockId, hash: B::Hash) -> Result>>, B::Error> { - let result = self.remove(&[hash], false).pop().expect("One hash passed; one result received; qed"); - if let Some(ex) = result { - self.submit_one(at, Arc::try_unwrap(ex).expect("Removed items have no references").original).map(Some) - } else { - Ok(None) - } - } - - /// Retrieve all transactions in the pool grouped by sender. - pub fn all(&self) -> AllExtrinsics { - use txpool::VerifiedTransaction; - let pool = self.pool.read(); - let all = pool.unordered_pending(AlwaysReady); - all.fold(Default::default(), |mut map: AllExtrinsics, tx| { - // Map with `null` key is not serializable, so we fallback to default accountId. - map.entry(tx.verified.sender().clone()) - .or_insert_with(Vec::new) - // use bytes type to make it serialize nicer. - .push(tx.original.clone()); - map - }) - } - - fn ready<'a, 'b>(&'a self, at: &'b BlockId) -> Ready<'a, 'b, B> { - Ready { - api: &self.api, - rotator: &self.rotator, - context: self.api.ready(), - at, - now: time::Instant::now(), - } - } -} - - /// A Readiness implementation that returns `Ready` for all transactions. -pub struct AlwaysReady; -impl txpool::Ready for AlwaysReady { - fn is_ready(&mut self, _tx: &VEx) -> txpool::Readiness { - txpool::Readiness::Ready - } -} - -#[cfg(test)] -pub mod tests { - use txpool; - use super::{VerifiedFor, ExtrinsicFor}; - use std::collections::HashMap; - use std::cmp::Ordering; - use {Pool, ChainApi, scoring, Readiness}; - use keyring::Keyring::{self, *}; - use codec::Encode; - use test_client::runtime::{AccountId, Block, Hash, Index, Extrinsic, Transfer}; - use runtime_primitives::{generic, traits::{Hash as HashT, BlindCheckable, BlakeTwo256}}; - use VerifiedTransaction as VerifiedExtrinsic; - - type BlockId = generic::BlockId; - - #[derive(Clone, Debug)] - pub struct VerifiedTransaction { - pub hash: Hash, - pub sender: AccountId, - pub nonce: u64, - } - - impl txpool::VerifiedTransaction for VerifiedTransaction { - type Hash = Hash; - type Sender = AccountId; - - fn hash(&self) -> &Self::Hash { - &self.hash - } - - fn sender(&self) -> &Self::Sender { - &self.sender - } - - fn mem_usage(&self) -> usize { - 256 - } - } - - struct TestApi; - - impl TestApi { - fn default() -> Self { - TestApi - } - } - - impl ChainApi for TestApi { - type Block = Block; - type Hash = Hash; - type Sender = AccountId; - type Error = txpool::Error; - type VEx = VerifiedTransaction; - type Ready = HashMap; - type Score = u64; - type Event = (); - - fn verify_transaction(&self, _at: &BlockId, uxt: &ExtrinsicFor) -> Result { - let hash = BlakeTwo256::hash(&uxt.encode()); - let xt = uxt.clone().check()?; - Ok(VerifiedTransaction { - hash, - sender: xt.transfer.from, - nonce: xt.transfer.nonce, - }) - } - - fn is_ready(&self, at: &BlockId, nonce_cache: &mut Self::Ready, xt: &VerifiedFor) -> Readiness { - let sender = xt.verified.sender; - let next_index = nonce_cache.entry(sender) - .or_insert_with(|| index(at, sender)); - - let result = match xt.original.transfer.nonce.cmp(&next_index) { - Ordering::Greater => Readiness::Future, - Ordering::Equal => Readiness::Ready, - Ordering::Less => Readiness::Stale, - }; - - // remember to increment `next_index` - *next_index = next_index.saturating_add(1); - - result - } - - fn ready(&self) -> Self::Ready { - HashMap::default() - } - - fn compare(old: &VerifiedFor, other: &VerifiedFor) -> Ordering { - old.original.transfer.nonce.cmp(&other.original.transfer.nonce) - } - - fn choose(old: &VerifiedFor, new: &VerifiedFor) -> scoring::Choice { - assert!(new.verified.sender == old.verified.sender, "Scoring::choose called with transactions from different senders"); - if old.original.transfer.nonce == new.original.transfer.nonce { - return scoring::Choice::RejectNew; - } - scoring::Choice::InsertNew - } - - fn update_scores( - xts: &[txpool::Transaction>], - scores: &mut [Self::Score], - _change: scoring::Change<()> - ) { - for i in 0..xts.len() { - scores[i] = xts[i].original.transfer.amount; - } - } - - fn should_replace(_old: &VerifiedFor, _new: &VerifiedFor) -> scoring::Choice { - scoring::Choice::InsertNew - } - } - - fn index(at: &BlockId, _account: AccountId) -> u64 { - (_account[0] as u64) + number_of(at) - } - - fn number_of(at: &BlockId) -> u64 { - match at { - generic::BlockId::Number(n) => *n as u64, - _ => 0, - } - } - - fn uxt(who: Keyring, nonce: Index) -> Extrinsic { - let transfer = Transfer { - from: who.to_raw_public().into(), - to: AccountId::default(), - nonce, - amount: 1, - }; - let signature = transfer.using_encoded(|e| who.sign(e)); - Extrinsic { - transfer, - signature: signature.into(), - } - } - - fn pool() -> Pool { - Pool::new(Default::default(), TestApi::default()) - } - - #[test] - fn submission_should_work() { - let pool = pool(); - assert_eq!(209, index(&BlockId::number(0), Alice.to_raw_public().into())); - pool.submit_one(&BlockId::number(0), uxt(Alice, 209)).unwrap(); - - let pending: Vec<_> = pool.cull_and_get_pending(&BlockId::number(0), |p| p.map(|a| (*a.sender(), a.original.transfer.nonce)).collect()).unwrap(); - assert_eq!(pending, vec![(Alice.to_raw_public().into(), 209)]); - } - - #[test] - fn multiple_submission_should_work() { - let pool = pool(); - pool.submit_one(&BlockId::number(0), uxt(Alice, 209)).unwrap(); - pool.submit_one(&BlockId::number(0), uxt(Alice, 210)).unwrap(); - - let pending: Vec<_> = pool.cull_and_get_pending(&BlockId::number(0), |p| p.map(|a| (*a.sender(), a.original.transfer.nonce)).collect()).unwrap(); - assert_eq!(pending, vec![(Alice.to_raw_public().into(), 209), (Alice.to_raw_public().into(), 210)]); - } - - #[test] - fn early_nonce_should_be_culled() { - let pool = pool(); - pool.submit_one(&BlockId::number(0), uxt(Alice, 208)).unwrap(); - - let pending: Vec<_> = pool.cull_and_get_pending(&BlockId::number(0), |p| p.map(|a| (*a.sender(), a.original.transfer.nonce)).collect()).unwrap(); - assert_eq!(pending, vec![]); - } - - #[test] - fn late_nonce_should_be_queued() { - let pool = pool(); - - pool.submit_one(&BlockId::number(0), uxt(Alice, 210)).unwrap(); - let pending: Vec<_> = pool.cull_and_get_pending(&BlockId::number(0), |p| p.map(|a| (*a.sender(), a.original.transfer.nonce)).collect()).unwrap(); - assert_eq!(pending, vec![]); - - pool.submit_one(&BlockId::number(0), uxt(Alice, 209)).unwrap(); - let pending: Vec<_> = pool.cull_and_get_pending(&BlockId::number(0), |p| p.map(|a| (*a.sender(), a.original.transfer.nonce)).collect()).unwrap(); - assert_eq!(pending, vec![(Alice.to_raw_public().into(), 209), (Alice.to_raw_public().into(), 210)]); - } - - #[test] - fn retrying_verification_might_not_change_anything() { - let pool = pool(); - pool.submit_one(&BlockId::number(0), uxt(Alice, 209)).unwrap(); - pool.submit_one(&BlockId::number(0), uxt(Alice, 210)).unwrap(); - - let pending: Vec<_> = pool.cull_and_get_pending(&BlockId::number(0), |p| p.map(|a| (*a.sender(), a.original.transfer.nonce)).collect()).unwrap(); - assert_eq!(pending, vec![(Alice.to_raw_public().into(), 209), (Alice.to_raw_public().into(), 210)]); - - pool.retry_verification(&BlockId::number(1), Alice.to_raw_public().into()).unwrap(); - - let pending: Vec<_> = pool.cull_and_get_pending(&BlockId::number(0), |p| p.map(|a| (*a.sender(), a.original.transfer.nonce)).collect()).unwrap(); - assert_eq!(pending, vec![(Alice.to_raw_public().into(), 209), (Alice.to_raw_public().into(), 210)]); - } - - #[test] - fn should_ban_invalid_transactions() { - let pool = pool(); - let uxt = uxt(Alice, 209); - let hash = *pool.submit_one(&BlockId::number(0), uxt.clone()).unwrap().hash(); - pool.remove(&[hash], true); - pool.submit_one(&BlockId::number(0), uxt.clone()).unwrap(); - - // when - pool.remove(&[hash], false); - let pending: Vec = pool.cull_and_get_pending(&BlockId::number(0), |p| p.map(|a| *a.sender()).collect()).unwrap(); - assert_eq!(pending, vec![]); - - // then - pool.submit_one(&BlockId::number(0), uxt.clone()).unwrap_err(); - } -} diff --git a/substrate/extrinsic-pool/src/rotator.rs b/substrate/extrinsic-pool/src/rotator.rs deleted file mode 100644 index 93acce9e9130c..0000000000000 --- a/substrate/extrinsic-pool/src/rotator.rs +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright 2018 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 . - -//! Rotate extrinsic inside the pool. -//! -//! Keeps only recent extrinsic and discard the ones kept for a significant amount of time. -//! Discarded extrinsics are banned so that they don't get re-imported again. - -use std::{ - collections::HashMap, - fmt, - hash, - time::{Duration, Instant}, -}; -use parking_lot::RwLock; -use txpool::VerifiedTransaction; -use Verified; - -/// Expected size of the banned extrinsics cache. -const EXPECTED_SIZE: usize = 2048; - -/// Pool rotator is responsible to only keep fresh extrinsics in the pool. -/// -/// Extrinsics that occupy the pool for too long are culled and temporarily banned from entering -/// the pool again. -pub struct PoolRotator { - /// How long the extrinsic is banned for. - ban_time: Duration, - /// Currently banned extrinsics. - banned_until: RwLock>, -} - -impl Default for PoolRotator { - fn default() -> Self { - PoolRotator { - ban_time: Duration::from_secs(60 * 30), - banned_until: Default::default(), - } - } -} - -impl PoolRotator { - /// Returns `true` if extrinsic hash is currently banned. - pub fn is_banned(&self, hash: &Hash) -> bool { - self.banned_until.read().contains_key(hash) - } - - /// Bans given set of hashes. - pub fn ban(&self, now: &Instant, hashes: &[Hash]) { - let mut banned = self.banned_until.write(); - - for hash in hashes { - banned.insert(hash.clone(), *now + self.ban_time); - } - - if banned.len() > 2 * EXPECTED_SIZE { - while banned.len() > EXPECTED_SIZE { - if let Some(key) = banned.keys().next().cloned() { - banned.remove(&key); - } - } - } - } - - /// Bans extrinsic if it's stale. - /// - /// Returns `true` if extrinsic is stale and got banned. - pub fn ban_if_stale(&self, now: &Instant, xt: &Verified) -> bool where - VEx: VerifiedTransaction, - Hash: fmt::Debug + fmt::LowerHex, - { - if &xt.valid_till > now { - return false; - } - - self.ban(now, &[xt.verified.hash().clone()]); - true - } - - /// Removes timed bans. - pub fn clear_timeouts(&self, now: &Instant) { - let mut banned = self.banned_until.write(); - - banned.retain(|_, &mut v| v >= *now); - } -} - -#[cfg(test)] -mod tests { - use super::*; - use pool::tests::VerifiedTransaction; - use test_client::runtime::Hash; - - fn rotator() -> PoolRotator { - PoolRotator { - ban_time: Duration::from_millis(10), - ..Default::default() - } - } - - fn tx() -> (Hash, Verified) { - let hash = 5.into(); - let tx = Verified { - original: 5, - verified: VerifiedTransaction { - hash, - sender: Default::default(), - nonce: Default::default(), - }, - valid_till: Instant::now(), - }; - - (hash, tx) - } - - #[test] - fn should_not_ban_if_not_stale() { - // given - let (hash, tx) = tx(); - let rotator = rotator(); - assert!(!rotator.is_banned(&hash)); - let past = Instant::now() - Duration::from_millis(1000); - - // when - assert!(!rotator.ban_if_stale(&past, &tx)); - - // then - assert!(!rotator.is_banned(&hash)); - } - - #[test] - fn should_ban_stale_extrinsic() { - // given - let (hash, tx) = tx(); - let rotator = rotator(); - assert!(!rotator.is_banned(&hash)); - - // when - assert!(rotator.ban_if_stale(&Instant::now(), &tx)); - - // then - assert!(rotator.is_banned(&hash)); - } - - - #[test] - fn should_clear_banned() { - // given - let (hash, tx) = tx(); - let rotator = rotator(); - assert!(rotator.ban_if_stale(&Instant::now(), &tx)); - assert!(rotator.is_banned(&hash)); - - // when - let future = Instant::now() + rotator.ban_time + rotator.ban_time; - rotator.clear_timeouts(&future); - - // then - assert!(!rotator.is_banned(&hash)); - } - - #[test] - fn should_garbage_collect() { - // given - fn tx_with(i: u64, time: Instant) -> Verified { - let hash = i.into(); - Verified { - original: i, - verified: VerifiedTransaction { - hash, - sender: Default::default(), - nonce: Default::default(), - }, - valid_till: time, - } - } - - let rotator = rotator(); - - let now = Instant::now(); - let past = now - Duration::from_secs(1); - - // when - for i in 0..2*EXPECTED_SIZE { - let tx = tx_with(i as u64, past); - assert!(rotator.ban_if_stale(&now, &tx)); - } - assert_eq!(rotator.banned_until.read().len(), 2*EXPECTED_SIZE); - - // then - let tx = tx_with(2*EXPECTED_SIZE as u64, past); - // trigger a garbage collection - assert!(rotator.ban_if_stale(&now, &tx)); - assert_eq!(rotator.banned_until.read().len(), EXPECTED_SIZE); - } -} diff --git a/substrate/extrinsic-pool/src/watcher.rs b/substrate/extrinsic-pool/src/watcher.rs deleted file mode 100644 index 78b27326f861f..0000000000000 --- a/substrate/extrinsic-pool/src/watcher.rs +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2018 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 . - -//! Extrinsics status updates. - -use futures::{ - Stream, - sync::mpsc, -}; - -/// Possible extrinsic status events -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub enum Status { - /// Extrinsic has been finalised in block with given hash. - Finalised(H), - /// Some state change (perhaps another extrinsic was included) rendered this extrinsic invalid. - Usurped(H), - /// The extrinsic has been broadcast to the given peers. - Broadcast(Vec), - /// Extrinsic has been dropped from the pool because of the limit. - Dropped, -} - -/// Extrinsic watcher. -/// -/// Represents a stream of status updates for particular extrinsic. -#[derive(Debug)] -pub struct Watcher { - receiver: mpsc::UnboundedReceiver>, -} - -impl Watcher { - /// Pipe the notifications to given sink. - /// - /// Make sure to drive the future to completion. - pub fn into_stream(self) -> impl Stream, Error=()> { - // we can safely ignore the error here, `UnboundedReceiver` never fails. - self.receiver.map_err(|_| ()) - } -} - -/// Sender part of the watcher. Exposed only for testing purposes. -#[derive(Debug, Default)] -pub struct Sender { - receivers: Vec>>, - finalised: bool, -} - -impl Sender { - /// Add a new watcher to this sender object. - pub fn new_watcher(&mut self) -> Watcher { - let (tx, receiver) = mpsc::unbounded(); - self.receivers.push(tx); - Watcher { - receiver, - } - } - - /// Some state change (perhaps another extrinsic was included) rendered this extrinsic invalid. - pub fn usurped(&mut self, hash: H) { - self.send(Status::Usurped(hash)) - } - - /// Extrinsic has been finalised in block with given hash. - pub fn finalised(&mut self, hash: H) { - self.send(Status::Finalised(hash)); - self.finalised = true; - } - - /// Transaction has been dropped from the pool because of the limit. - pub fn dropped(&mut self) { - self.send(Status::Dropped); - } - - /// The extrinsic has been broadcast to the given peers. - pub fn broadcast(&mut self, peers: Vec) { - self.send(Status::Broadcast(peers)) - } - - - /// Returns true if the are no more listeners for this extrinsic or it was finalised. - pub fn is_done(&self) -> bool { - self.finalised || self.receivers.is_empty() - } - - fn send(&mut self, status: Status) { - self.receivers.retain(|sender| sender.unbounded_send(status.clone()).is_ok()) - } -} diff --git a/substrate/keyring/Cargo.toml b/substrate/keyring/Cargo.toml deleted file mode 100644 index 69bd55a6f3e24..0000000000000 --- a/substrate/keyring/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "substrate-keyring" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -ed25519 = { path = "../ed25519" } -hex-literal = { version = "0.1.0" } -lazy_static = { version = "1.0" } diff --git a/substrate/keyring/README.adoc b/substrate/keyring/README.adoc deleted file mode 100644 index 0118fe883d1a6..0000000000000 --- a/substrate/keyring/README.adoc +++ /dev/null @@ -1,13 +0,0 @@ - -= Keyring - -.Summary -[source, toml] ----- -include::Cargo.toml[lines=2..5] ----- - -.Description ----- -include::src/lib.rs[tag=description] ----- diff --git a/substrate/keyring/src/lib.rs b/substrate/keyring/src/lib.rs deleted file mode 100644 index 6f0270c9d4de9..0000000000000 --- a/substrate/keyring/src/lib.rs +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -// tag::description[] -//! Support code for the runtime. -// end::description[] - -#[macro_use] extern crate hex_literal; -#[macro_use] extern crate lazy_static; -pub extern crate ed25519; - -use std::collections::HashMap; -use std::ops::Deref; -use ed25519::{Pair, Public, Signature}; - -/// Set of test accounts. -#[derive(Clone, Copy, PartialEq, Eq, Hash)] -pub enum Keyring { - Alice, - Bob, - Charlie, - Dave, - Eve, - Ferdie, - One, - Two, -} - -impl Keyring { - pub fn from_public(who: Public) -> Option { - [ - Keyring::Alice, - Keyring::Bob, - Keyring::Charlie, - Keyring::Dave, - Keyring::Eve, - Keyring::Ferdie, - Keyring::One, - Keyring::Two, - ].iter() - .map(|i| *i) - .find(|&k| Public::from(k) == who) - } - - pub fn from_raw_public(who: [u8; 32]) -> Option { - Self::from_public(Public::from_raw(who)) - } - - pub fn to_raw_public(self) -> [u8; 32] { - *Public::from(self).as_array_ref() - } - - pub fn to_raw_public_vec(self) -> Vec { - Public::from(self).to_raw_vec() - } - - pub fn sign(self, msg: &[u8]) -> Signature { - Pair::from(self).sign(msg) - } - - pub fn pair(self) -> Pair { - match self { - Keyring::Alice => Pair::from_seed(b"Alice "), - Keyring::Bob => Pair::from_seed(b"Bob "), - Keyring::Charlie => Pair::from_seed(b"Charlie "), - Keyring::Dave => Pair::from_seed(b"Dave "), - Keyring::Eve => Pair::from_seed(b"Eve "), - Keyring::Ferdie => Pair::from_seed(b"Ferdie "), - Keyring::One => Pair::from_seed(b"12345678901234567890123456789012"), - Keyring::Two => Pair::from_seed(&hex!("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60")), - } - } -} - -impl From for &'static str { - fn from(k: Keyring) -> Self { - match k { - Keyring::Alice => "Alice", - Keyring::Bob => "Bob", - Keyring::Charlie => "Charlie", - Keyring::Dave => "Dave", - Keyring::Eve => "Eve", - Keyring::Ferdie => "Ferdie", - Keyring::One => "one", - Keyring::Two => "two", - } - } -} - -lazy_static! { - static ref PRIVATE_KEYS: HashMap = { - [ - Keyring::Alice, - Keyring::Bob, - Keyring::Charlie, - Keyring::Dave, - Keyring::Eve, - Keyring::Ferdie, - Keyring::One, - Keyring::Two, - ].iter().map(|&i| (i, i.pair())).collect() - }; - - static ref PUBLIC_KEYS: HashMap = { - PRIVATE_KEYS.iter().map(|(&name, pair)| (name, pair.public())).collect() - }; -} - -impl From for Public { - fn from(k: Keyring) -> Self { - (*PUBLIC_KEYS).get(&k).unwrap().clone() - } -} - -impl From for Pair { - fn from(k: Keyring) -> Self { - k.pair() - } -} - -impl From for [u8; 32] { - fn from(k: Keyring) -> Self { - *(*PUBLIC_KEYS).get(&k).unwrap().as_array_ref() - } -} - -impl From for &'static [u8; 32] { - fn from(k: Keyring) -> Self { - (*PUBLIC_KEYS).get(&k).unwrap().as_array_ref() - } -} - -impl AsRef<[u8; 32]> for Keyring { - fn as_ref(&self) -> &[u8; 32] { - (*PUBLIC_KEYS).get(self).unwrap().as_array_ref() - } -} - -impl AsRef for Keyring { - fn as_ref(&self) -> &Public { - (*PUBLIC_KEYS).get(self).unwrap() - } -} - -impl Deref for Keyring { - type Target = [u8; 32]; - fn deref(&self) -> &[u8; 32] { - (*PUBLIC_KEYS).get(self).unwrap().as_array_ref() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use ed25519::Verifiable; - - #[test] - fn should_work() { - assert!(Keyring::Alice.sign(b"I am Alice!").verify(b"I am Alice!", Keyring::Alice)); - assert!(!Keyring::Alice.sign(b"I am Alice!").verify(b"I am Bob!", Keyring::Alice)); - assert!(!Keyring::Alice.sign(b"I am Alice!").verify(b"I am Alice!", Keyring::Bob)); - } -} diff --git a/substrate/keystore/Cargo.toml b/substrate/keystore/Cargo.toml deleted file mode 100644 index eb1da7e02061b..0000000000000 --- a/substrate/keystore/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "substrate-keystore" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -substrate-primitives = { path = "../primitives" } -parity-crypto = { version = "0.1", default_features = false } -ed25519 = { path = "../ed25519" } -error-chain = "0.12" -hex = "0.3" -rand = "0.4" -serde_json = "1.0" -serde = "1.0" -serde_derive = "1.0" -subtle = "0.5" - -[dev-dependencies] -tempdir = "0.3" diff --git a/substrate/keystore/README.adoc b/substrate/keystore/README.adoc deleted file mode 100644 index 5a66a882ff098..0000000000000 --- a/substrate/keystore/README.adoc +++ /dev/null @@ -1,13 +0,0 @@ - -= Keystore - -.Summary -[source, toml] ----- -include::Cargo.toml[lines=2..5] ----- - -.Description ----- -include::src/lib.rs[tag=description] ----- diff --git a/substrate/keystore/src/lib.rs b/substrate/keystore/src/lib.rs deleted file mode 100644 index 5686e637371b7..0000000000000 --- a/substrate/keystore/src/lib.rs +++ /dev/null @@ -1,295 +0,0 @@ -// Copyright 2017-2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -// tag::description[] -//! Keystore (and session key management) for ed25519 based chains like Polkadot. -// end::description[] - -extern crate substrate_primitives; -extern crate parity_crypto as crypto; -extern crate subtle; -extern crate ed25519; -extern crate rand; -extern crate serde_json; -extern crate serde; -extern crate hex; - -#[macro_use] -extern crate serde_derive; - -#[macro_use] -extern crate error_chain; - -#[cfg(test)] -extern crate tempdir; - -use std::collections::HashMap; -use std::path::PathBuf; -use std::fs::{self, File}; -use std::io::{self, Write}; - -use substrate_primitives::hashing::blake2_256; -use ed25519::{Pair, Public, PKCS_LEN}; - -pub use crypto::KEY_ITERATIONS; - -error_chain! { - foreign_links { - Io(io::Error); - Json(serde_json::Error); - } - - errors { - InvalidPassword { - description("Invalid password"), - display("Invalid password"), - } - InvalidPKCS8 { - description("Invalid PKCS#8 data"), - display("Invalid PKCS#8 data"), - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct InvalidPassword; - -#[derive(Serialize, Deserialize)] -struct EncryptedKey { - mac: [u8; 32], - salt: [u8; 32], - ciphertext: Vec, // TODO: switch to fixed-size when serde supports - iv: [u8; 16], - iterations: u32, -} - -impl EncryptedKey { - fn encrypt(plain: &[u8; PKCS_LEN], password: &str, iterations: u32) -> Self { - use rand::{Rng, OsRng}; - - let mut rng = OsRng::new().expect("OS Randomness available on all supported platforms; qed"); - - let salt: [u8; 32] = rng.gen(); - let iv: [u8; 16] = rng.gen(); - - // two parts of derived key - // DK = [ DK[0..15] DK[16..31] ] = [derived_left_bits, derived_right_bits] - let (derived_left_bits, derived_right_bits) = crypto::derive_key_iterations(password.as_bytes(), &salt, iterations); - - // preallocated (on-stack in case of `Secret`) buffer to hold cipher - // length = length(plain) as we are using CTR-approach - let mut ciphertext = vec![0; PKCS_LEN]; - - // aes-128-ctr with initial vector of iv - crypto::aes::encrypt_128_ctr(&derived_left_bits, &iv, plain, &mut *ciphertext) - .expect("input lengths of key and iv are both 16; qed"); - - // Blake2_256(DK[16..31] ++ ), where DK[16..31] - derived_right_bits - let mac = blake2_256(&crypto::derive_mac(&derived_right_bits, &*ciphertext)); - - EncryptedKey { - salt, - iv, - mac, - iterations, - ciphertext, - } - } - - fn decrypt(&self, password: &str) -> Result<[u8; PKCS_LEN]> { - let (derived_left_bits, derived_right_bits) = - crypto::derive_key_iterations(password.as_bytes(), &self.salt, self.iterations); - - let mac = blake2_256(&crypto::derive_mac(&derived_right_bits, &self.ciphertext)); - - if subtle::slices_equal(&mac[..], &self.mac[..]) != 1 { - return Err(ErrorKind::InvalidPassword.into()); - } - - let mut plain = [0; PKCS_LEN]; - crypto::aes::decrypt_128_ctr(&derived_left_bits, &self.iv, &self.ciphertext, &mut plain[..]) - .expect("input lengths of key and iv are both 16; qed"); - Ok(plain) - } -} - -type Seed = [u8; 32]; - -/// Key store. -pub struct Store { - path: PathBuf, - additional: HashMap, -} - -impl Store { - /// Create a new store at the given path. - pub fn open(path: PathBuf) -> Result { - fs::create_dir_all(&path)?; - Ok(Store { path, additional: HashMap::new() }) - } - - /// Generate a new key, placing it into the store. - pub fn generate(&self, password: &str) -> Result { - let (pair, pkcs_bytes) = Pair::generate_with_pkcs8(); - let key_file = EncryptedKey::encrypt(&pkcs_bytes, password, KEY_ITERATIONS as u32); - - let mut file = File::create(self.key_file_path(&pair.public()))?; - ::serde_json::to_writer(&file, &key_file)?; - - file.flush()?; - - Ok(pair) - } - - /// Create a new key from seed. Do not place it into the store. - /// Only the first 32 bytes of the sead are used. This is meant to be used for testing only. - // TODO: Remove this - pub fn generate_from_seed(&mut self, seed: &str) -> Result { - let mut s: [u8; 32] = [' ' as u8; 32]; - - let was_hex = if seed.len() == 66 && &seed[0..2] == "0x" { - if let Ok(d) = hex::decode(&seed[2..]) { - s.copy_from_slice(&d); - true - } else { false } - } else { false }; - - if !was_hex { - let len = ::std::cmp::min(32, seed.len()); - &mut s[..len].copy_from_slice(&seed.as_bytes()[..len]); - } - - let pair = Pair::from_seed(&s); - self.additional.insert(pair.public(), s); - Ok(pair) - } - - /// Load a key file with given public key. - pub fn load(&self, public: &Public, password: &str) -> Result { - if let Some(ref seed) = self.additional.get(public) { - let pair = Pair::from_seed(seed); - return Ok(pair); - } - let path = self.key_file_path(public); - let file = File::open(path)?; - - let encrypted_key: EncryptedKey = ::serde_json::from_reader(&file)?; - let pkcs_bytes = encrypted_key.decrypt(password)?; - - Pair::from_pkcs8(&pkcs_bytes[..]).map_err(|_| ErrorKind::InvalidPKCS8.into()) - } - - /// Get public keys of all stored keys. - pub fn contents(&self) -> Result> { - let mut public_keys: Vec = self.additional.keys().cloned().collect(); - 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)); - } - _ => continue, - } - } - } - - Ok(public_keys) - } - - fn key_file_path(&self, public: &Public) -> PathBuf { - let mut buf = self.path.clone(); - buf.push(hex::encode(public.as_slice())); - buf - } -} - -#[cfg(test)] -mod tests { - use super::*; - use tempdir::TempDir; - - #[test] - fn encrypt_and_decrypt() { - let plain = [1; PKCS_LEN]; - let encrypted_key = EncryptedKey::encrypt(&plain, "thepassword", KEY_ITERATIONS as u32); - - let decrypted_key = encrypted_key.decrypt("thepassword").unwrap(); - - assert_eq!(&plain[..], &decrypted_key[..]); - } - - #[test] - fn decrypt_wrong_password_fails() { - let plain = [1; PKCS_LEN]; - let encrypted_key = EncryptedKey::encrypt(&plain, "thepassword", KEY_ITERATIONS as u32); - - assert!(encrypted_key.decrypt("thepassword2").is_err()); - } - - #[test] - fn decrypt_wrong_iterations_fails() { - let plain = [1; PKCS_LEN]; - let mut encrypted_key = EncryptedKey::encrypt(&plain, "thepassword", KEY_ITERATIONS as u32); - - encrypted_key.iterations -= 64; - - assert!(encrypted_key.decrypt("thepassword").is_err()); - } - - #[test] - fn basic_store() { - let temp_dir = TempDir::new("keystore").unwrap(); - let store = Store::open(temp_dir.path().to_owned()).unwrap(); - - assert!(store.contents().unwrap().is_empty()); - - let key = store.generate("thepassword").unwrap(); - let key2 = store.load(&key.public(), "thepassword").unwrap(); - - assert!(store.load(&key.public(), "notthepassword").is_err()); - - assert_eq!(key.public(), key2.public()); - - assert_eq!(store.contents().unwrap()[0], key.public()); - } - - #[test] - fn test_generate_from_seed() { - let temp_dir = TempDir::new("keystore").unwrap(); - let mut store = Store::open(temp_dir.path().to_owned()).unwrap(); - - let pair = store.generate_from_seed("0x1").unwrap(); - assert_eq!("5GqhgbUd2S9uc5Tm7hWhw29Tw2jBnuHshmTV1fDF4V1w3G2z", pair.public().to_ss58check()); - - let pair = store.generate_from_seed("0x3d97c819d68f9bafa7d6e79cb991eebcd77d966c5334c0b94d9e1fa7ad0869dc").unwrap(); - assert_eq!("5DKUrgFqCPV8iAXx9sjy1nyBygQCeiUYRFWurZGhnrn3HBL8", pair.public().to_ss58check()); - - let pair = store.generate_from_seed("12345678901234567890123456789022").unwrap(); - assert_eq!("5DscZvfjnM5im7oKRXXP9xtCG1SEwfMb8J5eGLmw5EHhoHR3", pair.public().to_ss58check()); - - let pair = store.generate_from_seed("1").unwrap(); - assert_eq!("5DYnksEZFc7kgtfyNM1xK2eBtW142gZ3Ho3NQubrF2S6B2fq", pair.public().to_ss58check()); - } -} diff --git a/substrate/misbehavior-check/Cargo.toml b/substrate/misbehavior-check/Cargo.toml deleted file mode 100644 index aa726c18b18db..0000000000000 --- a/substrate/misbehavior-check/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "substrate-misbehavior-check" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -substrate-codec = { path = "../codec", default-features = false } -substrate-primitives = { path = "../primitives", default-features = false } -substrate-runtime-primitives = { path = "../runtime/primitives", default-features = false } -substrate-runtime-io = { path = "../runtime-io", default-features = false } - -[dev-dependencies] -substrate-bft = { path = "../bft" } -rhododendron = "0.3" -substrate-keyring = { path = "../keyring" } - -[features] -default = ["std"] -std = ["substrate-codec/std", "substrate-primitives/std", "substrate-runtime-primitives/std", "substrate-runtime-io/std"] diff --git a/substrate/misbehavior-check/README.adoc b/substrate/misbehavior-check/README.adoc deleted file mode 100644 index e5b52b954b673..0000000000000 --- a/substrate/misbehavior-check/README.adoc +++ /dev/null @@ -1,13 +0,0 @@ - -= Misbehavior-check - -.Summary -[source, toml] ----- -include::Cargo.toml[lines=2..5] ----- - -.Description ----- -include::src/lib.rs[tag=description] ----- diff --git a/substrate/misbehavior-check/src/lib.rs b/substrate/misbehavior-check/src/lib.rs deleted file mode 100644 index eff87ab1e5efb..0000000000000 --- a/substrate/misbehavior-check/src/lib.rs +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -// tag::description[] -//! Utility for substrate-based runtimes that want to check misbehavior reports. -// end::description[] - -#![cfg_attr(not(feature = "std"), no_std)] - -extern crate substrate_codec as codec; -extern crate substrate_primitives as primitives; -extern crate substrate_runtime_io as runtime_io; -extern crate substrate_runtime_primitives as runtime_primitives; - -#[cfg(test)] -extern crate substrate_bft; -#[cfg(test)] -extern crate substrate_keyring as keyring; -#[cfg(test)] -extern crate rhododendron; - -use codec::{Codec, Encode}; -use primitives::{AuthorityId, Signature}; - -use runtime_primitives::bft::{Action, Message, MisbehaviorKind}; - -// check a message signature. returns true if signed by that authority. -fn check_message_sig( - message: Message, - signature: &Signature, - from: &AuthorityId -) -> bool { - let msg: Vec = message.encode(); - runtime_io::ed25519_verify(&signature.0, &msg, from) -} - -fn prepare(parent: H, round_number: u32, hash: H) -> Message { - Message { - parent, - action: Action::Prepare(round_number, hash), - } -} - -fn commit(parent: H, round_number: u32, hash: H) -> Message { - Message { - parent, - action: Action::Commit(round_number, hash), - } -} - -/// Evaluate misbehavior. -/// -/// Doesn't check that the header hash in question is -/// valid or whether the misbehaving authority was part of -/// the set at that block. -pub fn evaluate_misbehavior( - misbehaved: &AuthorityId, - parent_hash: H, - kind: &MisbehaviorKind, -) -> bool { - match *kind { - MisbehaviorKind::BftDoublePrepare(round, (h_1, ref s_1), (h_2, ref s_2)) => { - s_1 != s_2 && - check_message_sig::(prepare::(parent_hash, round, h_1), s_1, misbehaved) && - check_message_sig::(prepare::(parent_hash, round, h_2), s_2, misbehaved) - } - MisbehaviorKind::BftDoubleCommit(round, (h_1, ref s_1), (h_2, ref s_2)) => { - s_1 != s_2 && - check_message_sig::(commit::(parent_hash, round, h_1), s_1, misbehaved) && - check_message_sig::(commit::(parent_hash, round, h_2), s_2, misbehaved) - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - use keyring::ed25519; - use keyring::Keyring; - - use runtime_primitives::testing::{H256, Block as RawBlock}; - - type Block = RawBlock; - - fn sign_prepare(key: &ed25519::Pair, round: u32, hash: H256, parent_hash: H256) -> (H256, Signature) { - let msg = substrate_bft::sign_message::( - rhododendron::Message::Vote(rhododendron::Vote::Prepare(round as _, hash)), - key, - parent_hash - ); - - match msg { - rhododendron::LocalizedMessage::Vote(vote) => (hash, vote.signature.signature), - _ => panic!("signing vote leads to signed vote"), - } - } - - fn sign_commit(key: &ed25519::Pair, round: u32, hash: H256, parent_hash: H256) -> (H256, Signature) { - let msg = substrate_bft::sign_message::( - rhododendron::Message::Vote(rhododendron::Vote::Commit(round as _, hash)), - key, - parent_hash - ); - - match msg { - rhododendron::LocalizedMessage::Vote(vote) => (hash, vote.signature.signature), - _ => panic!("signing vote leads to signed vote"), - } - } - - #[test] - fn evaluates_double_prepare() { - let key: ed25519::Pair = Keyring::One.into(); - let parent_hash = [0xff; 32].into(); - let hash_1 = [0; 32].into(); - let hash_2 = [1; 32].into(); - - assert!(evaluate_misbehavior::( - &key.public().into(), - parent_hash, - &MisbehaviorKind::BftDoublePrepare( - 1, - sign_prepare(&key, 1, hash_1, parent_hash), - sign_prepare(&key, 1, hash_2, parent_hash) - ) - )); - - // same signature twice is not misbehavior. - let signed = sign_prepare(&key, 1, hash_1, parent_hash); - assert!(evaluate_misbehavior::( - &key.public().into(), - parent_hash, - &MisbehaviorKind::BftDoublePrepare( - 1, - signed, - signed, - ) - ) == false); - - // misbehavior has wrong target. - assert!(evaluate_misbehavior::( - &Keyring::Two.to_raw_public().into(), - parent_hash, - &MisbehaviorKind::BftDoublePrepare( - 1, - sign_prepare(&key, 1, hash_1, parent_hash), - sign_prepare(&key, 1, hash_2, parent_hash), - ) - ) == false); - } - - #[test] - fn evaluates_double_commit() { - let key: ed25519::Pair = Keyring::One.into(); - let parent_hash = [0xff; 32].into(); - let hash_1 = [0; 32].into(); - let hash_2 = [1; 32].into(); - - assert!(evaluate_misbehavior::( - &key.public().into(), - parent_hash, - &MisbehaviorKind::BftDoubleCommit( - 1, - sign_commit(&key, 1, hash_1, parent_hash), - sign_commit(&key, 1, hash_2, parent_hash) - ) - )); - - // same signature twice is not misbehavior. - let signed = sign_commit(&key, 1, hash_1, parent_hash); - assert!(evaluate_misbehavior::( - &key.public().into(), - parent_hash, - &MisbehaviorKind::BftDoubleCommit( - 1, - signed, - signed, - ) - ) == false); - - // misbehavior has wrong target. - assert!(evaluate_misbehavior::( - &Keyring::Two.to_raw_public().into(), - parent_hash, - &MisbehaviorKind::BftDoubleCommit( - 1, - sign_commit(&key, 1, hash_1, parent_hash), - sign_commit(&key, 1, hash_2, parent_hash), - ) - ) == false); - } -} diff --git a/substrate/network-libp2p/Cargo.toml b/substrate/network-libp2p/Cargo.toml deleted file mode 100644 index b002fa82e969a..0000000000000 --- a/substrate/network-libp2p/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ -[package] -description = "libp2p implementation of the ethcore network library" -homepage = "http://parity.io" -license = "GPL-3.0" -name = "substrate-network-libp2p" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -bytes = "0.4" -error-chain = { version = "0.12", default-features = false } -fnv = "1.0" -futures = "0.1" -libp2p = { git = "https://github.com/libp2p/rust-libp2p", rev = "304e9c72c88bc97824f2734dc19d1b5f4556d346", default-features = false, features = ["libp2p-secio", "libp2p-secio-secp256k1"] } -ethcore-io = { git = "https://github.com/paritytech/parity.git" } -ethkey = { git = "https://github.com/paritytech/parity.git" } -ethereum-types = "0.3" -parking_lot = "0.5" -libc = "0.2" -log = "0.3" -rand = "0.5.0" -serde = "1.0.70" -serde_derive = "1.0.70" -serde_json = "1.0.24" -tokio = "0.1" -tokio-io = "0.1" -tokio-timer = "0.2" -unsigned-varint = { version = "0.2.1", features = ["codec"] } - -[dev-dependencies] -assert_matches = "1.2" -parity-bytes = "0.1" -ethcore-io = { git = "https://github.com/paritytech/parity.git" } -ethcore-logger = { git = "https://github.com/paritytech/parity.git" } diff --git a/substrate/network-libp2p/README.adoc b/substrate/network-libp2p/README.adoc deleted file mode 100644 index 2f340aa397e73..0000000000000 --- a/substrate/network-libp2p/README.adoc +++ /dev/null @@ -1,13 +0,0 @@ - -= Network libp2p - -.Summary -[source, toml] ----- -include::Cargo.toml[lines=2..5] ----- - -.Description ----- -include::src/lib.rs[tag=description] ----- diff --git a/substrate/network-libp2p/src/connection_filter.rs b/substrate/network-libp2p/src/connection_filter.rs deleted file mode 100644 index 46d9d86b58d33..0000000000000 --- a/substrate/network-libp2p/src/connection_filter.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2015-2018 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 . - -//! Connection filter trait. - -use super::NodeId; - -/// Filtered connection direction. -pub enum ConnectionDirection { - Inbound, - Outbound, -} - -/// Connection filter. Each connection is checked against `connection_allowed`. -pub trait ConnectionFilter : Send + Sync { - /// Filter a connection. Returns `true` if connection should be allowed. `false` if rejected. - fn connection_allowed(&self, own_id: &NodeId, connecting_id: &NodeId, direction: ConnectionDirection) -> bool; -} diff --git a/substrate/network-libp2p/src/custom_proto.rs b/substrate/network-libp2p/src/custom_proto.rs deleted file mode 100644 index 72807f21e8f2e..0000000000000 --- a/substrate/network-libp2p/src/custom_proto.rs +++ /dev/null @@ -1,290 +0,0 @@ -// Copyright 2018 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 bytes::{Bytes, BytesMut}; -use ProtocolId; -use libp2p::core::{Multiaddr, ConnectionUpgrade, Endpoint}; -use PacketId; -use std::io::Error as IoError; -use std::vec::IntoIter as VecIntoIter; -use futures::{future, Future, stream, Stream, Sink}; -use futures::sync::mpsc; -use tokio_io::{AsyncRead, AsyncWrite}; -use unsigned_varint::codec::UviBytes; - -/// Connection upgrade for a single protocol. -/// -/// Note that "a single protocol" here refers to `par` for example. However -/// each protocol can have multiple different versions for networking purposes. -#[derive(Clone)] -pub struct RegisteredProtocol { - /// Id of the protocol for API purposes. - id: ProtocolId, - /// Base name of the protocol as advertised on the network. - /// Ends with `/` so that we can append a version number behind. - base_name: Bytes, - /// List of protocol versions that we support, plus their packet count. - /// Ordered in descending order so that the best comes first. - /// The packet count is used to filter out invalid messages. - supported_versions: Vec<(u8, u8)>, - /// Custom data. - custom_data: T, -} - -/// Output of a `RegisteredProtocol` upgrade. -pub struct RegisteredProtocolOutput { - /// Data passed to `RegisteredProtocol::new`. - pub custom_data: T, - - /// Id of the protocol. - pub protocol_id: ProtocolId, - - /// Endpoint of the connection. - pub endpoint: Endpoint, - - /// Version of the protocol that was negotiated. - pub protocol_version: u8, - - /// Channel to sender outgoing messages to. - // TODO: consider assembling packet_id here - pub outgoing: mpsc::UnboundedSender, - - /// Stream where incoming messages are received. The stream ends whenever - /// either side is closed. - pub incoming: Box + Send>, -} - -impl RegisteredProtocol { - /// Creates a new `RegisteredProtocol`. The `custom_data` parameter will be - /// passed inside the `RegisteredProtocolOutput`. - pub fn new(custom_data: T, protocol: ProtocolId, versions: &[(u8, u8)]) - -> Self { - let mut proto_name = Bytes::from_static(b"/substrate/"); - proto_name.extend_from_slice(&protocol); - proto_name.extend_from_slice(b"/"); - - RegisteredProtocol { - base_name: proto_name, - id: protocol, - supported_versions: { - let mut tmp: Vec<_> = versions.iter().rev().cloned().collect(); - tmp.sort_unstable_by(|a, b| b.1.cmp(&a.1)); - tmp - }, - custom_data: custom_data, - } - } - - /// Returns the ID of the protocol. - pub fn id(&self) -> ProtocolId { - self.id - } - - /// Returns the custom data that was passed to `new`. - pub fn custom_data(&self) -> &T { - &self.custom_data - } -} - -// `Maf` is short for `MultiaddressFuture` -impl ConnectionUpgrade for RegisteredProtocol -where C: AsyncRead + AsyncWrite + Send + 'static, // TODO: 'static :-/ - Maf: Future + Send + 'static, // TODO: 'static :( -{ - type NamesIter = VecIntoIter<(Bytes, Self::UpgradeIdentifier)>; - type UpgradeIdentifier = u8; // Protocol version - - #[inline] - fn protocol_names(&self) -> Self::NamesIter { - // Report each version as an individual protocol. - self.supported_versions.iter().map(|&(ver, _)| { - let num = ver.to_string(); - let mut name = self.base_name.clone(); - name.extend_from_slice(num.as_bytes()); - (name, ver) - }).collect::>().into_iter() - } - - type Output = RegisteredProtocolOutput; - type MultiaddrFuture = Maf; - type Future = future::FutureResult<(Self::Output, Self::MultiaddrFuture), IoError>; - - #[allow(deprecated)] - fn upgrade( - self, - socket: C, - protocol_version: Self::UpgradeIdentifier, - endpoint: Endpoint, - remote_addr: Maf - ) -> Self::Future { - let packet_count = self.supported_versions - .iter() - .find(|&(v, _)| *v == protocol_version) - .expect("negotiated protocol version that wasn't advertised ; \ - programmer error") - .1; - - // This function is called whenever we successfully negotiated a - // protocol with a remote (both if initiated by us or by the remote) - - // This channel is used to send outgoing packets to the custom_data - // for this open substream. - let (msg_tx, msg_rx) = mpsc::unbounded(); - - // Build the sink for outgoing network bytes, and the stream for - // incoming instructions. `stream` implements `Stream`. - enum Message { - /// Received data from the network. - RecvSocket(BytesMut), - /// Data to send to the network. - /// The packet_id must already be inside the `Bytes`. - SendReq(Bytes), - /// The socket has been closed. - Finished, - } - - let (sink, stream) = { - let framed = AsyncRead::framed(socket, UviBytes::default()); - let msg_rx = msg_rx.map(Message::SendReq) - .map_err(|()| unreachable!("mpsc::UnboundedReceiver never errors")); - let (sink, stream) = framed.split(); - let stream = stream.map(Message::RecvSocket) - .chain(stream::once(Ok(Message::Finished))); - (sink, msg_rx.select(stream)) - }; - - let incoming = stream::unfold((sink, stream, false), move |(sink, stream, finished)| { - if finished { - return None - } - - Some(stream - .into_future() - .map_err(|(err, _)| err) - .and_then(move |(message, stream)| - match message { - Some(Message::RecvSocket(mut data)) => { - // The `data` should be prefixed by the packet ID, - // therefore an empty packet is invalid. - if data.is_empty() { - debug!(target: "sub-libp2p", "ignoring incoming \ - packet because it was empty"); - let f = future::ok((None, (sink, stream, false))); - return future::Either::A(f) - } - - let packet_id = data[0]; - let data = data.split_off(1); - - if packet_id >= packet_count { - debug!(target: "sub-libp2p", "ignoring incoming packet \ - because packet_id {} is too large", packet_id); - let f = future::ok((None, (sink, stream, false))); - future::Either::A(f) - } else { - let out = Some((packet_id, data.freeze())); - let f = future::ok((out, (sink, stream, false))); - future::Either::A(f) - } - }, - - Some(Message::SendReq(data)) => { - let fut = sink.send(data) - .map(move |sink| (None, (sink, stream, false))); - future::Either::B(fut) - }, - - Some(Message::Finished) | None => { - let f = future::ok((None, (sink, stream, true))); - future::Either::A(f) - }, - } - )) - }).filter_map(|v| v); - - let out = RegisteredProtocolOutput { - custom_data: self.custom_data, - protocol_id: self.id, - endpoint, - protocol_version: protocol_version, - outgoing: msg_tx, - incoming: Box::new(incoming), - }; - - future::ok((out, remote_addr)) - } -} - -// Connection upgrade for all the protocols contained in it. -#[derive(Clone)] -pub struct RegisteredProtocols(pub Vec>); - -impl RegisteredProtocols { - /// Finds a protocol in the list by its id. - pub fn find_protocol(&self, protocol: ProtocolId) - -> Option<&RegisteredProtocol> { - self.0.iter().find(|p| p.id == protocol) - } - - /// Returns true if the given protocol is in the list. - pub fn has_protocol(&self, protocol: ProtocolId) -> bool { - self.0.iter().any(|p| p.id == protocol) - } -} - -impl Default for RegisteredProtocols { - fn default() -> Self { - RegisteredProtocols(Vec::new()) - } -} - -impl ConnectionUpgrade for RegisteredProtocols -where C: AsyncRead + AsyncWrite + Send + 'static, // TODO: 'static :-/ - Maf: Future + Send + 'static, // TODO: 'static :( -{ - type NamesIter = VecIntoIter<(Bytes, Self::UpgradeIdentifier)>; - type UpgradeIdentifier = (usize, - as ConnectionUpgrade>::UpgradeIdentifier); - - fn protocol_names(&self) -> Self::NamesIter { - // We concat the lists of `RegisteredProtocol::protocol_names` for - // each protocol. - self.0.iter().enumerate().flat_map(|(n, proto)| - ConnectionUpgrade::::protocol_names(proto) - .map(move |(name, id)| (name, (n, id))) - ).collect::>().into_iter() - } - - type Output = as ConnectionUpgrade>::Output; - type MultiaddrFuture = as - ConnectionUpgrade>::MultiaddrFuture; - type Future = as ConnectionUpgrade>::Future; - - #[inline] - fn upgrade( - self, - socket: C, - upgrade_identifier: Self::UpgradeIdentifier, - endpoint: Endpoint, - remote_addr: Maf - ) -> Self::Future { - let (protocol_index, inner_proto_id) = upgrade_identifier; - self.0.into_iter() - .nth(protocol_index) - .expect("invalid protocol index ; programmer logic error") - .upgrade(socket, inner_proto_id, endpoint, remote_addr) - } -} diff --git a/substrate/network-libp2p/src/error.rs b/substrate/network-libp2p/src/error.rs deleted file mode 100644 index d095858b12203..0000000000000 --- a/substrate/network-libp2p/src/error.rs +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright 2015-2018 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::{io, net, fmt}; -use libc::{ENFILE, EMFILE}; -use io::IoError; -use ethkey; - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum DisconnectReason -{ - DisconnectRequested, - TCPError, - BadProtocol, - UselessPeer, - TooManyPeers, - DuplicatePeer, - IncompatibleProtocol, - NullIdentity, - ClientQuit, - UnexpectedIdentity, - LocalIdentity, - PingTimeout, - Unknown, -} - -impl DisconnectReason { - pub fn from_u8(n: u8) -> DisconnectReason { - match n { - 0 => DisconnectReason::DisconnectRequested, - 1 => DisconnectReason::TCPError, - 2 => DisconnectReason::BadProtocol, - 3 => DisconnectReason::UselessPeer, - 4 => DisconnectReason::TooManyPeers, - 5 => DisconnectReason::DuplicatePeer, - 6 => DisconnectReason::IncompatibleProtocol, - 7 => DisconnectReason::NullIdentity, - 8 => DisconnectReason::ClientQuit, - 9 => DisconnectReason::UnexpectedIdentity, - 10 => DisconnectReason::LocalIdentity, - 11 => DisconnectReason::PingTimeout, - _ => DisconnectReason::Unknown, - } - } -} - -impl fmt::Display for DisconnectReason { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use self::DisconnectReason::*; - - let msg = match *self { - DisconnectRequested => "disconnect requested", - TCPError => "TCP error", - BadProtocol => "bad protocol", - UselessPeer => "useless peer", - TooManyPeers => "too many peers", - DuplicatePeer => "duplicate peer", - IncompatibleProtocol => "incompatible protocol", - NullIdentity => "null identity", - ClientQuit => "client quit", - UnexpectedIdentity => "unexpected identity", - LocalIdentity => "local identity", - PingTimeout => "ping timeout", - Unknown => "unknown", - }; - - f.write_str(msg) - } -} - -error_chain! { - foreign_links { - SocketIo(IoError) #[doc = "Socket IO error."]; - } - - errors { - #[doc = "Error concerning the network address parsing subsystem."] - AddressParse { - description("Failed to parse network address"), - display("Failed to parse network address"), - } - - #[doc = "Error concerning the network address resolution subsystem."] - AddressResolve(err: Option) { - description("Failed to resolve network address"), - display("Failed to resolve network address {}", err.as_ref().map_or("".to_string(), |e| e.to_string())), - } - - #[doc = "Authentication failure"] - Auth { - description("Authentication failure"), - display("Authentication failure"), - } - - #[doc = "Unrecognised protocol"] - BadProtocol { - description("Bad protocol"), - display("Bad protocol"), - } - - #[doc = "Expired message"] - Expired { - description("Expired message"), - display("Expired message"), - } - - #[doc = "Peer not found"] - PeerNotFound { - description("Peer not found"), - display("Peer not found"), - } - - #[doc = "Peer is disconnected"] - Disconnect(reason: DisconnectReason) { - description("Peer disconnected"), - display("Peer disconnected: {}", reason), - } - - #[doc = "Invalid node id"] - InvalidNodeId { - description("Invalid node id"), - display("Invalid node id"), - } - - #[doc = "Packet size is over the protocol limit"] - OversizedPacket { - description("Packet is too large"), - display("Packet is too large"), - } - - #[doc = "Reached system resource limits for this process"] - ProcessTooManyFiles { - description("Too many open files in process."), - display("Too many open files in this process. Check your resource limits and restart parity"), - } - - #[doc = "Reached system wide resource limits"] - SystemTooManyFiles { - description("Too many open files on system."), - display("Too many open files on system. Consider closing some processes/release some file handlers or increas the system-wide resource limits and restart parity."), - } - - #[doc = "An unknown IO error occurred."] - Io(err: io::Error) { - description("IO Error"), - display("Unexpected IO error: {}", err), - } - } -} - -impl From for Error { - fn from(err: io::Error) -> Self { - match err.raw_os_error() { - Some(ENFILE) => ErrorKind::ProcessTooManyFiles.into(), - Some(EMFILE) => ErrorKind::SystemTooManyFiles.into(), - _ => Error::from_kind(ErrorKind::Io(err)) - } - } -} - -impl From for Error { - fn from(_err: ethkey::Error) -> Self { - ErrorKind::Auth.into() - } -} - -impl From for Error { - fn from(_err: ethkey::crypto::Error) -> Self { - ErrorKind::Auth.into() - } -} - -impl From for Error { - fn from(_err: net::AddrParseError) -> Self { ErrorKind::AddressParse.into() } -} - -#[test] -fn test_errors() { - assert_eq!(DisconnectReason::ClientQuit, DisconnectReason::from_u8(8)); - let mut r = DisconnectReason::DisconnectRequested; - for i in 0 .. 20 { - r = DisconnectReason::from_u8(i); - } - assert_eq!(DisconnectReason::Unknown, r); -} - -#[test] -fn test_io_errors() { - use libc::{EMFILE, ENFILE}; - - assert_matches!( - >::from( - io::Error::from_raw_os_error(ENFILE) - ).kind(), - ErrorKind::ProcessTooManyFiles); - - assert_matches!( - >::from( - io::Error::from_raw_os_error(EMFILE) - ).kind(), - ErrorKind::SystemTooManyFiles); - - assert_matches!( - >::from( - io::Error::from_raw_os_error(0) - ).kind(), - ErrorKind::Io(_)); -} diff --git a/substrate/network-libp2p/src/lib.rs b/substrate/network-libp2p/src/lib.rs deleted file mode 100644 index f06c8fefd865f..0000000000000 --- a/substrate/network-libp2p/src/lib.rs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -// tag::description[] -//! TODO: Missing doc -// end::description[] - -#![recursion_limit="128"] -#![type_length_limit = "268435456"] - -extern crate parking_lot; -extern crate fnv; -extern crate futures; -extern crate tokio; -extern crate tokio_io; -extern crate tokio_timer; -extern crate ethkey; -extern crate libc; -extern crate libp2p; -extern crate rand; -extern crate serde; -#[macro_use] -extern crate serde_derive; -extern crate serde_json; -extern crate bytes; -extern crate unsigned_varint; - -extern crate ethcore_io as io; -extern crate ethereum_types; - -#[macro_use] -extern crate error_chain; -#[macro_use] -extern crate log; -#[cfg(test)] #[macro_use] -extern crate assert_matches; - -pub use connection_filter::{ConnectionFilter, ConnectionDirection}; -pub use io::TimerToken; -pub use error::{Error, ErrorKind, DisconnectReason}; -pub use libp2p::{Multiaddr, multiaddr::AddrComponent}; -pub use traits::*; - -mod connection_filter; -mod custom_proto; -mod error; -mod network_state; -mod service; -mod timeouts; -mod topology; -mod traits; -mod transport; - -pub use service::NetworkService; - -/// Check if node url is valid -pub fn validate_node_url(url: &str) -> Result<(), Error> { - match url.parse::() { - Ok(_) => Ok(()), - Err(_) => Err(ErrorKind::InvalidNodeId.into()), - } -} diff --git a/substrate/network-libp2p/src/network_state.rs b/substrate/network-libp2p/src/network_state.rs deleted file mode 100644 index e06735f647fc5..0000000000000 --- a/substrate/network-libp2p/src/network_state.rs +++ /dev/null @@ -1,953 +0,0 @@ -// Copyright 2018 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 bytes::Bytes; -use fnv::{FnvHashMap, FnvHashSet}; -use futures::sync::mpsc; -use libp2p::core::{multiaddr::ToMultiaddr, Multiaddr, AddrComponent, Endpoint, UniqueConnec}; -use libp2p::core::{UniqueConnecState, PeerId, PublicKey}; -use libp2p::kad::KadConnecController; -use libp2p::ping::Pinger; -use libp2p::secio; -use {Error, ErrorKind, NetworkConfiguration, NonReservedPeerMode}; -use {NodeIndex, ProtocolId, SessionInfo}; -use parking_lot::{Mutex, RwLock}; -use rand::{self, Rng}; -use topology::{DisconnectReason, NetTopology}; -use std::fs; -use std::io::{Error as IoError, ErrorKind as IoErrorKind, Read, Write}; -use std::path::Path; -use std::sync::atomic; -use std::time::{Duration, Instant}; - -// File where the peers are stored. -const NODES_FILE: &str = "nodes.json"; -// File where the private key is stored. -const SECRET_FILE: &str = "secret"; -// Duration during which a peer is disabled. -const PEER_DISABLE_DURATION: Duration = Duration::from_secs(5 * 60); - -// Common struct shared throughout all the components of the service. -pub struct NetworkState { - /// Contains the information about the network. - topology: RwLock, - - /// Active connections. - connections: RwLock, - - /// Maximum incoming peers. - max_incoming_peers: u32, - /// Maximum outgoing peers. - max_outgoing_peers: u32, - - /// If true, only reserved peers can connect. - reserved_only: atomic::AtomicBool, - /// List of the IDs of the reserved peers. - reserved_peers: RwLock>, - - /// Each node we discover gets assigned a new unique ID. This ID increases linearly. - next_node_index: atomic::AtomicUsize, - - /// List of the IDs of the disabled peers. These peers will see their - /// connections refused. Includes the time when the disabling expires. - disabled_nodes: Mutex>, - - /// Local private key. - local_private_key: secio::SecioKeyPair, - /// Local public key. - local_public_key: PublicKey, -} - -struct Connections { - /// For each libp2p peer ID, the ID of the peer in the API we expose. - /// Also corresponds to the index in `info_by_peer`. - peer_by_nodeid: FnvHashMap, - - /// For each peer ID, information about our connection to this peer. - info_by_peer: FnvHashMap, -} - -struct PeerConnectionInfo { - /// A list of protocols, and the potential corresponding connection. - /// The `UniqueConnec` contains a sender and the protocol version. - /// The sender can be used to transmit data for the remote. Note that the - /// packet_id has to be inside the `Bytes`. - protocols: Vec<(ProtocolId, UniqueConnec<(mpsc::UnboundedSender, u8)>)>, - - /// The Kademlia connection to this node. - kad_connec: UniqueConnec, - - /// The ping connection to this node. - ping_connec: UniqueConnec, - - /// Id of the peer. - id: PeerId, - - /// True if this connection was initiated by us. `None` if we're not connected. - /// Note that it is theoretically possible that we dial the remote at the - /// same time they dial us, in which case the protocols may be dispatched - /// between both connections, and in which case the value here will be racy. - originated: Option, - - /// Latest known ping duration. - ping: Option, - - /// The client version of the remote, or `None` if not known. - client_version: Option, - - /// The multiaddresses of the remote, or `None` if not known. - remote_addresses: Vec, - - /// The local multiaddress used to communicate with the remote, or `None` - /// if not known. - // TODO: never filled ; also shouldn't be an `Option` - local_address: Option, -} - -/// Simplified, POD version of PeerConnectionInfo. -#[derive(Debug, Clone)] -pub struct PeerInfo { - /// Id of the peer. - pub id: PeerId, - - /// True if this connection was initiated by us. - /// Note that it is theoretically possible that we dial the remote at the - /// same time they dial us, in which case the protocols may be dispatched - /// between both connections, and in which case the value here will be racy. - pub originated: bool, - - /// Latest known ping duration. - pub ping: Option, - - /// The client version of the remote, or `None` if not known. - pub client_version: Option, - - /// The multiaddress of the remote. - pub remote_address: Option, - - /// The local multiaddress used to communicate with the remote, or `None` - /// if not known. - pub local_address: Option, -} - -impl<'a> From<&'a PeerConnectionInfo> for PeerInfo { - fn from(i: &'a PeerConnectionInfo) -> PeerInfo { - PeerInfo { - id: i.id.clone(), - originated: i.originated.unwrap_or(true), - ping: i.ping, - client_version: i.client_version.clone(), - remote_address: i.remote_addresses.get(0).map(|a| a.clone()), - local_address: i.local_address.clone(), - } - } -} - -impl NetworkState { - pub fn new(config: &NetworkConfiguration) -> Result { - // Private and public keys configuration. - let local_private_key = obtain_private_key(&config)?; - let local_public_key = local_private_key.to_public_key(); - - // Build the storage for peers, including the bootstrap nodes. - let mut topology = if let Some(ref path) = config.net_config_path { - let path = Path::new(path).join(NODES_FILE); - debug!(target: "sub-libp2p", "Initializing peer store for JSON file {:?}", path); - NetTopology::from_file(path) - } else { - debug!(target: "sub-libp2p", "No peers file configured ; peers won't be saved"); - NetTopology::memory() - }; - - let reserved_peers = { - let mut reserved_peers = FnvHashSet::with_capacity_and_hasher( - config.reserved_nodes.len(), - Default::default() - ); - for peer in config.reserved_nodes.iter() { - let (id, _) = parse_and_add_to_topology(peer, &mut topology)?; - reserved_peers.insert(id); - } - RwLock::new(reserved_peers) - }; - - let expected_max_peers = config.max_peers as usize + config.reserved_nodes.len(); - - Ok(NetworkState { - topology: RwLock::new(topology), - max_outgoing_peers: config.min_peers, - max_incoming_peers: config.max_peers.saturating_sub(config.min_peers), - connections: RwLock::new(Connections { - peer_by_nodeid: FnvHashMap::with_capacity_and_hasher(expected_max_peers, Default::default()), - info_by_peer: FnvHashMap::with_capacity_and_hasher(expected_max_peers, Default::default()), - }), - reserved_only: atomic::AtomicBool::new(config.non_reserved_mode == NonReservedPeerMode::Deny), - reserved_peers, - next_node_index: atomic::AtomicUsize::new(0), - disabled_nodes: Mutex::new(Default::default()), - local_private_key, - local_public_key, - }) - } - - /// Returns the private key of the local node. - pub fn local_private_key(&self) -> &secio::SecioKeyPair { - &self.local_private_key - } - - /// Returns the public key of the local node. - pub fn local_public_key(&self) -> &PublicKey { - &self.local_public_key - } - - /// Returns a list of peers and addresses which we should try connect to. - /// - /// Because of expiration and back-off mechanisms, this list can change - /// by itself over time. The `Instant` that is returned corresponds to - /// the earlier known time when a new entry will be added automatically to - /// the list. - pub fn outgoing_connections_to_attempt(&self) -> (Vec<(PeerId, Multiaddr)>, Instant) { - // TODO: handle better - let connections = self.connections.read(); - - let num_to_attempt = if self.reserved_only.load(atomic::Ordering::Relaxed) { - 0 - } else { - let num_open_custom_connections = num_open_custom_connections(&connections, &self.reserved_peers.read()); - self.max_outgoing_peers.saturating_sub(num_open_custom_connections.unreserved_outgoing) - }; - - let topology = self.topology.read(); - let (list, change) = topology.addrs_to_attempt(); - let list = list - .filter(|&(peer, _)| { - // Filter out peers which we are already connected to. - let cur = match connections.peer_by_nodeid.get(peer) { - Some(e) => e, - None => return true - }; - - let infos = match connections.info_by_peer.get(&cur) { - Some(i) => i, - None => return true - }; - - !infos.protocols.iter().any(|(_, conn)| conn.is_alive()) - }) - .take(num_to_attempt as usize) - .map(|(addr, peer)| (addr.clone(), peer.clone())) - .collect(); - (list, change) - } - - /// Returns true if we are connected to any peer at all. - pub fn has_connected_peer(&self) -> bool { - !self.connections.read().peer_by_nodeid.is_empty() - } - - /// Get a list of all connected peers by id. - pub fn connected_peers(&self) -> Vec { - self.connections.read().peer_by_nodeid.values().cloned().collect() - } - - /// Returns true if the given `NodeIndex` is valid. - /// - /// `NodeIndex`s are never reused, so once this function returns `false` it - /// will never return `true` again for the same `NodeIndex`. - pub fn is_peer_connected(&self, peer: NodeIndex) -> bool { - self.connections.read().info_by_peer.contains_key(&peer) - } - - /// Reports the ping of the peer. Returned later by `session_info()`. - /// No-op if the `who` is not valid/expired. - pub fn report_ping_duration(&self, who: NodeIndex, ping: Duration) { - let mut connections = self.connections.write(); - let info = match connections.info_by_peer.get_mut(&who) { - Some(info) => info, - None => return, - }; - info.ping = Some(ping); - } - - /// If we're connected to a peer with the given protocol, returns - /// information about the connection. Otherwise, returns `None`. - pub fn session_info(&self, peer: NodeIndex, protocol: ProtocolId) -> Option { - let connections = self.connections.read(); - let info = match connections.info_by_peer.get(&peer) { - Some(info) => info, - None => return None, - }; - - let protocol_version = match info.protocols.iter().find(|&(ref p, _)| p == &protocol) { - Some(&(_, ref unique_connec)) => - if let Some(val) = unique_connec.poll() { - val.1 as u32 - } else { - return None - } - None => return None, - }; - - Some(SessionInfo { - id: None, // TODO: ???? what to do??? wrong format! - client_version: info.client_version.clone().take().unwrap_or(String::new()), - protocol_version, - capabilities: Vec::new(), // TODO: list of supported protocols ; hard - peer_capabilities: Vec::new(), // TODO: difference with `peer_capabilities`? - ping: info.ping, - originated: info.originated.unwrap_or(true), - remote_address: info.remote_addresses.get(0).map(|a| a.to_string()).unwrap_or_default(), - local_address: info.local_address.as_ref().map(|a| a.to_string()) - .unwrap_or(String::new()), - }) - } - - /// If we're connected to a peer with the given protocol, returns the - /// protocol version. Otherwise, returns `None`. - pub fn protocol_version(&self, peer: NodeIndex, protocol: ProtocolId) -> Option { - let connections = self.connections.read(); - let peer = match connections.info_by_peer.get(&peer) { - Some(peer) => peer, - None => return None, - }; - - peer.protocols.iter() - .find(|p| p.0 == protocol) - .and_then(|p| p.1.poll()) - .map(|(_, version)| version) - } - - /// Equivalent to `session_info(peer).map(|info| info.client_version)`. - pub fn peer_client_version(&self, peer: NodeIndex, protocol: ProtocolId) -> Option { - // TODO: implement more directly, without going through `session_info` - self.session_info(peer, protocol) - .map(|info| info.client_version) - } - - /// Adds an address discovered by Kademlia. - /// Note that we don't have to be connected to a peer to add an address. - /// If `connectable` is `true`, that means we have a hint from a remote that this node can be - /// connected to. - pub fn add_kad_discovered_addr(&self, node_id: &PeerId, addr: Multiaddr, connectable: bool) { - self.topology.write().add_kademlia_discovered_addr(node_id, addr, connectable) - } - - /// Returns the known multiaddresses of a peer. - /// - /// The boolean associated to each address indicates whether we're connected to it. - pub fn addrs_of_peer(&self, node_id: &PeerId) -> Vec<(Multiaddr, bool)> { - let topology = self.topology.read(); - // Note: I have no idea why, but fusing the two lines below fails the - // borrow check - let out: Vec<_> = topology - .addrs_of_peer(node_id).map(|(a, c)| (a.clone(), c)).collect(); - out - } - - /// Sets information about a peer. - /// - /// No-op if the node index is invalid. - pub fn set_node_info( - &self, - node_index: NodeIndex, - client_version: String - ) { - let mut connections = self.connections.write(); - let infos = match connections.info_by_peer.get_mut(&node_index) { - Some(i) => i, - None => return - }; - - infos.client_version = Some(client_version); - } - - /// Adds a peer to the internal peer store. - /// Returns an error if the peer address is invalid. - pub fn add_bootstrap_peer(&self, peer: &str) -> Result<(PeerId, Multiaddr), Error> { - parse_and_add_to_topology(peer, &mut self.topology.write()) - } - - /// Adds a reserved peer to the list of reserved peers. - /// Returns an error if the peer address is invalid. - pub fn add_reserved_peer(&self, peer: &str) -> Result<(), Error> { - let (id, _) = parse_and_add_to_topology(peer, &mut self.topology.write())?; - self.reserved_peers.write().insert(id); - Ok(()) - } - - /// Removes the peer from the list of reserved peers. If we're in reserved mode, drops any - /// active connection to this peer. - /// Returns an error if the peer address is invalid. - pub fn remove_reserved_peer(&self, peer: &str) -> Result<(), Error> { - let (id, _) = parse_and_add_to_topology(peer, &mut self.topology.write())?; - self.reserved_peers.write().remove(&id); - - // Dropping the peer if we're in reserved mode. - if self.reserved_only.load(atomic::Ordering::SeqCst) { - let mut connections = self.connections.write(); - if let Some(who) = connections.peer_by_nodeid.remove(&id) { - connections.info_by_peer.remove(&who); - // TODO: use drop_peer instead - } - } - - Ok(()) - } - - /// Set the non-reserved peer mode. - pub fn set_non_reserved_mode(&self, mode: NonReservedPeerMode) { - match mode { - NonReservedPeerMode::Accept => - self.reserved_only.store(false, atomic::Ordering::SeqCst), - NonReservedPeerMode::Deny => - // TODO: drop existing peers? - self.reserved_only.store(true, atomic::Ordering::SeqCst), - } - } - - /// Reports that we tried to connect to the given address but failed. - /// - /// This decreases the chance this address will be tried again in the future. - #[inline] - pub fn report_failed_to_connect(&self, addr: &Multiaddr) { - trace!(target: "sub-libp2p", "Failed to connect to {:?}", addr); - self.topology.write().report_failed_to_connect(addr); - } - - /// Returns the `NodeIndex` corresponding to a node id, or assigns a `NodeIndex` if none - /// exists. - /// - /// Returns an error if this node is on the list of disabled/banned nodes.. - pub fn assign_node_index( - &self, - node_id: &PeerId - ) -> Result { - // Check whether node is disabled. - // TODO: figure out the locking strategy here to avoid possible deadlocks - // TODO: put disabled_nodes in connections? - let mut disabled_nodes = self.disabled_nodes.lock(); - if let Some(timeout) = disabled_nodes.get(node_id).cloned() { - if timeout > Instant::now() { - debug!(target: "sub-libp2p", "Refusing peer {:?} because it is disabled", node_id); - return Err(IoError::new(IoErrorKind::ConnectionRefused, "peer is disabled")); - } else { - disabled_nodes.remove(node_id); - } - } - drop(disabled_nodes); - - let mut connections = self.connections.write(); - let connections = &mut *connections; - let peer_by_nodeid = &mut connections.peer_by_nodeid; - let info_by_peer = &mut connections.info_by_peer; - - let who = *peer_by_nodeid.entry(node_id.clone()).or_insert_with(|| { - let new_id = self.next_node_index.fetch_add(1, atomic::Ordering::Relaxed); - trace!(target: "sub-libp2p", "Creating new peer #{:?} for {:?}", new_id, node_id); - - info_by_peer.insert(new_id, PeerConnectionInfo { - protocols: Vec::new(), // TODO: Vec::with_capacity(num_registered_protocols), - kad_connec: UniqueConnec::empty(), - ping_connec: UniqueConnec::empty(), - id: node_id.clone(), - originated: None, - ping: None, - client_version: None, - local_address: None, - remote_addresses: Vec::with_capacity(1), - }); - - new_id - }); - - Ok(who) - } - - /// Notifies that we're connected to a node through an address. - /// - /// Returns an error if we refuse the connection. - /// - /// Note that is it legal to connection multiple times to the same node id through different - /// addresses and endpoints. - pub fn report_connected( - &self, - node_index: NodeIndex, - addr: &Multiaddr, - endpoint: Endpoint - ) -> Result<(), IoError> { - let mut connections = self.connections.write(); - - // TODO: double locking in this function ; although this has been reviewed to not deadlock - // as of the writing of this code, it is possible that a later change that isn't carefully - // reviewed triggers one - - if endpoint == Endpoint::Listener { - let stats = num_open_custom_connections(&connections, &self.reserved_peers.read()); - if stats.unreserved_incoming >= self.max_incoming_peers { - debug!(target: "sub-libp2p", "Refusing incoming connection from {} because we \ - reached max incoming peers", addr); - return Err(IoError::new(IoErrorKind::ConnectionRefused, - "maximum incoming peers reached")); - } - } - - let infos = match connections.info_by_peer.get_mut(&node_index) { - Some(i) => i, - None => return Ok(()) - }; - - if !infos.remote_addresses.iter().any(|a| a == addr) { - infos.remote_addresses.push(addr.clone()); - } - - if infos.originated.is_none() { - infos.originated = Some(endpoint == Endpoint::Dialer); - } - - self.topology.write().report_connected(addr, &infos.id); - - Ok(()) - } - - /// Returns the node id from a node index. - /// - /// Returns `None` if the node index is invalid. - pub fn node_id_from_index( - &self, - node_index: NodeIndex - ) -> Option { - let mut connections = self.connections.write(); - let infos = match connections.info_by_peer.get_mut(&node_index) { - Some(i) => i, - None => return None - }; - Some(infos.id.clone()) - } - - /// Obtains the `UniqueConnec` corresponding to the Kademlia connection to a peer. - /// - /// Returns `None` if the node index is invalid. - pub fn kad_connection( - &self, - node_index: NodeIndex - ) -> Option> { - let mut connections = self.connections.write(); - let infos = match connections.info_by_peer.get_mut(&node_index) { - Some(i) => i, - None => return None - }; - Some(infos.kad_connec.clone()) - } - - /// Obtains the `UniqueConnec` corresponding to the Ping connection to a peer. - /// - /// Returns `None` if the node index is invalid. - pub fn ping_connection( - &self, - node_index: NodeIndex - ) -> Option> { - let mut connections = self.connections.write(); - let infos = match connections.info_by_peer.get_mut(&node_index) { - Some(i) => i, - None => return None - }; - Some(infos.ping_connec.clone()) - } - - /// Cleans up inactive connections and returns a list of - /// connections to ping and identify. - pub fn cleanup_and_prepare_updates( - &self - ) -> Vec { - self.topology.write().cleanup(); - - let mut connections = self.connections.write(); - let connections = &mut *connections; - let peer_by_nodeid = &mut connections.peer_by_nodeid; - let info_by_peer = &mut connections.info_by_peer; - - let mut ret = Vec::with_capacity(info_by_peer.len()); - info_by_peer.retain(|&who, infos| { - // Remove the peer if neither Kad nor any protocol is alive. - if !infos.kad_connec.is_alive() && - !infos.protocols.iter().any(|(_, conn)| conn.is_alive()) - { - peer_by_nodeid.remove(&infos.id); - trace!(target: "sub-libp2p", "Cleaning up expired peer \ - #{:?} ({:?})", who, infos.id); - return false; - } - - if let Some(addr) = infos.remote_addresses.get(0) { - ret.push(PeriodicUpdate { - node_index: who, - peer_id: infos.id.clone(), - address: addr.clone(), - pinger: infos.ping_connec.clone(), - identify: infos.client_version.is_none(), - }); - } - true - }); - ret - } - - /// Obtains the `UniqueConnec` corresponding to a custom protocol connection to a peer. - /// - /// Returns `None` if the node index is invalid. - pub fn custom_proto( - &self, - node_index: NodeIndex, - protocol_id: ProtocolId, - ) -> Option, u8)>> { - let mut connections = self.connections.write(); - let infos = match connections.info_by_peer.get_mut(&node_index) { - Some(i) => i, - None => return None - }; - - if let Some((_, ref uconn)) = infos.protocols.iter().find(|&(prot, _)| prot == &protocol_id) { - return Some(uconn.clone()) - } - - let unique_connec = UniqueConnec::empty(); - infos.protocols.push((protocol_id.clone(), unique_connec.clone())); - Some(unique_connec) - } - - /// Sends some data to the given peer, using the sender that was passed - /// to the `UniqueConnec` of `custom_proto`. - pub fn send(&self, who: NodeIndex, protocol: ProtocolId, message: Bytes) -> Result<(), Error> { - if let Some(peer) = self.connections.read().info_by_peer.get(&who) { - let sender = peer.protocols.iter().find(|elem| elem.0 == protocol) - .and_then(|e| e.1.poll()) - .map(|e| e.0); - if let Some(sender) = sender { - sender.unbounded_send(message) - .map_err(|err| ErrorKind::Io(IoError::new(IoErrorKind::Other, err)))?; - Ok(()) - } else { - // We are connected to this peer, but not with the current - // protocol. - debug!(target: "sub-libp2p", - "Tried to send message to peer {} for which we aren't connected with the requested protocol", - who - ); - return Err(ErrorKind::PeerNotFound.into()) - } - } else { - debug!(target: "sub-libp2p", "Tried to send message to invalid peer ID {}", who); - return Err(ErrorKind::PeerNotFound.into()) - } - } - - /// Get the info on a peer, if there's an active connection. - pub fn peer_info(&self, who: NodeIndex) -> Option { - self.connections.read().info_by_peer.get(&who).map(Into::into) - } - - /// Reports that an attempt to make a low-level ping of the peer failed. - pub fn report_ping_failed(&self, who: NodeIndex) { - self.drop_peer(who); - } - - /// Disconnects a peer, if a connection exists (ie. drops the Kademlia - /// controller, and the senders that were stored in the `UniqueConnec` of - /// `custom_proto`). - pub fn drop_peer(&self, who: NodeIndex) { - let mut connections = self.connections.write(); - if let Some(peer_info) = connections.info_by_peer.remove(&who) { - trace!(target: "sub-libp2p", "Destroying peer #{} {:?} ; kademlia = {:?} ; num_protos = {:?}", - who, - peer_info.id, - peer_info.kad_connec.is_alive(), - peer_info.protocols.iter().filter(|c| c.1.is_alive()).count()); - let old = connections.peer_by_nodeid.remove(&peer_info.id); - debug_assert_eq!(old, Some(who)); - for addr in &peer_info.remote_addresses { - self.topology.write().report_disconnected(addr, - DisconnectReason::ClosedGracefully); // TODO: wrong reason - } - } - } - - /// Disconnects all the peers. - /// This destroys all the Kademlia controllers and the senders that were - /// stored in the `UniqueConnec` of `custom_proto`. - pub fn disconnect_all(&self) { - let mut connec = self.connections.write(); - *connec = Connections { - info_by_peer: FnvHashMap::with_capacity_and_hasher( - connec.peer_by_nodeid.capacity(), Default::default()), - peer_by_nodeid: FnvHashMap::with_capacity_and_hasher( - connec.peer_by_nodeid.capacity(), Default::default()), - }; - } - - /// Disables a peer for `PEER_DISABLE_DURATION`. This adds the peer to the - /// list of disabled peers, and drops any existing connections if - /// necessary (ie. drops the sender that was stored in the `UniqueConnec` - /// of `custom_proto`). - pub fn ban_peer(&self, who: NodeIndex, reason: &str) { - // TODO: what do we do if the peer is reserved? - // TODO: same logging as in drop_peer - let mut connections = self.connections.write(); - let peer_info = if let Some(peer_info) = connections.info_by_peer.remove(&who) { - if let &Some(ref client_version) = &peer_info.client_version { - info!(target: "network", "Peer {} (version: {}, addresses: {:?}) disabled. {}", who, client_version, peer_info.remote_addresses, reason); - } else { - info!(target: "network", "Peer {} (addresses: {:?}) disabled. {}", who, peer_info.remote_addresses, reason); - } - let old = connections.peer_by_nodeid.remove(&peer_info.id); - debug_assert_eq!(old, Some(who)); - peer_info - } else { - return - }; - - drop(connections); - let timeout = Instant::now() + PEER_DISABLE_DURATION; - self.disabled_nodes.lock().insert(peer_info.id.clone(), timeout); - } - - /// Flushes the caches to the disk. - /// - /// This is done in an atomical way, so that an error doesn't corrupt - /// anything. - pub fn flush_caches_to_disk(&self) -> Result<(), IoError> { - match self.topology.read().flush_to_disk() { - Ok(()) => { - debug!(target: "sub-libp2p", "Flushed JSON peer store to disk"); - Ok(()) - } - Err(err) => { - warn!(target: "sub-libp2p", "Failed to flush changes to JSON peer store: {}", err); - Err(err) - } - } - } -} - -impl Drop for NetworkState { - fn drop(&mut self) { - let _ = self.flush_caches_to_disk(); - } -} - -/// Periodic update that should be performed by the user of the network state. -pub struct PeriodicUpdate { - /// Index of the node in the network state. - pub node_index: NodeIndex, - /// Id of the peer. - pub peer_id: PeerId, - /// Address of the node to ping. - pub address: Multiaddr, - /// Object that allows pinging the node. - pub pinger: UniqueConnec, - /// The node should be identified as well. - pub identify: bool, -} - -struct OpenCustomConnectionsNumbers { - /// Total number of open and pending connections. - pub total: u32, - /// Unreserved incoming number of open and pending connections. - pub unreserved_incoming: u32, - /// Unreserved outgoing number of open and pending connections. - pub unreserved_outgoing: u32, -} - -/// Returns the number of open and pending connections with -/// custom protocols. -fn num_open_custom_connections(connections: &Connections, reserved_peers: &FnvHashSet) -> OpenCustomConnectionsNumbers { - let filtered = connections - .info_by_peer - .values() - .filter(|info| - info.protocols.iter().any(|&(_, ref connec)| - match connec.state() { - UniqueConnecState::Pending | UniqueConnecState::Full => true, - _ => false - } - ) - ); - - let mut total: u32 = 0; - let mut unreserved_incoming: u32 = 0; - let mut unreserved_outgoing: u32 = 0; - - for info in filtered { - total += 1; - let node_is_reserved = reserved_peers.contains(&info.id); - if !node_is_reserved { - if !info.originated.unwrap_or(true) { - unreserved_incoming += 1; - } else { - unreserved_outgoing += 1; - } - } - } - - OpenCustomConnectionsNumbers { - total, - unreserved_incoming, - unreserved_outgoing, - } -} - -/// Parses an address of the form `/ip4/x.x.x.x/tcp/x/p2p/xxxxxx`, and adds it -/// to the given topology. Returns the corresponding peer ID and multiaddr. -fn parse_and_add_to_topology( - addr_str: &str, - topology: &mut NetTopology -) -> Result<(PeerId, Multiaddr), Error> { - - let mut addr = addr_str.to_multiaddr().map_err(|_| ErrorKind::AddressParse)?; - let who = match addr.pop() { - Some(AddrComponent::P2P(key)) => - PeerId::from_multihash(key).map_err(|_| ErrorKind::AddressParse)?, - _ => return Err(ErrorKind::AddressParse.into()), - }; - - topology.add_bootstrap_addr(&who, addr.clone()); - Ok((who, addr)) -} - -/// Obtains or generates the local private key using the configuration. -fn obtain_private_key(config: &NetworkConfiguration) - -> Result { - if let Some(ref secret) = config.use_secret { - // Key was specified in the configuration. - secio::SecioKeyPair::secp256k1_raw_key(&secret[..]) - .map_err(|err| IoError::new(IoErrorKind::InvalidData, err)) - - } else { - if let Some(ref path) = config.net_config_path { - fs::create_dir_all(Path::new(path))?; - - // Try fetch the key from a the file containing th esecret. - let secret_path = Path::new(path).join(SECRET_FILE); - match load_private_key_from_file(&secret_path) { - Ok(s) => Ok(s), - Err(err) => { - // Failed to fetch existing file ; generate a new key - trace!(target: "sub-libp2p", - "Failed to load existing secret key file {:?}, generating new key ; err = {:?}", - secret_path, - err - ); - Ok(gen_key_and_try_write_to_file(&secret_path)) - } - } - - } else { - // No path in the configuration, nothing we can do except generate - // a new key. - let mut key: [u8; 32] = [0; 32]; - rand::rngs::EntropyRng::new().fill(&mut key); - Ok(secio::SecioKeyPair::secp256k1_raw_key(&key) - .expect("randomly-generated key with correct len should always be valid")) - } - } -} - -/// Tries to load a private key from a file located at the given path. -fn load_private_key_from_file