diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..479bca0f81 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +root = true + +[*] +end_of_line = lf +charset = utf-8 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.rs] +indent_size = 4 + +[*.toml] +indent_size = 2 + +[*.{yml,yaml}] +indent_size = 4 diff --git a/.gitignore b/.gitignore index 3af7788708..a1f6014f9b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,16 @@ + +### Rust ### +# Generated by Cargo +# will have compiled files and executables +/target/ */target/* */*/target/* +# These are backup files generated by rustfmt +**/*.rs.bt + +### Editors ### .idea -*.vscode +/target/* + +*/launch.json diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 49189f3f1d..59ac997ed7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,15 +4,18 @@ image: "rust:latest" # Install a C compiler, cmake and git into the container. before_script: - - apt-get -y update - - apt-get install -y build-essential cmake pkg-config libssl-dev clang libclang-dev llvm - - rustc --version && cargo --version # Print version info for debugging + - apt-get -y update + - apt-get install -y build-essential cmake pkg-config libssl-dev clang libclang-dev llvm + - rustup toolchain install nightly-2020-03-01 # rustc version 1.43.0-nightly + - rustup default nightly-2020-03-01 + - rustup component add rustfmt + - rustc --version && rustfmt --version && cargo --version # Print version info for debugging # Declare stages stages: - - build # for crates and pallets - - test # for tests in dev - - deploy # for deployment in master + - build # for crates and pallets + - test # for tests in dev + - deploy # for deployment in master # # master branch # btc-parachain-test: @@ -58,14 +61,14 @@ stages: # pallets and crates test-pallets-and-crates: - stage: test - image: rustlang/rust:nightly - script: - - cargo build --release --verbose - - cargo test --all --release - cache: - key: build-cache - paths: - - target - only: - - merge_requests + stage: test + script: + - cargo build --release --verbose + - cargo fmt -- --check + - cargo test --all --release + cache: + key: build-cache + paths: + - target + only: + - merge_requests diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000000..0f406d2f0b --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,18 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug", + "program": "${workspaceRoot}/target/debug/${workspaceRootFolderName}", + "args": [], + "cwd": "${workspaceRoot}", + "sourceLanguages": ["rust"] + } + + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..e153e3a12c --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "editor.formatOnSave": true, + "[rust]": { + "editor.defaultFormatter": "rust-lang.rust" + } +} diff --git a/Cargo.lock b/Cargo.lock index 7017dc9339..1a98339cd6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,6 +28,26 @@ dependencies = [ "memchr", ] +[[package]] +name = "alga" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f823d037a7ec6ea2197046bafd4ae150e6bc36f9ca347404f46a46823fa84f2" +dependencies = [ + "approx", + "num-complex", + "num-traits", +] + +[[package]] +name = "approx" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3" +dependencies = [ + "num-traits", +] + [[package]] name = "arrayref" version = "0.3.6" @@ -99,10 +119,10 @@ dependencies = [ "mocktopus", "node-primitives", "parity-scale-codec", - "primitive-types", + "primitive-types 0.6.2", "sha2", - "sp-core", - "sp-std", + "sp-core 2.0.0", + "sp-std 2.0.0", "twox-hash", ] @@ -163,8 +183,8 @@ dependencies = [ name = "btc-core" version = "0.1.0" dependencies = [ - "frame-support", - "sp-runtime", + "frame-support 2.0.0", + "sp-runtime 2.0.0", ] [[package]] @@ -173,22 +193,28 @@ version = "0.0.1" dependencies = [ "bitcoin", "btc-core", - "frame-support", - "frame-system", + "frame-support 2.0.0", + "frame-system 2.0.0", "hex", "indexmap", "mocktopus", "node-primitives", - "pallet-timestamp", + "pallet-timestamp 2.0.0", "parity-scale-codec", "security", "serde", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", + "sp-core 2.0.0", + "sp-io 2.0.0", + "sp-runtime 2.0.0", + "sp-std 2.0.0", ] +[[package]] +name = "bumpalo" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ae9db68ad7fac5fe51304d20f016c911539251075a214f8e663babefa35187" + [[package]] name = "byte-slice-cast" version = "0.3.5" @@ -237,6 +263,21 @@ dependencies = [ "bitflags", ] +[[package]] +name = "collateral" +version = "0.1.0" +dependencies = [ + "frame-support 2.0.0-alpha.5", + "frame-system 2.0.0-alpha.5", + "pallet-balances", + "parity-scale-codec", + "safe-mix", + "sp-core 2.0.0-alpha.5", + "sp-io 2.0.0-alpha.5", + "sp-runtime 2.0.0-alpha.5", + "xclaim-core", +] + [[package]] name = "const-random" version = "0.1.8" @@ -353,14 +394,14 @@ checksum = "516aa8d7a71cb00a1c4146f0798549b93d083d4f189b3ced8f3de6b8f11ee6c4" name = "exchange-rate-oracle" version = "0.0.1" dependencies = [ - "frame-support", - "frame-system", + "frame-support 2.0.0-alpha.5", + "frame-system 2.0.0-alpha.5", "mocktopus", - "pallet-timestamp", + "pallet-timestamp 2.0.0-alpha.5", "parity-scale-codec", - "sp-core", - "sp-io", - "sp-runtime", + "sp-core 2.0.0-alpha.5", + "sp-io 2.0.0-alpha.5", + "sp-runtime 2.0.0-alpha.5", "xclaim-core", ] @@ -405,6 +446,35 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "fixed-hash" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32529fc42e86ec06e5047092082aab9ad459b070c5d2a76b14f4f5ce70bf2e84" +dependencies = [ + "byteorder", + "rand 0.7.3", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "frame-benchmarking" +version = "2.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712481345c6430a3a9a9369871d74e10c8afd64e5191b8d233f43aca5d9df955" +dependencies = [ + "frame-support 2.0.0-alpha.5", + "frame-system 2.0.0-alpha.5", + "linregress", + "parity-scale-codec", + "sp-api 2.0.0-alpha.5", + "sp-io 2.0.0-alpha.5", + "sp-runtime 2.0.0-alpha.5", + "sp-runtime-interface 2.0.0-alpha.5", + "sp-std 2.0.0-alpha.5", +] + [[package]] name = "frame-metadata" version = "2.0.0" @@ -412,8 +482,45 @@ source = "git+https://github.com/paritytech/substrate.git?rev=3e651110aa06aa8357 dependencies = [ "parity-scale-codec", "serde", - "sp-core", - "sp-std", + "sp-core 2.0.0", + "sp-std 2.0.0", +] + +[[package]] +name = "frame-metadata" +version = "11.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7083431304c527dea7559f6b262b186473f44d1c9819b7da74388d6487b2653f" +dependencies = [ + "parity-scale-codec", + "serde", + "sp-core 2.0.0-alpha.5", + "sp-std 2.0.0-alpha.5", +] + +[[package]] +name = "frame-support" +version = "2.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "390a1a495b50001f75a2f4fe0602734ce61890d7acbd6aa7ddce0b60d9145a82" +dependencies = [ + "bitmask", + "frame-metadata 11.0.0-alpha.5", + "frame-support-procedural 2.0.0-alpha.5", + "impl-trait-for-tuples", + "log", + "once_cell 1.3.1", + "parity-scale-codec", + "paste", + "serde", + "sp-arithmetic 2.0.0-alpha.5", + "sp-core 2.0.0-alpha.5", + "sp-inherents 2.0.0-alpha.5", + "sp-io 2.0.0-alpha.5", + "sp-runtime 2.0.0-alpha.5", + "sp-state-machine 0.8.0-alpha.5", + "sp-std 2.0.0-alpha.5", + "tracing", ] [[package]] @@ -422,30 +529,55 @@ version = "2.0.0" source = "git+https://github.com/paritytech/substrate.git?rev=3e651110aa06aa835790df63410a29676243fc54#3e651110aa06aa835790df63410a29676243fc54" dependencies = [ "bitmask", - "frame-metadata", - "frame-support-procedural", + "frame-metadata 2.0.0", + "frame-support-procedural 2.0.0", "impl-trait-for-tuples", "log", "once_cell 0.2.4", "parity-scale-codec", "paste", "serde", - "sp-arithmetic", - "sp-core", - "sp-inherents", - "sp-io", - "sp-runtime", - "sp-state-machine", - "sp-std", + "sp-arithmetic 2.0.0", + "sp-core 2.0.0", + "sp-inherents 2.0.0", + "sp-io 2.0.0", + "sp-runtime 2.0.0", + "sp-state-machine 2.0.0", + "sp-std 2.0.0", "tracing", ] +[[package]] +name = "frame-support-procedural" +version = "2.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79e509284ec2b06c24d28ba18fda0c21f6ad69545a3da5e9fcb7ba1817686ffa" +dependencies = [ + "frame-support-procedural-tools 2.0.0-alpha.5", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "frame-support-procedural" version = "2.0.0" source = "git+https://github.com/paritytech/substrate.git?rev=3e651110aa06aa835790df63410a29676243fc54#3e651110aa06aa835790df63410a29676243fc54" dependencies = [ - "frame-support-procedural-tools", + "frame-support-procedural-tools 2.0.0", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "frame-support-procedural-tools" +version = "2.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bf920148acb725b9ac1a6238b57a010ba660ecae7cd125f4b146c4a7d43192d" +dependencies = [ + "frame-support-procedural-tools-derive 2.0.0-alpha.5", + "proc-macro-crate", "proc-macro2", "quote", "syn", @@ -456,13 +588,24 @@ name = "frame-support-procedural-tools" version = "2.0.0" source = "git+https://github.com/paritytech/substrate.git?rev=3e651110aa06aa835790df63410a29676243fc54#3e651110aa06aa835790df63410a29676243fc54" dependencies = [ - "frame-support-procedural-tools-derive", + "frame-support-procedural-tools-derive 2.0.0", "proc-macro-crate", "proc-macro2", "quote", "syn", ] +[[package]] +name = "frame-support-procedural-tools-derive" +version = "2.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00d277cb5e284309c165ac1cfd7e1077b0d66d4aa0a1043dd32329191b510c40" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "frame-support-procedural-tools-derive" version = "2.0.0" @@ -473,21 +616,38 @@ dependencies = [ "syn", ] +[[package]] +name = "frame-system" +version = "2.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e525321245fb95648e49a22ab1e8a53abd86dcc71e0f9474a7945afe2d7f50" +dependencies = [ + "frame-support 2.0.0-alpha.5", + "impl-trait-for-tuples", + "parity-scale-codec", + "serde", + "sp-core 2.0.0-alpha.5", + "sp-io 2.0.0-alpha.5", + "sp-runtime 2.0.0-alpha.5", + "sp-std 2.0.0-alpha.5", + "sp-version 2.0.0-alpha.5", +] + [[package]] name = "frame-system" version = "2.0.0" source = "git+https://github.com/paritytech/substrate.git?rev=3e651110aa06aa835790df63410a29676243fc54#3e651110aa06aa835790df63410a29676243fc54" dependencies = [ - "frame-support", + "frame-support 2.0.0", "impl-trait-for-tuples", "parity-scale-codec", "safe-mix", "serde", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", - "sp-version", + "sp-core 2.0.0", + "sp-io 2.0.0", + "sp-runtime 2.0.0", + "sp-std 2.0.0", + "sp-version 2.0.0", ] [[package]] @@ -496,6 +656,98 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +[[package]] +name = "futures" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c329ae8753502fb44ae4fc2b622fa2a94652c41e795143765ba0927f92ab780" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c77d04ce8edd9cb903932b608268b3fffec4163dc053b3b402bf47eac1f1a8" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a" + +[[package]] +name = "futures-executor" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f674f3e1bcb15b37284a90cedf55afdba482ab061c407a9c0ebbd0f3109741ba" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", + "num_cpus", +] + +[[package]] +name = "futures-io" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6" + +[[package]] +name = "futures-macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a5081aa3de1f7542a794a397cde100ed903b0630152d0973479018fd85423a7" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3466821b4bc114d95b087b850a724c6f83115e929bc88f1fa98a3304a944c8a6" + +[[package]] +name = "futures-task" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27" + +[[package]] +name = "futures-util" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", +] + [[package]] name = "generic-array" version = "0.12.3" @@ -551,6 +803,15 @@ dependencies = [ "autocfg 0.1.7", ] +[[package]] +name = "hermit-abi" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "725cf19794cf90aa94e65050cb4191ff5d8fa87a498383774c47b332e3af952e" +dependencies = [ + "libc", +] + [[package]] name = "hex" version = "0.4.2" @@ -631,6 +892,15 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f65877bf7d44897a473350b1046277941cee20b263397e90869c50b6e766088b" +[[package]] +name = "js-sys" +version = "0.3.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a27d435371a2fa5b6d2b028a74bbdb1234f308da363226a2854ca3ff8ba7055" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "keccak" version = "0.1.0" @@ -649,6 +919,12 @@ version = "0.2.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" +[[package]] +name = "libm" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a" + [[package]] name = "libsecp256k1" version = "0.3.5" @@ -665,6 +941,17 @@ dependencies = [ "typenum", ] +[[package]] +name = "linregress" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9290cf6f928576eeb9c096c6fad9d8d452a0a1a70a2bbffa6e36064eedc0aac9" +dependencies = [ + "failure", + "nalgebra", + "statrs", +] + [[package]] name = "lock_api" version = "0.1.5" @@ -703,6 +990,15 @@ dependencies = [ "synstructure", ] +[[package]] +name = "matrixmultiply" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4f7ec66360130972f34830bfad9ef05c6610a43938a467bcc9ab9369ab3478f" +dependencies = [ + "rawpointer", +] + [[package]] name = "maybe-uninit" version = "2.0.0" @@ -724,7 +1020,19 @@ dependencies = [ "ahash", "hash-db", "hashbrown 0.6.3", - "parity-util-mem", + "parity-util-mem 0.3.0", +] + +[[package]] +name = "memory-db" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f58381b20ebe2c578e75dececd9da411414903415349548ccc46aac3209cdfbc" +dependencies = [ + "ahash", + "hash-db", + "hashbrown 0.6.3", + "parity-util-mem 0.6.0", ] [[package]] @@ -745,6 +1053,18 @@ dependencies = [ "zeroize 1.1.0", ] +[[package]] +name = "merlin" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6feca46f4fa3443a01769d768727f10c10a20fdb65e52dc16a81f0c8269bb78" +dependencies = [ + "byteorder", + "keccak", + "rand_core 0.5.1", + "zeroize 1.1.0", +] + [[package]] name = "mocktopus" version = "0.7.5" @@ -765,13 +1085,30 @@ dependencies = [ "syn", ] +[[package]] +name = "nalgebra" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaa9fddbc34c8c35dd2108515587b8ce0cab396f17977b8c738568e4edb521a2" +dependencies = [ + "alga", + "approx", + "generic-array", + "matrixmultiply", + "num-complex", + "num-rational", + "num-traits", + "rand 0.6.5", + "typenum", +] + [[package]] name = "node-primitives" version = "2.0.0" source = "git+https://github.com/paritytech/substrate.git?rev=3e651110aa06aa835790df63410a29676243fc54#3e651110aa06aa835790df63410a29676243fc54" dependencies = [ - "sp-core", - "sp-runtime", + "sp-core 2.0.0", + "sp-runtime 2.0.0", ] [[package]] @@ -791,6 +1128,16 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-complex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" +dependencies = [ + "autocfg 1.0.0", + "num-traits", +] + [[package]] name = "num-integer" version = "0.1.42" @@ -820,6 +1167,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" dependencies = [ "autocfg 1.0.0", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6" +dependencies = [ + "hermit-abi", + "libc", ] [[package]] @@ -837,26 +1195,69 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d584f08c2d717d5c23a6414fc2822b71c651560713e54fa7eace675f758a355e" +[[package]] +name = "once_cell" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" +dependencies = [ + "parking_lot 0.9.0", +] + [[package]] name = "opaque-debug" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +[[package]] +name = "pallet-balances" +version = "2.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4190f97ea53bebe7a5ae2c089a43e72561d9166968d28d0911eed2cea878d5dd" +dependencies = [ + "frame-benchmarking", + "frame-support 2.0.0-alpha.5", + "frame-system 2.0.0-alpha.5", + "parity-scale-codec", + "serde", + "sp-io 2.0.0-alpha.5", + "sp-runtime 2.0.0-alpha.5", + "sp-std 2.0.0-alpha.5", +] + +[[package]] +name = "pallet-timestamp" +version = "2.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec68fe57dc24af2869f5447f406de1de4dc105b11fbab59ae94f580cd2787aa" +dependencies = [ + "frame-benchmarking", + "frame-support 2.0.0-alpha.5", + "frame-system 2.0.0-alpha.5", + "impl-trait-for-tuples", + "parity-scale-codec", + "serde", + "sp-inherents 2.0.0-alpha.5", + "sp-runtime 2.0.0-alpha.5", + "sp-std 2.0.0-alpha.5", + "sp-timestamp 2.0.0-alpha.5", +] + [[package]] name = "pallet-timestamp" version = "2.0.0" source = "git+https://github.com/paritytech/substrate.git?rev=3e651110aa06aa835790df63410a29676243fc54#3e651110aa06aa835790df63410a29676243fc54" dependencies = [ - "frame-support", - "frame-system", + "frame-support 2.0.0", + "frame-system 2.0.0", "impl-trait-for-tuples", "parity-scale-codec", "serde", - "sp-inherents", - "sp-runtime", - "sp-std", - "sp-timestamp", + "sp-inherents 2.0.0", + "sp-runtime 2.0.0", + "sp-std 2.0.0", + "sp-timestamp 2.0.0", ] [[package]] @@ -895,6 +1296,31 @@ dependencies = [ "winapi", ] +[[package]] +name = "parity-util-mem" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e42755f26e5ea21a6a819d9e63cbd70713e9867a2b767ec2cc65ca7659532c5" +dependencies = [ + "cfg-if", + "impl-trait-for-tuples", + "parity-util-mem-derive", + "parking_lot 0.10.0", + "primitive-types 0.7.0", + "winapi", +] + +[[package]] +name = "parity-util-mem-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" +dependencies = [ + "proc-macro2", + "syn", + "synstructure", +] + [[package]] name = "parity-wasm" version = "0.41.0" @@ -923,7 +1349,17 @@ dependencies = [ ] [[package]] -name = "parking_lot_core" +name = "parking_lot" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc" +dependencies = [ + "lock_api 0.3.3", + "parking_lot_core 0.7.0", +] + +[[package]] +name = "parking_lot_core" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" @@ -950,6 +1386,20 @@ dependencies = [ "winapi", ] +[[package]] +name = "parking_lot_core" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1" +dependencies = [ + "cfg-if", + "cloudabi", + "libc", + "redox_syscall", + "smallvec 1.2.0", + "winapi", +] + [[package]] name = "paste" version = "0.1.9" @@ -982,6 +1432,12 @@ dependencies = [ "crypto-mac", ] +[[package]] +name = "pin-utils" +version = "0.1.0-alpha.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" + [[package]] name = "ppv-lite86" version = "0.2.6" @@ -994,7 +1450,19 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4336f4f5d5524fa60bcbd6fe626f9223d8142a50e7053e979acdf0da41ab975" dependencies = [ - "fixed-hash", + "fixed-hash 0.5.2", + "impl-codec", + "impl-serde 0.3.0", + "uint", +] + +[[package]] +name = "primitive-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5e4b9943a2da369aec5e96f7c10ebc74fcf434d39590d974b0a3460e6f67fbb" +dependencies = [ + "fixed-hash 0.6.0", "impl-codec", "impl-serde 0.3.0", "uint", @@ -1015,6 +1483,12 @@ version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63" +[[package]] +name = "proc-macro-nested" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e946095f9d3ed29ec38de908c22f95d9ac008e424c7bcae54c75a79c527c694" + [[package]] name = "proc-macro2" version = "1.0.10" @@ -1039,6 +1513,19 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "def50a86306165861203e7f84ecffbbdfdea79f0e51039b33de1e952358c47ac" +[[package]] +name = "rand" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" +dependencies = [ + "cloudabi", + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "winapi", +] + [[package]] name = "rand" version = "0.6.5" @@ -1186,6 +1673,12 @@ dependencies = [ "rand_core 0.3.1", ] +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + [[package]] name = "rdrand" version = "0.4.0" @@ -1225,6 +1718,12 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc-hex" version = "2.1.0" @@ -1257,7 +1756,7 @@ checksum = "eacd8381b3c37840c9c9f40472af529e49975bdcbc24f83c31059fd6539023d3" dependencies = [ "curve25519-dalek 1.2.3", "failure", - "merlin", + "merlin 1.3.0", "rand 0.6.5", "rand_core 0.4.2", "rand_os", @@ -1266,6 +1765,24 @@ dependencies = [ "zeroize 0.9.3", ] +[[package]] +name = "schnorrkel" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "021b403afe70d81eea68f6ea12f6b3c9588e5d536a94c3bf80f15e7faa267862" +dependencies = [ + "arrayref", + "arrayvec 0.5.1", + "curve25519-dalek 2.0.0", + "getrandom", + "merlin 2.0.0", + "rand 0.7.3", + "rand_core 0.5.1", + "sha2", + "subtle 2.2.2", + "zeroize 1.1.0", +] + [[package]] name = "scopeguard" version = "0.3.3" @@ -1283,16 +1800,16 @@ name = "security" version = "0.1.0" dependencies = [ "bitcoin", - "frame-support", - "frame-system", + "frame-support 2.0.0", + "frame-system 2.0.0", "node-primitives", - "pallet-timestamp", + "pallet-timestamp 2.0.0", "parity-scale-codec", "serde", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", + "sp-core 2.0.0", + "sp-io 2.0.0", + "sp-runtime 2.0.0", + "sp-std 2.0.0", ] [[package]] @@ -1310,6 +1827,12 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +[[package]] +name = "send_wrapper" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0eddf2e8f50ced781f288c19f18621fa72a3779e3cb58dbf23b07469b0abeb4" + [[package]] name = "serde" version = "1.0.105" @@ -1342,6 +1865,12 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + [[package]] name = "smallvec" version = "0.6.13" @@ -1357,18 +1886,47 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc" +[[package]] +name = "sp-api" +version = "2.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4312c5081652e14f9c87fe6f8c765bbe63ac0ae69daeba48c2e5f7e7b7b25b" +dependencies = [ + "hash-db", + "parity-scale-codec", + "sp-api-proc-macro 2.0.0-alpha.5", + "sp-core 2.0.0-alpha.5", + "sp-runtime 2.0.0-alpha.5", + "sp-state-machine 0.8.0-alpha.5", + "sp-std 2.0.0-alpha.5", + "sp-version 2.0.0-alpha.5", +] + [[package]] name = "sp-api" version = "2.0.0" source = "git+https://github.com/paritytech/substrate.git?rev=3e651110aa06aa835790df63410a29676243fc54#3e651110aa06aa835790df63410a29676243fc54" dependencies = [ "parity-scale-codec", - "sp-api-proc-macro", - "sp-core", - "sp-runtime", - "sp-state-machine", - "sp-std", - "sp-version", + "sp-api-proc-macro 2.0.0", + "sp-core 2.0.0", + "sp-runtime 2.0.0", + "sp-state-machine 2.0.0", + "sp-std 2.0.0", + "sp-version 2.0.0", +] + +[[package]] +name = "sp-api-proc-macro" +version = "2.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73a94ac1f228f99fb5279d7983f6bcde83bf1fe5824f9a56e5f7a81cc8a7069c" +dependencies = [ + "blake2-rfc", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -1383,6 +1941,19 @@ dependencies = [ "syn", ] +[[package]] +name = "sp-application-crypto" +version = "2.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed68f0c283a01b5a8c768965b160fb4aa0a637a7e1b5e0d1168cba2f2310b348" +dependencies = [ + "parity-scale-codec", + "serde", + "sp-core 2.0.0-alpha.5", + "sp-io 2.0.0-alpha.5", + "sp-std 2.0.0-alpha.5", +] + [[package]] name = "sp-application-crypto" version = "2.0.0" @@ -1390,9 +1961,23 @@ source = "git+https://github.com/paritytech/substrate.git?rev=3e651110aa06aa8357 dependencies = [ "parity-scale-codec", "serde", - "sp-core", - "sp-io", - "sp-std", + "sp-core 2.0.0", + "sp-io 2.0.0", + "sp-std 2.0.0", +] + +[[package]] +name = "sp-arithmetic" +version = "2.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d1e32dfcd4c87069d6e48d7d4a873221a8c27161a0ef5ad6ff683b591e6f97" +dependencies = [ + "integer-sqrt", + "num-traits", + "parity-scale-codec", + "serde", + "sp-debug-derive 2.0.0-alpha.5", + "sp-std 2.0.0-alpha.5", ] [[package]] @@ -1404,8 +1989,50 @@ dependencies = [ "num-traits", "parity-scale-codec", "serde", - "sp-debug-derive", - "sp-std", + "sp-debug-derive 2.0.0", + "sp-std 2.0.0", +] + +[[package]] +name = "sp-core" +version = "2.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7a2938aefa827fbf6dbf5c3e8eea5cc6dedafe97a28bbfb733258ee99c82c67" +dependencies = [ + "base58", + "blake2-rfc", + "byteorder", + "ed25519-dalek", + "futures", + "hash-db", + "hash256-std-hasher", + "hex", + "impl-serde 0.3.0", + "lazy_static", + "libsecp256k1", + "log", + "num-traits", + "parity-scale-codec", + "parity-util-mem 0.6.0", + "parking_lot 0.10.0", + "primitive-types 0.7.0", + "rand 0.7.3", + "regex", + "rustc-hex", + "schnorrkel 0.9.1", + "serde", + "sha2", + "sp-debug-derive 2.0.0-alpha.5", + "sp-externalities 0.8.0-alpha.5", + "sp-runtime-interface 2.0.0-alpha.5", + "sp-std 2.0.0-alpha.5", + "sp-storage 2.0.0-alpha.5", + "substrate-bip39 0.4.1", + "tiny-bip39 0.7.3", + "tiny-keccak", + "twox-hash", + "wasmi", + "zeroize 1.1.0", ] [[package]] @@ -1427,26 +2054,37 @@ dependencies = [ "num-traits", "parity-scale-codec", "parking_lot 0.9.0", - "primitive-types", + "primitive-types 0.6.2", "rand 0.7.3", "regex", "rustc-hex", - "schnorrkel", + "schnorrkel 0.8.5", "serde", "sha2", - "sp-debug-derive", - "sp-externalities", - "sp-runtime-interface", - "sp-std", - "sp-storage", - "substrate-bip39", - "tiny-bip39", + "sp-debug-derive 2.0.0", + "sp-externalities 2.0.0", + "sp-runtime-interface 2.0.0", + "sp-std 2.0.0", + "sp-storage 2.0.0", + "substrate-bip39 0.3.1", + "tiny-bip39 0.6.2", "tiny-keccak", "twox-hash", "wasmi", "zeroize 1.1.0", ] +[[package]] +name = "sp-debug-derive" +version = "2.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "124f3ffa2d897139864715cc624811ca997ca92be8880229143c6dee801b5ab2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "sp-debug-derive" version = "2.0.0" @@ -1457,14 +2095,38 @@ dependencies = [ "syn", ] +[[package]] +name = "sp-externalities" +version = "0.8.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b3aa6665b2a1a9df50a5514597e96a7d08d69e0e7ff90d238380fb266e14622" +dependencies = [ + "environmental", + "sp-std 2.0.0-alpha.5", + "sp-storage 2.0.0-alpha.5", +] + [[package]] name = "sp-externalities" version = "2.0.0" source = "git+https://github.com/paritytech/substrate.git?rev=3e651110aa06aa835790df63410a29676243fc54#3e651110aa06aa835790df63410a29676243fc54" dependencies = [ "environmental", - "sp-std", - "sp-storage", + "sp-std 2.0.0", + "sp-storage 2.0.0", +] + +[[package]] +name = "sp-inherents" +version = "2.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d5ea2312969d929d617f852f9728644b98591b503a641106ee444acea4394fc" +dependencies = [ + "derive_more", + "parity-scale-codec", + "parking_lot 0.10.0", + "sp-core 2.0.0-alpha.5", + "sp-std 2.0.0-alpha.5", ] [[package]] @@ -1475,8 +2137,27 @@ dependencies = [ "derive_more", "parity-scale-codec", "parking_lot 0.9.0", - "sp-core", - "sp-std", + "sp-core 2.0.0", + "sp-std 2.0.0", +] + +[[package]] +name = "sp-io" +version = "2.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ed12cd1b0eddf5250703fff43da9e76be6cce32c492b2e2a71b4f6d9a12f49" +dependencies = [ + "hash-db", + "libsecp256k1", + "log", + "parity-scale-codec", + "sp-core 2.0.0-alpha.5", + "sp-externalities 0.8.0-alpha.5", + "sp-runtime-interface 2.0.0-alpha.5", + "sp-state-machine 0.8.0-alpha.5", + "sp-std 2.0.0-alpha.5", + "sp-trie 2.0.0-alpha.5", + "sp-wasm-interface 2.0.0-alpha.5", ] [[package]] @@ -1488,12 +2169,22 @@ dependencies = [ "libsecp256k1", "log", "parity-scale-codec", - "sp-core", - "sp-externalities", - "sp-runtime-interface", - "sp-state-machine", - "sp-std", - "sp-trie", + "sp-core 2.0.0", + "sp-externalities 2.0.0", + "sp-runtime-interface 2.0.0", + "sp-state-machine 2.0.0", + "sp-std 2.0.0", + "sp-trie 2.0.0", +] + +[[package]] +name = "sp-panic-handler" +version = "2.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643ae9f2dc119551342422394cdfa8bfe0a230d1adc9891fcb08cf15fdf0f703" +dependencies = [ + "backtrace", + "log", ] [[package]] @@ -1505,6 +2196,28 @@ dependencies = [ "log", ] +[[package]] +name = "sp-runtime" +version = "2.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2eb5d07f847c7420070d9dc28d810d405dc6316cd58f58548a41f0aeeca84e8" +dependencies = [ + "hash256-std-hasher", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "parity-util-mem 0.6.0", + "paste", + "rand 0.7.3", + "serde", + "sp-application-crypto 2.0.0-alpha.5", + "sp-arithmetic 2.0.0-alpha.5", + "sp-core 2.0.0-alpha.5", + "sp-inherents 2.0.0-alpha.5", + "sp-io 2.0.0-alpha.5", + "sp-std 2.0.0-alpha.5", +] + [[package]] name = "sp-runtime" version = "2.0.0" @@ -1516,12 +2229,27 @@ dependencies = [ "paste", "rand 0.7.3", "serde", - "sp-application-crypto", - "sp-arithmetic", - "sp-core", - "sp-inherents", - "sp-io", - "sp-std", + "sp-application-crypto 2.0.0", + "sp-arithmetic 2.0.0", + "sp-core 2.0.0", + "sp-inherents 2.0.0", + "sp-io 2.0.0", + "sp-std 2.0.0", +] + +[[package]] +name = "sp-runtime-interface" +version = "2.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d25229ba01740b13368f743e866d8ee155826932c9f2c4390e4ceb7730df214" +dependencies = [ + "parity-scale-codec", + "primitive-types 0.7.0", + "sp-externalities 0.8.0-alpha.5", + "sp-runtime-interface-proc-macro 2.0.0-alpha.5", + "sp-std 2.0.0-alpha.5", + "sp-wasm-interface 2.0.0-alpha.5", + "static_assertions", ] [[package]] @@ -1531,14 +2259,27 @@ source = "git+https://github.com/paritytech/substrate.git?rev=3e651110aa06aa8357 dependencies = [ "environmental", "parity-scale-codec", - "primitive-types", - "sp-externalities", - "sp-runtime-interface-proc-macro", - "sp-std", - "sp-wasm-interface", + "primitive-types 0.6.2", + "sp-externalities 2.0.0", + "sp-runtime-interface-proc-macro 2.0.0", + "sp-std 2.0.0", + "sp-wasm-interface 2.0.0", "static_assertions", ] +[[package]] +name = "sp-runtime-interface-proc-macro" +version = "2.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f1d9e32fa5db2f3dd89affbd3c97959432272c9e52e4e2610dad32dd4705784" +dependencies = [ + "Inflector", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "sp-runtime-interface-proc-macro" version = "2.0.0" @@ -1551,6 +2292,26 @@ dependencies = [ "syn", ] +[[package]] +name = "sp-state-machine" +version = "0.8.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e52a17e293be8e7a518ac6aebfc7fdafbe531b6c808a83a754e4a8730f139521" +dependencies = [ + "hash-db", + "log", + "num-traits", + "parity-scale-codec", + "parking_lot 0.10.0", + "rand 0.7.3", + "sp-core 2.0.0-alpha.5", + "sp-externalities 0.8.0-alpha.5", + "sp-panic-handler 2.0.0-alpha.5", + "sp-trie 2.0.0-alpha.5", + "trie-db 0.20.0", + "trie-root 0.16.0", +] + [[package]] name = "sp-state-machine" version = "2.0.0" @@ -1562,19 +2323,37 @@ dependencies = [ "parity-scale-codec", "parking_lot 0.9.0", "rand 0.7.3", - "sp-core", - "sp-externalities", - "sp-panic-handler", - "sp-trie", - "trie-db", - "trie-root", + "sp-core 2.0.0", + "sp-externalities 2.0.0", + "sp-panic-handler 2.0.0", + "sp-trie 2.0.0", + "trie-db 0.18.1", + "trie-root 0.15.2", ] +[[package]] +name = "sp-std" +version = "2.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "752e038359704226bb5737531fb91df2e4ab33214e0be6eb6f32ad688a71b411" + [[package]] name = "sp-std" version = "2.0.0" source = "git+https://github.com/paritytech/substrate.git?rev=3e651110aa06aa835790df63410a29676243fc54#3e651110aa06aa835790df63410a29676243fc54" +[[package]] +name = "sp-storage" +version = "2.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c389a75ddd45ba771b20c4caed1011d23004069e436b73eca058d2f894585069" +dependencies = [ + "impl-serde 0.2.3", + "serde", + "sp-debug-derive 2.0.0-alpha.5", + "sp-std 2.0.0-alpha.5", +] + [[package]] name = "sp-storage" version = "2.0.0" @@ -1582,8 +2361,23 @@ source = "git+https://github.com/paritytech/substrate.git?rev=3e651110aa06aa8357 dependencies = [ "impl-serde 0.2.3", "serde", - "sp-debug-derive", - "sp-std", + "sp-debug-derive 2.0.0", + "sp-std 2.0.0", +] + +[[package]] +name = "sp-timestamp" +version = "2.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990bc28e4e7fd31a13f5e61fd30ef0aa3ba5747b64ab3560df5285abbd879f6c" +dependencies = [ + "impl-trait-for-tuples", + "parity-scale-codec", + "sp-api 2.0.0-alpha.5", + "sp-inherents 2.0.0-alpha.5", + "sp-runtime 2.0.0-alpha.5", + "sp-std 2.0.0-alpha.5", + "wasm-timer", ] [[package]] @@ -1593,10 +2387,25 @@ source = "git+https://github.com/paritytech/substrate.git?rev=3e651110aa06aa8357 dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", - "sp-api", - "sp-inherents", - "sp-runtime", - "sp-std", + "sp-api 2.0.0", + "sp-inherents 2.0.0", + "sp-runtime 2.0.0", + "sp-std 2.0.0", +] + +[[package]] +name = "sp-trie" +version = "2.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f417a60d0d1222f92733deef4dffc73b21180b0cf44ec335ac73fc871198721" +dependencies = [ + "hash-db", + "memory-db 0.20.0", + "parity-scale-codec", + "sp-core 2.0.0-alpha.5", + "sp-std 2.0.0-alpha.5", + "trie-db 0.20.0", + "trie-root 0.16.0", ] [[package]] @@ -1605,12 +2414,25 @@ version = "2.0.0" source = "git+https://github.com/paritytech/substrate.git?rev=3e651110aa06aa835790df63410a29676243fc54#3e651110aa06aa835790df63410a29676243fc54" dependencies = [ "hash-db", - "memory-db", + "memory-db 0.18.1", "parity-scale-codec", - "sp-core", - "sp-std", - "trie-db", - "trie-root", + "sp-core 2.0.0", + "sp-std 2.0.0", + "trie-db 0.18.1", + "trie-root 0.15.2", +] + +[[package]] +name = "sp-version" +version = "2.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f7ded62b3fd6346dc7e96dcfc55f8137653809e09cfa3f0da8052efd35c875" +dependencies = [ + "impl-serde 0.2.3", + "parity-scale-codec", + "serde", + "sp-runtime 2.0.0-alpha.5", + "sp-std 2.0.0-alpha.5", ] [[package]] @@ -1621,8 +2443,20 @@ dependencies = [ "impl-serde 0.2.3", "parity-scale-codec", "serde", - "sp-runtime", - "sp-std", + "sp-runtime 2.0.0", + "sp-std 2.0.0", +] + +[[package]] +name = "sp-wasm-interface" +version = "2.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab9dec817ad8e5eed83a3579da8638703d7559be5da5342312c8effee6a70aa" +dependencies = [ + "impl-trait-for-tuples", + "parity-scale-codec", + "sp-std 2.0.0-alpha.5", + "wasmi", ] [[package]] @@ -1640,6 +2474,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "statrs" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10102ac8d55e35db2b3fafc26f81ba8647da2e15879ab686a67e6d19af2685e8" +dependencies = [ + "rand 0.5.6", +] + [[package]] name = "substrate-bip39" version = "0.3.1" @@ -1648,7 +2491,19 @@ checksum = "3be511be555a3633e71739a79e4ddff6a6aaa6579fa6114182a51d72c3eb93c5" dependencies = [ "hmac", "pbkdf2", - "schnorrkel", + "schnorrkel 0.8.5", + "sha2", +] + +[[package]] +name = "substrate-bip39" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c004e8166d6e0aa3a9d5fa673e5b7098ff25f930de1013a21341988151e681bb" +dependencies = [ + "hmac", + "pbkdf2", + "schnorrkel 0.9.1", "sha2", ] @@ -1711,6 +2566,22 @@ dependencies = [ "sha2", ] +[[package]] +name = "tiny-bip39" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0165e045cc2ae1660270ca65e1676dbaab60feb0f91b10f7d0665e9b47e31f2" +dependencies = [ + "failure", + "hmac", + "once_cell 1.3.1", + "pbkdf2", + "rand 0.7.3", + "rustc-hash", + "sha2", + "unicode-normalization", +] + [[package]] name = "tiny-keccak" version = "2.0.2" @@ -1759,6 +2630,23 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "treasury" +version = "0.1.0" +dependencies = [ + "frame-support 2.0.0-alpha.5", + "frame-system 2.0.0-alpha.5", + "mocktopus", + "pallet-balances", + "parity-scale-codec", + "safe-mix", + "sp-core 2.0.0-alpha.5", + "sp-io 2.0.0-alpha.5", + "sp-runtime 2.0.0-alpha.5", + "sp-std 2.0.0-alpha.5", + "xclaim-core", +] + [[package]] name = "trie-db" version = "0.18.1" @@ -1772,6 +2660,19 @@ dependencies = [ "smallvec 1.2.0", ] +[[package]] +name = "trie-db" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de9222c50cc325855621271157c973da27a0dcd26fa06f8edf81020bd2333df0" +dependencies = [ + "hash-db", + "hashbrown 0.6.3", + "log", + "rustc-hex", + "smallvec 1.2.0", +] + [[package]] name = "trie-root" version = "0.15.2" @@ -1781,6 +2682,15 @@ dependencies = [ "hash-db", ] +[[package]] +name = "trie-root" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "652931506d2c1244d7217a70b99f56718a7b4161b37f04e7cd868072a99f68cd" +dependencies = [ + "hash-db", +] + [[package]] name = "twox-hash" version = "1.5.0" @@ -1808,18 +2718,126 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "unicode-normalization" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" +dependencies = [ + "smallvec 1.2.0", +] + [[package]] name = "unicode-xid" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +[[package]] +name = "vault-registry" +version = "0.0.1" +dependencies = [ + "frame-support 2.0.0-alpha.5", + "frame-system 2.0.0-alpha.5", + "mocktopus", + "node-primitives", + "pallet-balances", + "pallet-timestamp 2.0.0-alpha.5", + "parity-scale-codec", + "sp-core 2.0.0-alpha.5", + "sp-io 2.0.0-alpha.5", + "sp-runtime 2.0.0-alpha.5", + "xclaim-core", +] + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +[[package]] +name = "wasm-bindgen" +version = "0.2.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cc57ce05287f8376e998cbddfb4c8cb43b84a7ec55cf4551d7c00eef317a47f" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d967d37bf6c16cca2973ca3af071d0a2523392e4a594548155d89a678f4237cd" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7add542ea1ac7fdaa9dc25e031a6af33b7d63376292bd24140c637d00d1c312a" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bd151b63e1ea881bb742cd20e1d6127cef28399558f3b5d415289bc41eee3a4" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d68a5b36eef1be7868f668632863292e37739656a80fc4b9acec7b0bd35a4931" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf76fe7d25ac79748a37538b7daeed1c7a6867c92d3245c12c6222e4a20d639" + +[[package]] +name = "wasm-timer" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "324c5e65a08699c9c4334ba136597ab22b85dccd4b65dd1e36ccf8f723a95b54" +dependencies = [ + "futures", + "js-sys", + "parking_lot 0.9.0", + "pin-utils", + "send_wrapper", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "wasmi" version = "0.6.2" @@ -1843,6 +2861,16 @@ dependencies = [ "parity-wasm", ] +[[package]] +name = "web-sys" +version = "0.3.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d6f51648d8c56c366144378a33290049eafdd784071077f6fe37dae64c1c4cb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "winapi" version = "0.3.8" @@ -1869,8 +2897,8 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" name = "xclaim-core" version = "0.1.0" dependencies = [ - "frame-support", - "sp-runtime", + "frame-support 2.0.0-alpha.5", + "sp-runtime 2.0.0-alpha.5", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 7e8083c47f..1069fcaeed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,7 @@ members = [ "crates/btc-relay", "crates/security", "crates/exchange-rate-oracle", + "crates/vault-registry", + "crates/treasury", + "crates/collateral", ] diff --git a/README.md b/README.md index ae19666fa3..c0d98791ff 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,8 @@ The Substrate runtime makes use of various custom pallets and crates that are fo ### Development Progess -- [bitcoin](crates/bitcoin): [Beta] library for Bitcoin type, parsing and verification functions. +- [bitcoin](crates/bitcoin): [Beta] Library for Bitcoin type, parsing and verification functions. +- [btc-relay](crates/btc-relay): [Beta] Stateful SPV client for Bitcoin. Stores Bitcoin main chain, tracks forks, verifies Merkle proofs and validates specific transaction formats. - [btc-core](crates/btc-core): [Beta] Error types used in BTC-Relay and Bitcoin. - [priority-map](crates/priority-map): [WIP] Priority queue based on a mapping. Used to efficiently track ongoing forks and handle re-orgs. - [exchange-rate-oracle](crates/exchange-rate-oracle): [Beta] Exchange rate oracle. Integration with external provider pending. diff --git a/crates/bitcoin/examples/parse-transaction.rs b/crates/bitcoin/examples/parse-transaction.rs index 5694e0e8de..92e15521f4 100644 --- a/crates/bitcoin/examples/parse-transaction.rs +++ b/crates/bitcoin/examples/parse-transaction.rs @@ -8,7 +8,10 @@ use bitcoin::parser::parse_transaction; fn main() { let raw_tx = hex::decode(RAW_TRANSACTION).unwrap(); let tx = parse_transaction(&raw_tx).unwrap(); - println!("height {}", hex::encode(&tx.inputs[0].height.as_ref().unwrap())); + println!( + "height {}", + hex::encode(&tx.inputs[0].height.as_ref().unwrap()) + ); println!("ouptut 1 script {}", hex::encode(&tx.outputs[0].script)); println!("ouptut 2 script {}", hex::encode(&tx.outputs[1].script)); } diff --git a/crates/bitcoin/src/merkle.rs b/crates/bitcoin/src/merkle.rs index 132675514d..0ec22ba0fd 100644 --- a/crates/bitcoin/src/merkle.rs +++ b/crates/bitcoin/src/merkle.rs @@ -6,12 +6,11 @@ use mocktopus::macros::mockable; use btc_core::Error; use crate::parser::BytesParser; -use crate::types::{BlockHeader, H256Le, CompactUint}; +use crate::types::{BlockHeader, CompactUint, H256Le}; use crate::utils::hash256_merkle_step; - /// Values taken from https://github.com/bitcoin/bitcoin/blob/78dae8caccd82cfbfd76557f1fb7d7557c7b5edb/src/consensus/consensus.h -const MAX_BLOCK_WEIGHT: u32 = 4000000; +const MAX_BLOCK_WEIGHT: u32 = 4_000_000; const WITNESS_SCALE_FACTOR: u32 = 4; const MIN_TRANSACTION_WEIGHT: u32 = WITNESS_SCALE_FACTOR * 60; const MAX_TRANSACTIONS_IN_PROOF: u32 = MAX_BLOCK_WEIGHT / MIN_TRANSACTION_WEIGHT; @@ -152,7 +151,7 @@ impl MerkleProof { /// * `merkle_proof` - Raw bytes of the merkle proof pub fn parse(merkle_proof: &[u8]) -> Result { let mut proof_parser = BytesParser::new(merkle_proof); - let header = proof_parser.parse()?; + let block_header = proof_parser.parse()?; let transactions_count = proof_parser.parse()?; let hashes_count: CompactUint = proof_parser.parse()?; @@ -170,10 +169,10 @@ impl MerkleProof { } Ok(MerkleProof { - block_header: header, - transactions_count: transactions_count, - hashes: hashes, - flag_bits: flag_bits, + block_header, + transactions_count, + hashes, + flag_bits, }) } } @@ -182,9 +181,9 @@ impl MerkleProof { mod tests { use super::*; + use mocktopus::mocking::*; use primitive_types::H256; use std::str::FromStr; - use mocktopus::mocking::*; // curl -s -H 'content-type: application/json' http://satoshi.doc.ic.ac.uk:8332 -d '{ // "jsonrpc": "1.0", @@ -198,9 +197,18 @@ mod tests { const PROOF_HEX: &str = "00000020ecf348128755dbeea5deb8eddf64566d9d4e59bc65d485000000000000000000901f0d92a66ee7dcefd02fa282ca63ce85288bab628253da31ef259b24abe8a0470a385a45960018e8d672f8a90a00000d0bdabada1fb6e3cef7f5c6e234621e3230a2f54efc1cba0b16375d9980ecbc023cbef3ba8d8632ea220927ec8f95190b30769eb35d87618f210382c9445f192504074f56951b772efa43b89320d9c430b0d156b93b7a1ff316471e715151a0619a39392657f25289eb713168818bd5b37476f1bc59b166deaa736d8a58756f9d7ce2aef46d8004c5fe3293d883838f87b5f1da03839878895b71530e9ff89338bb6d4578b3c3135ff3e8671f9a64d43b22e14c2893e8271cecd420f11d2359307403bb1f3128885b3912336045269ef909d64576b93e816fa522c8c027fe408700dd4bdee0254c069ccb728d3516fe1e27578b31d70695e3e35483da448f3a951273e018de7f2a8f657064b013c6ede75c74bbd7f98fdae1c2ac6789ee7b21a791aa29d60e89fff2d1d2b1ada50aa9f59f403823c8c58bb092dc58dc09b28158ca15447da9c3bedb0b160f3fe1668d5a27716e27661bcb75ddbf3468f5c76b7bed1004c6b4df4da2ce80b831a7c260b515e6355e1c306373d2233e8de6fda3674ed95d17a01a1f64b27ba88c3676024fbf8d5dd962ffc4d5e9f3b1700763ab88047f7d0000"; fn sample_valid_proof_result() -> ProofResult { - let tx_id = H256Le::from_bytes_le(&hex::decode("c8589f304d3b9df1d4d8b3d15eb6edaaa2af9d796e9d9ace12b31f293705c5e9".to_owned()).unwrap()); - let merkle_root = H256Le::from_bytes_le(&hex::decode("90d079ef103a8b7d3d9315126468f78b456690ba6628d1dcd5a16c9990fbe11e".to_owned()).unwrap()); - + let tx_id = H256Le::from_bytes_le( + &hex::decode( + "c8589f304d3b9df1d4d8b3d15eb6edaaa2af9d796e9d9ace12b31f293705c5e9".to_owned(), + ) + .unwrap(), + ); + let merkle_root = H256Le::from_bytes_le( + &hex::decode( + "90d079ef103a8b7d3d9315126468f78b456690ba6628d1dcd5a16c9990fbe11e".to_owned(), + ) + .unwrap(), + ); ProofResult { extracted_root: merkle_root, transaction_hash: tx_id, @@ -213,8 +221,7 @@ mod tests { let mock_proof_result = sample_valid_proof_result(); let proof = MerkleProof::parse(&hex::decode(&PROOF_HEX[..]).unwrap()).unwrap(); - MerkleProof::verify_proof. - mock_safe(move |_| MockResult::Return(Ok(mock_proof_result))); + MerkleProof::verify_proof.mock_safe(move |_| MockResult::Return(Ok(mock_proof_result))); let res = MerkleProof::verify_proof(&proof).unwrap(); assert_eq!(res, mock_proof_result); diff --git a/crates/bitcoin/src/parser.rs b/crates/bitcoin/src/parser.rs index f081825e72..0de816a1f9 100644 --- a/crates/bitcoin/src/parser.rs +++ b/crates/bitcoin/src/parser.rs @@ -7,11 +7,9 @@ extern crate mocktopus; #[cfg(test)] use mocktopus::macros::mockable; - use btc_core::Error; -const SERIALIZE_TRANSACTION_NO_WITNESS: i32 = 0x40000000; - +const SERIALIZE_TRANSACTION_NO_WITNESS: i32 = 0x4000_0000; /// Type to be parsed from a bytes array pub(crate) trait Parsable: Sized { @@ -66,8 +64,8 @@ impl Parsable for BlockHeader { if position + 80 > raw_bytes.len() { return Err(Error::EOS); } - let header_bytes = header_from_bytes(&raw_bytes[position..position + 80]); - let block_header = parse_block_header(header_bytes)?; + let header_bytes = RawBlockHeader::from_bytes(&raw_bytes[position..position + 80])?; + let block_header = parse_block_header(&header_bytes)?; Ok((block_header, 80)) } } @@ -96,7 +94,10 @@ impl Parsable for Vec { } } -impl ParsableMeta for Vec where T: ParsableMeta { +impl ParsableMeta for Vec +where + T: ParsableMeta, +{ fn parse_with(raw_bytes: &[u8], position: usize, extra: U) -> Result<(Vec, usize), Error> { let mut result: Vec = Vec::new(); let mut parser = BytesParser::new(&raw_bytes[position..]); @@ -140,7 +141,7 @@ impl Parsable for TransactionOutput { impl Parsable for U256 { fn parse(raw_bytes: &[u8], position: usize) -> Result<(U256, usize), Error> { if position + 4 > raw_bytes.len() { - return Err(Error::EOS) + return Err(Error::EOS); } let raw_exponent = raw_bytes[position + 3]; if raw_exponent < 3 { @@ -210,46 +211,34 @@ pub trait FromLeBytes: Sized { impl FromLeBytes for BlockHeader { fn from_le_bytes(bytes: &[u8]) -> Result { - parse_block_header(header_from_bytes(bytes)) + parse_block_header(&RawBlockHeader::from_bytes(bytes)?) } } -/// Returns a raw block header from a bytes slice -/// -/// # Arguments -/// -/// * `bytes` - A slice containing the header -pub fn header_from_bytes(bytes: &[u8]) -> RawBlockHeader { - let mut result: RawBlockHeader = [0; 80]; - result.copy_from_slice(&bytes); - result -} - - /// Parses the raw bitcoin header into a Rust struct /// /// # Arguments /// /// * `header` - An 80-byte Bitcoin header -pub fn parse_block_header(raw_header: RawBlockHeader) -> Result { - let mut parser = BytesParser::new(&raw_header); +pub fn parse_block_header(raw_header: &RawBlockHeader) -> Result { + let mut parser = BytesParser::new(raw_header.as_slice()); let version: i32 = parser.parse()?; - let previous_block_hash: H256Le = parser.parse()?; + let hash_prev_block: H256Le = parser.parse()?; let merkle_root: H256Le = parser.parse()?; let timestamp: u32 = parser.parse()?; let target: U256 = parser.parse()?; let nonce: u32 = parser.parse()?; let block_header = BlockHeader { - merkle_root: merkle_root, - target: target, + merkle_root, + target, timestamp: timestamp as u64, - version: version, - nonce: nonce, - hash_prev_block: previous_block_hash, + version, + nonce, + hash_prev_block, }; - return Ok(block_header); + Ok(block_header) } /// Returns the value of a compactly encoded uint and the number of bytes consumed @@ -299,7 +288,7 @@ pub fn parse_transaction(raw_transaction: &[u8]) -> Result { let mut inputs: Vec = parser.parse_with(version)?; let mut flags: u8 = 0; - if inputs.len() == 0 && allow_witness { + if inputs.is_empty() && allow_witness { flags = parser.parse()?; inputs = parser.parse_with(version)?; } @@ -325,29 +314,29 @@ pub fn parse_transaction(raw_transaction: &[u8]) -> Result { } Ok(Transaction { - version: version, - inputs: inputs, - outputs: outputs, - block_height: block_height, - locktime: locktime, + version, + inputs, + outputs, + block_height, + locktime, }) } /// Parses a transaction input -pub fn parse_transaction_input( +fn parse_transaction_input( raw_input: &[u8], version: i32, ) -> Result<(TransactionInput, usize), Error> { let mut parser = BytesParser::new(raw_input); let previous_hash: H256Le = parser.parse()?; - let pervious_index: u32 = parser.parse()?; + let previous_index: u32 = parser.parse()?; // coinbase input has no previous hash let is_coinbase = previous_hash == H256Le::zero(); // fail if transaction is coinbase and previous index is not 0xffffffff // previous_hash - if is_coinbase && pervious_index != u32::max_value() { + if is_coinbase && previous_index != u32::max_value() { return Err(Error::MalformedTransaction); } @@ -370,19 +359,19 @@ pub fn parse_transaction_input( Ok(( TransactionInput { - previous_hash: previous_hash, - previous_index: pervious_index, + previous_hash, + previous_index, coinbase: is_coinbase, - height: height, - script: script, - sequence: sequence, + height, + script, + sequence, witness: None, }, consumed_bytes, )) } -pub fn parse_transaction_output(raw_output: &[u8]) -> Result<(TransactionOutput, usize), Error> { +fn parse_transaction_output(raw_output: &[u8]) -> Result<(TransactionOutput, usize), Error> { let mut parser = BytesParser::new(raw_output); let value: i64 = parser.parse()?; let script_size: CompactUint = parser.parse()?; @@ -390,25 +379,11 @@ pub fn parse_transaction_output(raw_output: &[u8]) -> Result<(TransactionOutput, return Err(Error::MalformedTransaction); } let script = parser.read(script_size.value as usize)?; - Ok(( - TransactionOutput { - value: value, - script: Vec::from(script), - }, - parser.position, - )) -} - -pub fn extract_value(raw_output: &[u8]) -> u64 { - let mut arr: [u8; 8] = Default::default(); - arr.copy_from_slice(&raw_output[..8]); - u64::from_le_bytes(arr) + Ok((TransactionOutput { value, script }, parser.position)) } pub fn extract_address_hash(output_script: &[u8]) -> Result, Error> { - let script_len = output_script.len(); - // Witness if output_script[0] == 0 { if script_len < 2 { @@ -425,24 +400,35 @@ pub fn extract_address_hash(output_script: &[u8]) -> Result, Error> { // 25 bytes // Format: // 0x76 (OP_DUP) - 0xa9 (OP_HASH160) - 0x14 (20 bytes len) - <20 bytes pubkey hash> - 0x88 (OP_EQUALVERIFY) - 0xac (OP_CHECKSIG) - if script_len as u32 == P2PKH_SCRIPT_SIZE && output_script[0..=2] == [OpCode::OpDup as u8, OpCode::OpHash160 as u8, HASH160_SIZE_HEX] { - if output_script[script_len - 2..] != [OpCode::OpEqualVerify as u8, OpCode::OpCheckSig as u8] { + if script_len as u32 == P2PKH_SCRIPT_SIZE + && output_script[0..=2] + == [ + OpCode::OpDup as u8, + OpCode::OpHash160 as u8, + HASH160_SIZE_HEX, + ] + { + if output_script[script_len - 2..] + != [OpCode::OpEqualVerify as u8, OpCode::OpCheckSig as u8] + { return Err(Error::MalformedP2PKHOutput); } - return Ok(output_script[3..script_len-2].to_vec()); + return Ok(output_script[3..script_len - 2].to_vec()); } // P2SH // 23 bytes - // Format: + // Format: // 0xa9 (OP_HASH160) - 0x14 (20 bytes hash) - <20 bytes script hash> - 0x87 (OP_EQUAL) - if script_len as u32 == P2SH_SCRIPT_SIZE && output_script[0..=1] == [OpCode::OpHash160 as u8, HASH160_SIZE_HEX] { - if output_script[script_len-1] != OpCode::OpEqual as u8 { - return Err(Error::MalformedP2SHOutput) + if script_len as u32 == P2SH_SCRIPT_SIZE + && output_script[0..=1] == [OpCode::OpHash160 as u8, HASH160_SIZE_HEX] + { + if output_script[script_len - 1] != OpCode::OpEqual as u8 { + return Err(Error::MalformedP2SHOutput); } - return Ok(output_script[2..(script_len-1)].to_vec()) + return Ok(output_script[2..(script_len - 1)].to_vec()); } - return Err(Error::UnsupportedOutputFormat); + Err(Error::UnsupportedOutputFormat) } pub fn extract_op_return_data(output_script: &[u8]) -> Result, Error> { @@ -458,14 +444,12 @@ pub fn extract_op_return_data(output_script: &[u8]) -> Result, Error> { Ok(output_script[2..].to_vec()) } - #[cfg(test)] mod tests { use super::*; // examples from https://bitcoin.org/en/developer-reference#block-headers - #[test] fn test_parse_block_header() { let hex_header = "02000000".to_owned() + // ............... Block version: 2 @@ -476,8 +460,8 @@ mod tests { "24d95a54" + // ........................... Unix time: 1415239972 "30c31b18" + // ........................... Target: 0x1bc330 * 256**(0x18-3) "fe9f0864"; - let raw_header = hex::decode(&hex_header[..]).unwrap(); - let parsed_header = parse_block_header(header_from_bytes(&raw_header)).unwrap(); + let raw_header = RawBlockHeader::from_hex(&hex_header).unwrap(); + let parsed_header = parse_block_header(&raw_header).unwrap(); assert_eq!(parsed_header.version, 2); assert_eq!(parsed_header.timestamp, 1415239972); assert_eq!( @@ -545,7 +529,7 @@ mod tests { "cbc20a7664f2f69e5355aa427045bc15" + "e7c6c772" + // PubKey hash "88" + // OP_EQUALVERIFY - "ac" // OP_CHECKSIG + "ac" // OP_CHECKSIG } fn sample_transaction() -> String { @@ -636,17 +620,23 @@ mod tests { assert_eq!(inputs[0].coinbase, true); assert_eq!(inputs[0].witness.is_some(), true); assert_eq!(outputs.len(), 2); - assert_eq!(&hex::encode(&outputs[0].script), "a91466c7060feb882664ae62ffad0051fe843e318e8587"); - assert_eq!(&hex::encode(&outputs[1].script), "6a24aa21a9ede5c17d15b8b1fa2811b7e6da66ffa5e1aaa05922c69068bf90cd585b95bb4675"); + assert_eq!( + &hex::encode(&outputs[0].script), + "a91466c7060feb882664ae62ffad0051fe843e318e8587" + ); + assert_eq!( + &hex::encode(&outputs[1].script), + "6a24aa21a9ede5c17d15b8b1fa2811b7e6da66ffa5e1aaa05922c69068bf90cd585b95bb4675" + ); assert_eq!(transaction.block_height, Some(0)); assert_eq!(transaction.locktime, None); } #[test] - fn test_extract_address_hash_valid_p2pkh(){ + fn test_extract_address_hash_valid_p2pkh() { let p2pkh_script = hex::decode(&sample_valid_p2pkh()).unwrap(); - let p2pkh_address: [u8; 20] = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; + let p2pkh_address: [u8; 20] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; let extr_p2pkh = extract_address_hash(&p2pkh_script).unwrap(); @@ -654,10 +644,10 @@ mod tests { } #[test] - fn test_extract_address_hash_valid_p2sh(){ + fn test_extract_address_hash_valid_p2sh() { let p2sh_script = hex::decode(&sample_valid_p2sh()).unwrap(); - let p2sh_address: [u8; 20] = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; + let p2sh_address: [u8; 20] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; let extr_p2sh = extract_address_hash(&p2sh_script).unwrap(); diff --git a/crates/bitcoin/src/types.rs b/crates/bitcoin/src/types.rs index 7a9185b9c2..8d93490422 100644 --- a/crates/bitcoin/src/types.rs +++ b/crates/bitcoin/src/types.rs @@ -1,29 +1,69 @@ extern crate hex; -use primitive_types::{U256, H256}; -use codec::{Encode, Decode}; -use node_primitives::{Moment}; -use sp_std::collections::btree_map::BTreeMap; +use crate::parser::*; +use crate::utils::*; +use codec::{Decode, Encode}; +use node_primitives::Moment; +use primitive_types::{H256, U256}; use sp_std::collections::btree_set::BTreeSet; use btc_core::Error; -use crate::utils::*; -use crate::parser::*; /// Custom Types /// Bitcoin Raw Block Header type +#[derive(Encode, Decode, Copy, Clone)] +pub struct RawBlockHeader([u8; 80]); + +impl RawBlockHeader { + /// Returns a raw block header from a bytes slice + /// + /// # Arguments + /// + /// * `bytes` - A slice containing the header + pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != 80 { + return Err(Error::InvalidHeaderSize); + } + let mut result: [u8; 80] = [0; 80]; + result.copy_from_slice(&bytes); + Ok(RawBlockHeader(result)) + } -pub type RawBlockHeader = [u8; 80]; + /// Returns a raw block header from a bytes slice + /// + /// # Arguments + /// + /// * `bytes` - A slice containing the header + pub fn from_hex>(hex_string: T) -> Result { + let bytes = hex::decode(hex_string).map_err(|_e| Error::MalformedHeader)?; + Self::from_bytes(&bytes) + } -// #[derive(Encode, Decode, Default, Copy, Clone, PartialEq)] -// struct RawBlockHeader(pub [u8; 32]); + /// Returns the hash of the block header using Bitcoin's double sha256 + pub fn hash(&self) -> H256Le { + H256Le::from_bytes_le(&sha256d(self.as_slice())) + } -// impl RawBlockHeader { -// fn hash(&self) -> H256Le { + /// Returns the block header as a slice + pub fn as_slice(&self) -> &[u8] { + &self.0 + } +} -// } -// } +impl PartialEq for RawBlockHeader { + fn eq(&self, other: &Self) -> bool { + let self_bytes = &self.0[..]; + let other_bytes = &other.0[..]; + self_bytes == other_bytes + } +} + +impl std::fmt::Debug for RawBlockHeader { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_list().entries(self.0.iter()).finish() + } +} // Constants pub const P2PKH_SCRIPT_SIZE: u32 = 25; @@ -41,18 +81,7 @@ pub struct BlockHeader { pub timestamp: Moment, pub version: i32, pub hash_prev_block: H256Le, - pub nonce: u32 -} - -impl BlockHeader { - - pub fn block_hash_le(bytes: &[u8]) -> H256Le{ - sha256d_le(bytes) - } - - pub fn block_hash_be(bytes: &[u8]) -> H256{ - sha256d_be(bytes) - } + pub nonce: u32, } /// Bitcoin transaction input @@ -68,7 +97,7 @@ pub struct TransactionInput { } impl TransactionInput { - pub fn with_witness(&mut self, witness: Vec) -> () { + pub fn with_witness(&mut self, witness: Vec) { self.witness = Some(witness); } } @@ -87,10 +116,9 @@ pub struct Transaction { pub inputs: Vec, pub outputs: Vec, pub block_height: Option, //FIXME: why is this optional? - pub locktime: Option, //FIXME: why is this optional? + pub locktime: Option, //FIXME: why is this optional? } - impl Transaction { pub fn tx_id(raw_tx: &[u8]) -> H256Le { sha256d_le(&raw_tx) @@ -99,7 +127,6 @@ impl Transaction { /// Bitcoin Enriched Block Headers #[derive(Encode, Decode, Default, Clone, Copy, PartialEq, Debug)] -// #[cfg_attr(feature = "std", derive(Debug))] pub struct RichBlockHeader { pub block_hash: H256Le, pub block_header: BlockHeader, @@ -108,12 +135,15 @@ pub struct RichBlockHeader { } impl RichBlockHeader { - // Creates a RichBlockHeader given a RawBlockHeader, Blockchain identifier and block height - pub fn construct_rich_block_header(raw_block_header: RawBlockHeader, chain_ref: u32, block_height: u32) -> Result { + pub fn construct( + raw_block_header: RawBlockHeader, + chain_ref: u32, + block_height: u32, + ) -> Result { Ok(RichBlockHeader { - block_hash: BlockHeader::block_hash_le(&raw_block_header), - block_header: BlockHeader::from_le_bytes(&raw_block_header)?, + block_hash: raw_block_header.hash(), + block_header: BlockHeader::from_le_bytes(raw_block_header.as_slice())?, block_height: block_height, chain_ref: chain_ref, }) @@ -122,10 +152,8 @@ impl RichBlockHeader { /// Representation of a Bitcoin blockchain #[derive(Encode, Decode, Default, Clone, PartialEq, Debug)] -//#[cfg_attr(feature = "std", derive(Debug))] pub struct BlockChain { pub chain_id: u32, - pub chain: BTreeMap, pub start_height: u32, pub max_height: u32, pub no_data: BTreeSet, @@ -134,9 +162,8 @@ pub struct BlockChain { /// Represents a bitcoin 32 bytes hash digest encoded in little-endian #[derive(Encode, Decode, Default, PartialEq, Eq, Clone, Copy, Debug)] -//#[cfg_attr(feature="std", derive(Debug))] pub struct H256Le { - content: [u8; 32] + content: [u8; 32], } impl H256Le { @@ -149,7 +176,7 @@ impl H256Le { pub fn from_bytes_le(bytes: &[u8]) -> H256Le { let mut content: [u8; 32] = Default::default(); content.copy_from_slice(&bytes); - H256Le { content: content } + H256Le { content } } /// Creates a H256Le from big endian bytes @@ -157,7 +184,7 @@ impl H256Le { let bytes_le = reverse_endianness(bytes); let mut content: [u8; 32] = Default::default(); content.copy_from_slice(&bytes_le); - H256Le { content: content } + H256Le { content } } pub fn from_hex_le(hex: &str) -> H256Le { @@ -177,7 +204,7 @@ impl H256Le { /// Returns the content of the H256Le encoded in little endian pub fn to_bytes_le(&self) -> [u8; 32] { - self.content.clone() + self.content } /// Returns the content of the H256Le encoded in little endian hex @@ -201,7 +228,7 @@ impl H256Le { } } -#[cfg(feature="std")] +#[cfg(feature = "std")] impl std::fmt::Display for H256Le { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "0x{}", self.to_hex_be()) @@ -214,15 +241,14 @@ impl std::fmt::LowerHex for H256Le { } } - // Bitcoin Script OpCodes pub enum OpCode { OpDup = 0x76, OpHash160 = 0xa9, OpEqualVerify = 0x88, - OpCheckSig = 0xac, + OpCheckSig = 0xac, OpEqual = 0x87, - OpReturn = 0x6a + OpReturn = 0x6a, } impl PartialEq for H256 { @@ -238,7 +264,6 @@ impl PartialEq for H256Le { } } - pub(crate) struct CompactUint { pub(crate) value: u64, } diff --git a/crates/bitcoin/src/utils.rs b/crates/bitcoin/src/utils.rs index 54d96e0a46..545084f13c 100644 --- a/crates/bitcoin/src/utils.rs +++ b/crates/bitcoin/src/utils.rs @@ -1,14 +1,12 @@ - -use sha2::{Sha256, Digest}; use crate::types::H256Le; use primitive_types::H256; - +use sha2::{Digest, Sha256}; /// Computes Bitcoin's double SHA256 hash over a LE byte encoded input -/// +/// /// # Arguments /// * data: LE bytes encoded input -/// +/// /// # Returns /// * The double SHA256 hash encoded as LE bytes from data pub fn sha256d(bytes: &[u8]) -> [u8; 32] { @@ -37,7 +35,6 @@ pub fn hash256_merkle_step(a: &[u8], b: &[u8]) -> H256Le { H256Le::from_bytes_le(&sha256d(&res)) } - /// Reverses endianness of the value /// ``` /// let bytes = bitcoin::utils::reverse_endianness(&[1, 2, 3]); @@ -49,12 +46,11 @@ pub fn reverse_endianness(bytes: &[u8]) -> Vec { vec } - // FIXME: maybe use sp_core sha2_256? pub fn sha256d_be(bytes: &[u8]) -> H256 { - return H256::from_slice(&sha256d(bytes)[..]); + H256::from_slice(&sha256d(bytes)[..]) } pub fn sha256d_le(bytes: &[u8]) -> H256Le { - return H256Le::from_bytes_le(&sha256d(bytes)); -} \ No newline at end of file + H256Le::from_bytes_le(&sha256d(bytes)) +} diff --git a/crates/btc-core/src/lib.rs b/crates/btc-core/src/lib.rs index edd89a49f0..454ed1a1fe 100644 --- a/crates/btc-core/src/lib.rs +++ b/crates/btc-core/src/lib.rs @@ -53,7 +53,7 @@ pub enum Error { } impl Error { - pub fn message(&self) -> &'static str { + pub fn message(self) -> &'static str { match self { Error::AlreadyInitialized => "Already initialized", Error::MissingBlockHeight => "Missing the block at this height", diff --git a/crates/btc-relay/.vscode/launch.json b/crates/btc-relay/.vscode/launch.json new file mode 100644 index 0000000000..58f00a5168 --- /dev/null +++ b/crates/btc-relay/.vscode/launch.json @@ -0,0 +1,28 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in library 'btc-relay'", + "cargo": { + "args": [ + "test", + "test_validate_transaction_insufficient_payment_value_fails", + "--no-run", + "--lib", + "--package=btc-relay" + ], + "filter": { + "name": "btc-relay", + "kind": "lib" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + } + ] +} \ No newline at end of file diff --git a/crates/btc-relay/src/lib.rs b/crates/btc-relay/src/lib.rs index 7e57a51db2..37eb104899 100644 --- a/crates/btc-relay/src/lib.rs +++ b/crates/btc-relay/src/lib.rs @@ -16,27 +16,20 @@ use mocktopus::macros::mockable; /// # BTC-Relay implementation /// This is the implementation of the BTC-Relay following the spec at: /// https://interlay.gitlab.io/polkabtc-spec/btcrelay-spec/ - // Substrate -use frame_support::{ - decl_event, decl_module, decl_storage, dispatch::DispatchResult, ensure -}; +use frame_support::{decl_event, decl_module, decl_storage, dispatch::DispatchResult, ensure}; use sp_core::{H160, U256}; -use sp_std::collections::btree_map::BTreeMap; use sp_std::collections::btree_set::BTreeSet; use system::ensure_signed; // Crates use bitcoin::merkle::{MerkleProof, ProofResult}; use bitcoin::parser::{ - extract_address_hash, extract_op_return_data, - header_from_bytes, parse_block_header, parse_transaction, + extract_address_hash, extract_op_return_data, parse_block_header, parse_transaction, }; use bitcoin::types::{ - BlockChain, BlockHeader, H256Le, - RawBlockHeader, RichBlockHeader, Transaction + BlockChain, BlockHeader, H256Le, RawBlockHeader, RichBlockHeader, Transaction, }; -use security; use security::ErrorCode; use btc_core::Error; @@ -55,7 +48,7 @@ pub trait Trait: system::Trait //+ security::Trait pub const DIFFICULTY_ADJUSTMENT_INTERVAL: u32 = 2016; /// Target Timespan -pub const TARGET_TIMESPAN: u32 = 1209600; +pub const TARGET_TIMESPAN: u32 = 1_209_600; // Used in Bitcoin's retarget algorithm pub const TARGET_TIMESPAN_DIVISOR: u32 = 4; @@ -66,7 +59,7 @@ pub const UNROUNDED_MAX_TARGET: U256 = U256([ ::max_value(), ::max_value(), ::max_value(), - 0x00000000ffffffffu64, + 0x0000_0000_ffff_ffffu64, ]); /// Main chain id @@ -88,6 +81,9 @@ decl_storage! { /// Store the index for each tracked blockchain ChainsIndex: map u32 => BlockChain; + /// Stores a mapping from (chain_index, block height) to block hash + ChainsHashes: double_map u32, blake2_256(u32) => H256Le; + /// Store the current blockchain tip BestBlock: H256Le; @@ -115,7 +111,7 @@ decl_module! { /// block header. fn initialize( origin, - block_header_bytes: Vec, + raw_block_header: RawBlockHeader, block_height: u32) -> DispatchResult { @@ -125,9 +121,8 @@ decl_module! { ensure!(!Self::best_block_exists(), Error::AlreadyInitialized); // Parse the block header bytes to extract the required info - let raw_block_header = header_from_bytes(&block_header_bytes); - let basic_block_header = parse_block_header(raw_block_header)?; - let block_header_hash = BlockHeader::block_hash_le(&raw_block_header); + let basic_block_header = parse_block_header(&raw_block_header)?; + let block_header_hash = raw_block_header.hash(); // construct the BlockChain struct let blockchain = Self::initialize_blockchain( @@ -167,9 +162,9 @@ decl_module! { /// /// # Arguments /// - /// * `block_header_bytes` - 80 byte raw Bitcoin block header. + /// * `raw_block_header` - 80 byte raw Bitcoin block header. fn store_block_header( - origin, block_header_bytes: Vec + origin, raw_block_header: RawBlockHeader ) -> DispatchResult { let _ = ensure_signed(origin)?; // Check if BTC _Parachain is in shutdown state.+ @@ -181,9 +176,8 @@ decl_module! { // ); // Parse the block header bytes to extract the required info - let raw_block_header = header_from_bytes(&block_header_bytes); - let basic_block_header = Self::verify_block_header(raw_block_header)?; - let block_header_hash = BlockHeader::block_hash_le(&raw_block_header); + let basic_block_header = Self::verify_block_header(&raw_block_header)?; + let block_header_hash = raw_block_header.hash(); let prev_header = Self::get_block_header_from_hash( basic_block_header.hash_prev_block @@ -282,13 +276,13 @@ decl_module! { /// # Arguments /// /// * `tx_id` - The hash of the transaction to check for - /// * `tx_block_height` - The height of the block in which the + /// * `tx_block_height` - The height of the block in which the /// transaction should be included - /// * `raw_merkle_proof` - The raw merkle proof as returned by + /// * `raw_merkle_proof` - The raw merkle proof as returned by /// bitcoin `gettxoutproof` - /// * `confirmations` - The number of confirmations needed to accept + /// * `confirmations` - The number of confirmations needed to accept /// the proof - /// * `insecure` - determines if checks against recommended global transaction confirmation are to be executed. Recommended: set to `true` + /// * `insecure` - determines if checks against recommended global transaction confirmation are to be executed. Recommended: set to `true` fn verify_transaction_inclusion( origin, tx_id: H256Le, @@ -305,7 +299,6 @@ decl_module! { ensure!(>::check_parachain_status(StatusCode::Running), Error::::Shutdown); */ - //let main_chain = Self::get_block_chain_from_id(MAIN_CHAIN_ID); let best_block_height = Self::get_best_block_height(); @@ -315,21 +308,20 @@ decl_module! { ).max_height; // fail if there is an ongoing fork - ensure!(best_block_height + ensure!(best_block_height >= next_best_fork_height + STABLE_TRANSACTION_CONFIRMATIONS, Error::OngoingFork); // This call fails if not enough confirmations Self::check_confirmations( - best_block_height, - confirmations, - block_height, + best_block_height, + confirmations, + block_height, insecure)?; - + let proof_result = Self::verify_merkle_proof(&raw_merkle_proof)?; - let rich_header = Self::get_block_header_from_height( - &Self::get_block_chain_from_id(MAIN_CHAIN_ID), + &Self::get_block_chain_from_id(MAIN_CHAIN_ID), block_height )?; @@ -344,8 +336,8 @@ decl_module! { Ok(()) } - /// Validates a given raw Bitcoin transaction, according to the - /// supported transaction format (see + /// Validates a given raw Bitcoin transaction, according to the + /// supported transaction format (see /// https://interlay.gitlab.io/polkabtc-spec/btcrelay-spec/intro/ /// accepted-format.html) /// This DOES NOT check if the transaction is included in a block @@ -354,11 +346,11 @@ decl_module! { /// /// # Arguments /// * `raw_tx` - raw Bitcoin transaction - /// * `paymentValue` - value of BTC sent in the 1st / + /// * `paymentValue` - value of BTC sent in the 1st / /// payment UTXO of the transaction - /// * `recipientBtcAddress` - 20 byte Bitcoin address of recipient + /// * `recipientBtcAddress` - 20 byte Bitcoin address of recipient /// of the BTC in the 1st / payment UTXO - /// * `op_return_id` - 32 byte hash identifier expected in + /// * `op_return_id` - 32 byte hash identifier expected in /// OP_RETURN (replay protection) fn validate_transaction( origin, @@ -403,13 +395,13 @@ impl Module { // START: Storage getter functions // ******************************** - /// Get chain id from position (sorted by max block height) + /// Get chain id from position (sorted by max block height) fn get_chain_id_from_position(position: u32) -> u32 { ::get(position) } /// Get the position of the fork in Chains fn get_chain_position_from_chain_id(chain_id: u32) -> Result { - for (k,v) in ::enumerate() { + for (k, v) in ::enumerate() { if v == chain_id { return Ok(k); } @@ -437,7 +429,7 @@ impl Module { fn best_block_exists() -> bool { ::exists() } - /// get the best block height + /// get the best block height fn get_best_block_height() -> u32 { ::get() } @@ -448,20 +440,17 @@ impl Module { /// Get a block hash from a blockchain /// # Arguments /// - /// * `blockchain`: the blockchian struct to search in + /// * `chain_id`: the id of the blockchain to search in /// * `block_height`: the height if the block header - fn get_block_hash( - blockchain: &BlockChain, block_height: u32 - ) -> Result { - match blockchain.chain.get(&block_height) { - Some(hash) => Ok(*hash), - None => return Err(Error::MissingBlockHeight.into()), + fn get_block_hash(chain_id: u32, block_height: u32) -> Result { + if !Self::block_exists(chain_id, block_height) { + return Err(Error::MissingBlockHeight); } + Ok(::get(chain_id, block_height)) } + /// Get a block header from its hash - fn get_block_header_from_hash( - block_hash: H256Le - ) -> Result { + fn get_block_header_from_hash(block_hash: H256Le) -> Result { if ::exists(block_hash) { return Ok(::get(block_hash)); } @@ -471,15 +460,14 @@ impl Module { fn block_header_exists(block_hash: H256Le) -> bool { ::exists(block_hash) } - /// Get a block header from + /// Get a block header from fn get_block_header_from_height( blockchain: &BlockChain, block_height: u32, ) -> Result { - let block_hash = Self::get_block_hash(blockchain, block_height)?; + let block_hash = Self::get_block_hash(blockchain.chain_id, block_height)?; Self::get_block_header_from_hash(block_hash) } - /// Storage setter functions /// Set a new chain with position and id fn set_chain_from_position_and_id(position: u32, id: u32) { @@ -505,9 +493,9 @@ impl Module { fn set_block_chain_from_id(id: u32, chain: &BlockChain) { ::insert(id, &chain); } - /// Update a blockchain in ChainsIndex + /// Update a blockchain in ChainsIndex fn mutate_block_chain_from_id(id: u32, chain: BlockChain) { - ::mutate(id, |b| {*b = chain}); + ::mutate(id, |b| *b = chain); } /// Remove a blockchain element from chainindex fn remove_blockchain_from_chainindex(id: u32) { @@ -519,9 +507,7 @@ impl Module { } /// update the chain_ref of a block header fn mutate_block_header_from_chain_id(hash: &H256Le, chain_ref: u32) { - ::mutate(&hash, |header| { - header.chain_ref = chain_ref - }); + ::mutate(&hash, |header| header.chain_ref = chain_ref); } /// Set a new best block @@ -539,47 +525,52 @@ impl Module { new_counter } - /// Initialize the new main blockchain with a single block - fn initialize_blockchain( - block_height: u32, block_hash: H256Le - ) -> BlockChain { + fn initialize_blockchain(block_height: u32, block_hash: H256Le) -> BlockChain { let chain_id = MAIN_CHAIN_ID; // generate an empty blockchain Self::generate_blockchain(chain_id, block_height, block_hash) } /// Create a new blockchain element with a new chain id - fn create_blockchain( - block_height: u32, block_hash: H256Le - ) -> BlockChain { + fn create_blockchain(block_height: u32, block_hash: H256Le) -> BlockChain { // get a new chain id let chain_id: u32 = Self::increment_chain_counter(); // generate an empty blockchain Self::generate_blockchain(chain_id, block_height, block_hash) } - /// Generate the raw blockchain from a chain Id and with a single block - fn generate_blockchain( - chain_id: u32, - block_height: u32, - block_hash: H256Le, - ) -> BlockChain { + /// Generate the raw blockchain from a chain Id and with a single block + fn generate_blockchain(chain_id: u32, block_height: u32, block_hash: H256Le) -> BlockChain { // initialize an empty chain - let mut chain = BTreeMap::new(); - chain.insert(block_height, block_hash); + Self::insert_block_hash(chain_id, block_height, block_hash); - let blockchain = BlockChain { - chain_id: chain_id, - chain: chain, + BlockChain { + chain_id, start_height: block_height, max_height: block_height, no_data: BTreeSet::new(), invalid: BTreeSet::new(), - }; - blockchain + } } + + fn insert_block_hash(chain_id: u32, block_height: u32, block_hash: H256Le) { + ::insert(chain_id, block_height, block_hash); + } + + fn remove_block_hash(chain_id: u32, block_height: u32) { + ::remove(chain_id, block_height); + } + + fn block_exists(chain_id: u32, block_height: u32) -> bool { + ::exists(chain_id, block_height) + } + + fn _blocks_count(chain_id: u32) -> usize { + ::iter_prefix(chain_id).count() + } + /// Add a new block header to an existing blockchain fn extend_blockchain( block_height: u32, @@ -588,9 +579,10 @@ impl Module { ) -> Result { let mut blockchain = prev_blockchain; - if blockchain.chain.insert(block_height, *block_hash).is_some() { - return Err(Error::DuplicateBlock.into()); + if Self::block_exists(blockchain.chain_id, block_height) { + return Err(Error::DuplicateBlock); } + Self::insert_block_hash(blockchain.chain_id, block_height, *block_hash); blockchain.max_height = block_height; @@ -624,10 +616,10 @@ impl Module { /// /// # Panics /// If ParachainStatus in Security module is not set to RUNNING - fn verify_block_header(raw_block_header: RawBlockHeader) -> Result { - let basic_block_header = parse_block_header(raw_block_header)?; + fn verify_block_header(raw_block_header: &RawBlockHeader) -> Result { + let basic_block_header = parse_block_header(&raw_block_header)?; - let block_header_hash = BlockHeader::block_hash_le(&raw_block_header); + let block_header_hash = raw_block_header.hash(); // Check that the block header is not yet stored in BTC-Relay ensure!( @@ -647,34 +639,44 @@ impl Module { // Check that the diff. target is indeed correctly set in the block header, i.e., check for re-target. let block_height = prev_block_header.block_height + 1; - let expected_target = match block_height >= 2016 && block_height % DIFFICULTY_ADJUSTMENT_INTERVAL == 0 { - true => Self::compute_new_target( - &prev_block_header, - block_height)?, - false => prev_block_header.block_header.target - }; + let expected_target = + if block_height >= 2016 && block_height % DIFFICULTY_ADJUSTMENT_INTERVAL == 0 { + Self::compute_new_target(&prev_block_header, block_height)? + } else { + prev_block_header.block_header.target + }; - ensure!(basic_block_header.target == expected_target, Error::DiffTargetHeader); + ensure!( + basic_block_header.target == expected_target, + Error::DiffTargetHeader + ); Ok(basic_block_header) } - /// Computes Bitcoin's PoW retarget algorithm for a given block height /// # Arguments /// * `prev_block_header`: previous block header /// * `block_height` : block height of new target - fn compute_new_target(prev_block_header: &RichBlockHeader, block_height: u32) -> Result { - + fn compute_new_target( + prev_block_header: &RichBlockHeader, + block_height: u32, + ) -> Result { // get time of last retarget - let last_retarget_time = Self::get_last_retarget_time(prev_block_header.chain_ref, block_height)?; + let last_retarget_time = + Self::get_last_retarget_time(prev_block_header.chain_ref, block_height)?; // Compute new target - let actual_timespan = match ((prev_block_header.block_header.timestamp - last_retarget_time) as u32) < (TARGET_TIMESPAN / TARGET_TIMESPAN_DIVISOR) { - true => TARGET_TIMESPAN / TARGET_TIMESPAN_DIVISOR, - false => TARGET_TIMESPAN * TARGET_TIMESPAN_DIVISOR, + let actual_timespan = if ((prev_block_header.block_header.timestamp - last_retarget_time) + as u32) + < (TARGET_TIMESPAN / TARGET_TIMESPAN_DIVISOR) + { + TARGET_TIMESPAN / TARGET_TIMESPAN_DIVISOR + } else { + TARGET_TIMESPAN * TARGET_TIMESPAN_DIVISOR }; - let new_target = U256::from(actual_timespan) * prev_block_header.block_header.target / U256::from(TARGET_TIMESPAN); + let new_target = U256::from(actual_timespan) * prev_block_header.block_header.target + / U256::from(TARGET_TIMESPAN); // ensure target does not exceed max. target Ok(if new_target > UNROUNDED_MAX_TARGET { @@ -685,20 +687,23 @@ impl Module { } /// Returns the timestamp of the last difficulty retarget on the specified BlockChain, given the current block height - /// + /// /// # Arguments /// * `chain_ref` - BlockChain identifier /// * `block_height` - current block height fn get_last_retarget_time(chain_ref: u32, block_height: u32) -> Result { - let block_chain = Self::get_block_chain_from_id(chain_ref); - let last_retarget_header = Self::get_block_header_from_height(&block_chain, block_height - DIFFICULTY_ADJUSTMENT_INTERVAL)?; + let block_chain = Self::get_block_chain_from_id(chain_ref); + let last_retarget_header = Self::get_block_header_from_height( + &block_chain, + block_height - DIFFICULTY_ADJUSTMENT_INTERVAL, + )?; Ok(last_retarget_header.block_header.timestamp) } - /// Swap the main chain with a fork. This method takes the starting height + /// Swap the main chain with a fork. This method takes the starting height /// of the fork and replaces each block in the main chain with the blocks - /// in the fork. It moves the replaced blocks in the main chain to a new - /// fork. + /// in the fork. It moves the replaced blocks in the main chain to a new + /// fork. /// Last, it replaces the chain_ref of each block header in the new main /// chain to the MAIN_CHAIN_ID and each block header in the new fork to the /// new chain id. @@ -718,13 +723,11 @@ impl Module { // generate a chain id let chain_id = Self::increment_chain_counter(); - // split off the chain - let forked_chain = main_chain.chain.split_off(&start_height); - // maybe split off the no data elements // check if there is a no_data block element // that is greater than start_height - let index_no_data = main_chain.no_data + let index_no_data = main_chain + .no_data .iter() .position(|&h| h >= start_height) .map(|v| v as u32); @@ -734,7 +737,8 @@ impl Module { }; // maybe split off the invalid elements - let index_invalid = main_chain.invalid + let index_invalid = main_chain + .invalid .iter() .position(|&h| h >= start_height) .map(|v| v as u32); @@ -746,25 +750,19 @@ impl Module { // store the main chain part that is going to be replaced by the new fork // into the forked_main_chain element let forked_main_chain: BlockChain = BlockChain { - chain_id: chain_id, - chain: forked_chain.clone(), - start_height: start_height, + chain_id, + start_height, max_height: main_chain.max_height, - no_data: no_data, - invalid: invalid, + no_data, + invalid, }; - // append the fork to the main chain - main_chain.chain.append(&mut fork.chain.clone()); main_chain.max_height = fork.max_height; main_chain.no_data.append(&mut fork.no_data.clone()); main_chain.invalid.append(&mut fork.invalid.clone()); // get the best block hash - let best_block = match main_chain.chain.get(&main_chain.max_height) { - Some(block) => block, - None => return Err(Error::BlockNotFound), - }; + let best_block = Self::get_block_hash(fork.chain_id, fork.max_height)?; // get the position of the fork in Chains let position: u32 = Self::get_chain_position_from_chain_id(fork.chain_id)?; @@ -773,7 +771,7 @@ impl Module { Self::set_block_chain_from_id(MAIN_CHAIN_ID, &main_chain); // Set BestBlock and BestBlockHeight to the submitted block - Self::set_best_block(best_block.clone()); + Self::set_best_block(best_block); Self::set_best_block_height(main_chain.max_height); // remove the fork from storage @@ -781,24 +779,26 @@ impl Module { Self::remove_blockchain_from_chain(position)?; // store the forked main chain - Self::set_block_chain_from_id( - forked_main_chain.chain_id, &forked_main_chain); + Self::set_block_chain_from_id(forked_main_chain.chain_id, &forked_main_chain); // insert the reference to the forked main chain in Chains Self::insert_sorted(&forked_main_chain); - // get an iterator of all forked block headers // update all the forked block headers - for (_height, block) in forked_chain.iter() { - Self::mutate_block_header_from_chain_id( - &block, forked_main_chain.chain_id); + for height in fork.start_height..=forked_main_chain.max_height { + let block_hash = Self::get_block_hash(main_chain.chain_id, height)?; + Self::insert_block_hash(forked_main_chain.chain_id, height, block_hash); + Self::mutate_block_header_from_chain_id(&block_hash, forked_main_chain.chain_id); + Self::remove_block_hash(MAIN_CHAIN_ID, height); } - // get an iterator of all new main chain block headers // update all new main chain block headers - for (_height, block) in fork.chain.iter() { + for height in fork.start_height..=fork.max_height { + let block = Self::get_block_hash(fork.chain_id, height)?; Self::mutate_block_header_from_chain_id(&block, MAIN_CHAIN_ID); + Self::insert_block_hash(MAIN_CHAIN_ID, height, block); } + ::remove_prefix(fork.chain_id); Ok(()) } @@ -817,8 +817,7 @@ impl Module { } // get the position of the fork in Chains - let fork_position: u32 = Self::get_chain_position_from_chain_id( - fork.chain_id)?; + let fork_position: u32 = Self::get_chain_position_from_chain_id(fork.chain_id)?; // print!("fork position {:?}\n", fork_position); // check if the previous element in Chains has a lower block_height let mut current_position = fork_position; @@ -829,11 +828,9 @@ impl Module { // get the previous position let prev_position = current_position - 1; // get the blockchain id - let prev_blockchain_id = Self::get_chain_id_from_position( - prev_position); + let prev_blockchain_id = Self::get_chain_id_from_position(prev_position); // get the previous blockchain height - let prev_height = Self::get_block_chain_from_id(prev_blockchain_id) - .max_height; + let prev_height = Self::get_block_chain_from_id(prev_blockchain_id).max_height; // swap elements if block height is greater // print!("curr height {:?}\n", current_height); // print!("prev height {:?}\n", prev_height); @@ -845,8 +842,7 @@ impl Module { // and the current height is more than the // STABLE_TRANSACTION_CONFIRMATIONS ahead // we are swapping the main chain - if prev_height + STABLE_TRANSACTION_CONFIRMATIONS - < current_height { + if prev_height + STABLE_TRANSACTION_CONFIRMATIONS < current_height { Self::swap_main_blockchain(&fork)?; // announce the new main chain @@ -863,9 +859,9 @@ impl Module { )); } else { Self::deposit_event(Event::ForkAheadOfMainChain( - prev_height, // main chain height + prev_height, // main chain height fork.max_height, // fork height - fork.chain_id, // fork id + fork.chain_id, // fork id )); } // break the while loop @@ -908,9 +904,7 @@ impl Module { // NOTE: we never want to insert a new main chain through this function for (curr_position, curr_chain_id) in chains.iter().skip(1) { // get the height of the current chain_id - let curr_height = Self::get_block_chain_from_id( - curr_chain_id.clone()) - .max_height; + let curr_height = Self::get_block_chain_from_id(*curr_chain_id).max_height; // if the height of the current blockchain is lower than // the new blockchain, it should be inserted at that position @@ -921,13 +915,12 @@ impl Module { } // insert the new fork into the chains element - Self::set_chain_from_position_and_id( - max_chain_element, blockchain.chain_id); + Self::set_chain_from_position_and_id(max_chain_element, blockchain.chain_id); // starting from the last element swap the positions until // the new blockchain is at the position_blockchain // print!("max element {:?}\n", max_chain_element); // print!("position blockchain {:?}\n", position_blockchain); - for curr_position in (position_blockchain+1..max_chain_element+1).rev() { + for curr_position in (position_blockchain + 1..max_chain_element + 1).rev() { // stop when the blockchain element is at it's // designated position // print!("current position {:?}\n", curr_position); @@ -940,15 +933,14 @@ impl Module { Self::swap_chain(curr_position, prev_position); } } - /// Flag an error in a block header. This function is called by the + /// Flag an error in a block header. This function is called by the /// security pallet. /// /// # Arguments /// /// * `block_hash` - the hash of the block header with the error /// * `error` - the error code for the block header - pub fn flag_block_error(block_hash: H256Le, error: ErrorCode) - -> Result<(), Error> { + pub fn flag_block_error(block_hash: H256Le, error: ErrorCode) -> Result<(), Error> { // Get the chain id of the block header let block_header = Self::get_block_header_from_hash(block_hash)?; let chain_id = block_header.chain_ref; @@ -959,12 +951,8 @@ impl Module { // Flag errors in the blockchain entry // Check which error we are dealing with let newly_flagged = match error { - ErrorCode::NoDataBTCRelay => blockchain - .no_data - .insert(block_header.block_height), - ErrorCode::InvalidBTCRelay => blockchain - .invalid - .insert(block_header.block_height), + ErrorCode::NoDataBTCRelay => blockchain.no_data.insert(block_header.block_height), + ErrorCode::InvalidBTCRelay => blockchain.invalid.insert(block_header.block_height), _ => return Err(Error::UnknownErrorcode), }; @@ -974,18 +962,17 @@ impl Module { Self::deposit_event(Event::FlagBlockError(block_hash, chain_id, error)); } - Ok (()) + Ok(()) } - /// Clear an error from a block header. This function is called by the + /// Clear an error from a block header. This function is called by the /// security pallet. /// /// # Arguments /// /// * `block_hash` - the hash of the block header being cleared /// * `error` - the error code for the block header - pub fn clear_block_error(block_hash: H256Le, error: ErrorCode) - -> Result<(), Error> { + pub fn clear_block_error(block_hash: H256Le, error: ErrorCode) -> Result<(), Error> { // Get the chain id of the block header let block_header = Self::get_block_header_from_hash(block_hash)?; let chain_id = block_header.chain_ref; @@ -996,12 +983,8 @@ impl Module { // Clear errors in the blockchain entry // Check which error we are dealing with let block_exists = match error { - ErrorCode::NoDataBTCRelay => { - blockchain.no_data.remove(&block_header.block_height) - }, - ErrorCode::InvalidBTCRelay => { - blockchain.invalid.remove(&block_header.block_height) - }, + ErrorCode::NoDataBTCRelay => blockchain.no_data.remove(&block_header.block_height), + ErrorCode::InvalidBTCRelay => blockchain.invalid.remove(&block_header.block_height), _ => return Err(Error::UnknownErrorcode), }; @@ -1009,45 +992,49 @@ impl Module { // Store the updated blockchain entry Self::mutate_block_chain_from_id(chain_id, blockchain); - Self::deposit_event( - Event::ClearBlockError(block_hash, chain_id, error) - ); + Self::deposit_event(Event::ClearBlockError(block_hash, chain_id, error)); } - Ok (()) + Ok(()) } /// Checks if the given transaction confirmations are greater/equal to the /// requested confirmations (and/or the global k security parameter) - /// + /// /// # Arguments /// * `block_height` - current main chain block height /// * `req_confs` - confirmations requested by the caller /// * `tx_block_height` - block height of checked transaction - /// * `insecure` - determines if checks against recommended global transaction confirmation are to be executed. Recommended: set to `true` - /// - pub fn check_confirmations(main_chain_height: u32, req_confs: u32, tx_block_height: u32, insecure: bool) -> Result<(),Error> { + /// * `insecure` - determines if checks against recommended global transaction confirmation are to be executed. Recommended: set to `true` + /// + pub fn check_confirmations( + main_chain_height: u32, + req_confs: u32, + tx_block_height: u32, + insecure: bool, + ) -> Result<(), Error> { // insecure call: only checks against user parameter if insecure { - match tx_block_height + req_confs <= main_chain_height { - true => Ok(()), - false => Err(Error::Confirmations) + if tx_block_height + req_confs <= main_chain_height { + Ok(()) + } else { + Err(Error::Confirmations) } } else { // secure call: checks against max of user- and global security parameter let global_confs = Self::get_stable_transaction_confirmations(); if global_confs > req_confs { - match tx_block_height + global_confs <= main_chain_height { - true => Ok(()), - false => Err(Error::InsufficientStableConfirmations) + if tx_block_height + global_confs <= main_chain_height { + Ok(()) + } else { + Err(Error::InsufficientStableConfirmations) } + } else if tx_block_height + req_confs <= main_chain_height { + Ok(()) } else { - match tx_block_height + req_confs <= main_chain_height { - true => Ok(()), - false => Err(Error::Confirmations) - } - } + Err(Error::Confirmations) + } } } } diff --git a/crates/btc-relay/src/tests.rs b/crates/btc-relay/src/tests.rs index 570e9193cf..92a2693230 100644 --- a/crates/btc-relay/src/tests.rs +++ b/crates/btc-relay/src/tests.rs @@ -6,7 +6,6 @@ use bitcoin::parser::*; use bitcoin::types::*; use frame_support::{assert_err, assert_ok}; use security::ErrorCode; -use sp_std::collections::btree_map::BTreeMap; use sp_std::collections::btree_set::BTreeSet; use mocktopus::mocking::*; @@ -40,7 +39,7 @@ fn get_block_header_from_hash_succeeds() { run_test(|| { let chain_ref: u32 = 2; let block_height: u32 = 100; - let block_header = hex::decode(sample_block_header()).unwrap(); + let block_header = hex::decode(sample_block_header_hex()).unwrap(); let rich_header = RichBlockHeader { block_hash: H256Le::zero(), @@ -95,8 +94,8 @@ fn get_block_chain_from_id_succeeds() { fn initialize_once_succeeds() { run_test(|| { let block_height: u32 = 1; - let block_header = hex::decode(sample_block_header()).unwrap(); - let block_header_hash = BlockHeader::block_hash_le(&block_header); + let block_header = RawBlockHeader::from_hex(sample_block_header_hex()).unwrap(); + let block_header_hash = block_header.hash(); BTCRelay::best_block_exists.mock_safe(|| MockResult::Return(false)); assert_ok!(BTCRelay::initialize( @@ -115,12 +114,12 @@ fn initialize_once_succeeds() { fn initialize_best_block_already_set_fails() { run_test(|| { let block_height: u32 = 1; - let block_header = hex::decode(sample_block_header()).unwrap(); + let raw_block_header = RawBlockHeader::from_hex(sample_block_header_hex()).unwrap(); BTCRelay::best_block_exists.mock_safe(|| MockResult::Return(true)); assert_err!( - BTCRelay::initialize(Origin::signed(3), block_header, block_height), + BTCRelay::initialize(Origin::signed(3), raw_block_header, block_height), Error::AlreadyInitialized ); }) @@ -130,18 +129,19 @@ fn initialize_best_block_already_set_fails() { #[test] fn store_block_header_on_mainchain_succeeds() { run_test(|| { - BTCRelay::verify_block_header - .mock_safe(|h| MockResult::Return(Ok(BlockHeader::from_le_bytes(&h).unwrap()))); + BTCRelay::verify_block_header.mock_safe(|h| { + MockResult::Return(Ok(BlockHeader::from_le_bytes(h.as_slice()).unwrap())) + }); BTCRelay::block_header_exists.mock_safe(|_| MockResult::Return(true)); let chain_ref: u32 = 0; let start_height: u32 = 0; let block_height: u32 = 100; - let block_header = hex::decode(sample_block_header()).unwrap(); + let block_header = RawBlockHeader::from_hex(sample_block_header_hex()).unwrap(); let rich_header = RichBlockHeader { block_hash: H256Le::zero(), - block_header: BlockHeader::from_le_bytes(&block_header).unwrap(), + block_header: parse_block_header(&block_header).unwrap(), block_height: block_height, chain_ref: chain_ref, }; @@ -154,7 +154,7 @@ fn store_block_header_on_mainchain_succeeds() { BTCRelay::get_block_chain_from_id .mock_safe(move |_: u32| MockResult::Return(prev_blockchain.clone())); - let block_header_hash = BlockHeader::block_hash_le(&block_header); + let block_header_hash = block_header.hash(); assert_ok!(BTCRelay::store_block_header( Origin::signed(3), block_header @@ -171,18 +171,17 @@ fn store_block_header_on_mainchain_succeeds() { #[test] fn store_block_header_on_fork_succeeds() { run_test(|| { - BTCRelay::verify_block_header - .mock_safe(|h| MockResult::Return(Ok(BlockHeader::from_le_bytes(&h).unwrap()))); + BTCRelay::verify_block_header.mock_safe(|h| MockResult::Return(parse_block_header(&h))); BTCRelay::block_header_exists.mock_safe(|_| MockResult::Return(true)); let chain_ref: u32 = 1; let start_height: u32 = 20; let block_height: u32 = 100; - let block_header = hex::decode(sample_block_header()).unwrap(); + let block_header = RawBlockHeader::from_hex(sample_block_header_hex()).unwrap(); let rich_header = RichBlockHeader { block_hash: H256Le::zero(), - block_header: BlockHeader::from_le_bytes(&block_header).unwrap(), + block_header: parse_block_header(&block_header).unwrap(), block_height: block_height - 1, chain_ref: chain_ref, }; @@ -194,7 +193,7 @@ fn store_block_header_on_fork_succeeds() { BTCRelay::get_block_chain_from_id .mock_safe(move |_: u32| MockResult::Return(prev_blockchain.clone())); - let block_header_hash = BlockHeader::block_hash_le(&block_header); + let block_header_hash = block_header.hash(); assert_ok!(BTCRelay::store_block_header( Origin::signed(3), block_header @@ -491,19 +490,17 @@ fn swap_main_blockchain_succeeds() { // swap the main and fork assert_ok!(BTCRelay::swap_main_blockchain(&fork)); - let mut main_chain_map = main.chain.clone(); - for (height, hash) in fork.chain.iter() { - main_chain_map.insert(height.clone(), hash.clone()); - } + // check that the new main chain is correct let new_main = BTCRelay::get_block_chain_from_id(main_chain_ref); assert_eq!(fork_height, new_main.max_height); assert_eq!(main_start, new_main.start_height); assert_eq!(main_chain_ref, new_main.chain_id); - assert_eq!(main_chain_map.len(), new_main.chain.len()); - for (height, _hash) in new_main.chain.iter() { - assert_eq!(main_chain_map.get(height), new_main.chain.get(height)); - } + assert_eq!( + fork_height + 1, + BTCRelay::_blocks_count(main_chain_ref) as u32 + ); + assert_eq!(main.no_data, new_main.no_data); assert_eq!(main.invalid, new_main.invalid); @@ -516,36 +513,37 @@ fn swap_main_blockchain_succeeds() { assert_eq!(main_height, old_main.max_height); assert_eq!(fork_start, old_main.start_height); assert_eq!(old_main_ref, old_main.chain_id); - assert_eq!(main_height - fork_start + 1, old_main.chain.len() as u32); - for (height, _hash) in old_main.chain.iter() { - assert_eq!(main.chain.get(height), old_main.chain.get(height)); - } + let old_main_length = BTCRelay::_blocks_count(old_main.chain_id); + assert_eq!(main_height - fork_start + 1, old_main_length as u32); + assert_eq!(main.no_data, old_main.no_data); assert_eq!(main.invalid, old_main.invalid); // check that the best block is set assert_eq!( - fork.chain.get(&fork_height), - Some(&BTCRelay::get_best_block()) + BTCRelay::get_block_hash(new_main.chain_id, fork_height).unwrap(), + BTCRelay::get_best_block() ); // check that the best block height is correct assert_eq!(fork_height, BTCRelay::get_best_block_height()); // check that all fork headers are updated - for (_height, hash) in fork.chain.iter() { - let header = BTCRelay::get_block_header_from_hash(hash.clone()).unwrap(); + for height in fork_start..=fork_height { + let block_hash = BTCRelay::get_block_hash(main_chain_ref, height).unwrap(); + let header = BTCRelay::get_block_header_from_hash(block_hash).unwrap(); assert_eq!(header.chain_ref, main_chain_ref); } // check that all main headers are updated - for (_height, hash) in main.chain.iter().skip(fork_start as usize) { - let header = BTCRelay::get_block_header_from_hash(hash.clone()).unwrap(); + for height in fork_start..=main_height { + let block_hash = BTCRelay::get_block_hash(old_main_ref, height).unwrap(); + let header = BTCRelay::get_block_header_from_hash(block_hash).unwrap(); assert_eq!(header.chain_ref, old_main_ref); } }) } -/// verify_block_header +/// verify_block_header #[test] fn test_verify_block_header_no_retarget_succeeds() { run_test(|| { @@ -554,8 +552,7 @@ fn test_verify_block_header_no_retarget_succeeds() { let block_height: u32 = 100; let genesis_header = sample_parsed_genesis_header(chain_ref, block_height); - let raw_first_header = - header_from_bytes(&(hex::decode(sample_raw_first_header()).unwrap())); + let raw_first_header = RawBlockHeader::from_hex(sample_raw_first_header()).unwrap(); let rich_first_header = sample_parsed_first_block(chain_ref, block_height + 1); // Prev block is genesis @@ -564,7 +561,7 @@ fn test_verify_block_header_no_retarget_succeeds() { // Not duplicate block BTCRelay::block_header_exists.mock_safe(move |_| MockResult::Return(false)); - let verified_header = BTCRelay::verify_block_header(raw_first_header).unwrap(); + let verified_header = BTCRelay::verify_block_header(&raw_first_header).unwrap(); assert_eq!(verified_header, rich_first_header.block_header) }) } @@ -578,13 +575,10 @@ fn test_verify_block_header_correct_retarget_increase_succeeds() { // Sample interval with INCREASING target let retarget_headers = sample_retarget_interval_increase(); - let prev_block_header_rich = RichBlockHeader::construct_rich_block_header( - retarget_headers[1], - chain_ref, - block_height, - ).unwrap(); + let prev_block_header_rich = + RichBlockHeader::construct(retarget_headers[1], chain_ref, block_height).unwrap(); - let curr_block_header = BlockHeader::from_le_bytes(&retarget_headers[2]).unwrap(); + let curr_block_header = parse_block_header(&retarget_headers[2]).unwrap(); // Prev block exists BTCRelay::get_block_header_from_hash .mock_safe(move |_| MockResult::Return(Ok(prev_block_header_rich))); @@ -594,7 +588,7 @@ fn test_verify_block_header_correct_retarget_increase_succeeds() { BTCRelay::compute_new_target .mock_safe(move |_, _| MockResult::Return(Ok(curr_block_header.target))); - let verified_header = BTCRelay::verify_block_header(retarget_headers[2]).unwrap(); + let verified_header = BTCRelay::verify_block_header(&retarget_headers[2]).unwrap(); assert_eq!(verified_header, curr_block_header) }) } @@ -608,13 +602,10 @@ fn test_verify_block_header_correct_retarget_decrease_succeeds() { // Sample interval with DECREASING target let retarget_headers = sample_retarget_interval_decrease(); - let prev_block_header_rich = RichBlockHeader::construct_rich_block_header( - retarget_headers[1], - chain_ref, - block_height, - ).unwrap(); + let prev_block_header_rich = + RichBlockHeader::construct(retarget_headers[1], chain_ref, block_height).unwrap(); - let curr_block_header = BlockHeader::from_le_bytes(&retarget_headers[2]).unwrap(); + let curr_block_header = parse_block_header(&retarget_headers[2]).unwrap(); // Prev block exists BTCRelay::get_block_header_from_hash .mock_safe(move |_| MockResult::Return(Ok(prev_block_header_rich))); @@ -624,7 +615,7 @@ fn test_verify_block_header_correct_retarget_decrease_succeeds() { BTCRelay::compute_new_target .mock_safe(move |_, _| MockResult::Return(Ok(curr_block_header.target))); - let verified_header = BTCRelay::verify_block_header(retarget_headers[2]).unwrap(); + let verified_header = BTCRelay::verify_block_header(&retarget_headers[2]).unwrap(); assert_eq!(verified_header, curr_block_header) }) } @@ -637,13 +628,10 @@ fn test_verify_block_header_missing_retarget_succeeds() { let block_height: u32 = 2015; let retarget_headers = sample_retarget_interval_increase(); - let prev_block_header_rich = RichBlockHeader::construct_rich_block_header( - retarget_headers[1], - chain_ref, - block_height, - ).unwrap(); + let prev_block_header_rich = + RichBlockHeader::construct(retarget_headers[1], chain_ref, block_height).unwrap(); - let curr_block_header = BlockHeader::from_le_bytes(&retarget_headers[2]).unwrap(); + let curr_block_header = parse_block_header(&retarget_headers[2]).unwrap(); // Prev block exists BTCRelay::get_block_header_from_hash .mock_safe(move |_| MockResult::Return(Ok(prev_block_header_rich))); @@ -654,7 +642,7 @@ fn test_verify_block_header_missing_retarget_succeeds() { .mock_safe(move |_, _| MockResult::Return(Ok(curr_block_header.target + 1))); assert_err!( - BTCRelay::verify_block_header(retarget_headers[2]), + BTCRelay::verify_block_header(&retarget_headers[2]), Error::DiffTargetHeader ); }) @@ -667,11 +655,11 @@ fn test_compute_new_target() { let block_height: u32 = 2016; let retarget_headers = sample_retarget_interval_increase(); - let last_retarget_time = BlockHeader::from_le_bytes(&retarget_headers[0]).unwrap().timestamp; + let last_retarget_time = parse_block_header(&retarget_headers[0]).unwrap().timestamp; let prev_block_header = - RichBlockHeader::construct_rich_block_header(retarget_headers[1], chain_ref, block_height).unwrap(); + RichBlockHeader::construct(retarget_headers[1], chain_ref, block_height).unwrap(); - let curr_block_header = BlockHeader::from_le_bytes(&retarget_headers[2]).unwrap(); + let curr_block_header = parse_block_header(&retarget_headers[2]).unwrap(); BTCRelay::get_last_retarget_time .mock_safe(move |_, _| MockResult::Return(Ok(last_retarget_time))); @@ -700,10 +688,9 @@ fn test_verify_block_header_duplicate_fails() { MockResult::Return(true) }); - let raw_first_header = - header_from_bytes(&(hex::decode(sample_raw_first_header()).unwrap())); + let raw_first_header = RawBlockHeader::from_hex(sample_raw_first_header()).unwrap(); assert_err!( - BTCRelay::verify_block_header(raw_first_header), + BTCRelay::verify_block_header(&raw_first_header), Error::DuplicateBlock ); }) @@ -718,10 +705,9 @@ fn test_verify_block_header_no_prev_block_fails() { // submitted block does not yet exist BTCRelay::block_header_exists.mock_safe(move |_| MockResult::Return(false)); - let raw_first_header = - header_from_bytes(&(hex::decode(sample_raw_first_header()).unwrap())); + let raw_first_header = RawBlockHeader::from_hex(sample_raw_first_header()).unwrap(); assert_err!( - BTCRelay::verify_block_header(raw_first_header), + BTCRelay::verify_block_header(&raw_first_header), Error::PrevBlock ); }) @@ -737,7 +723,7 @@ fn test_verify_block_header_low_diff_fails() { // block header with high target but weak hash let raw_first_header_weak = - header_from_bytes(&(hex::decode(sample_raw_first_header_low_diff()).unwrap())); + RawBlockHeader::from_hex(sample_raw_first_header_low_diff()).unwrap(); // Prev block is genesis BTCRelay::get_block_header_from_hash @@ -746,7 +732,7 @@ fn test_verify_block_header_low_diff_fails() { BTCRelay::block_header_exists.mock_safe(move |_| MockResult::Return(false)); assert_err!( - BTCRelay::verify_block_header(raw_first_header_weak), + BTCRelay::verify_block_header(&raw_first_header_weak), Error::LowDiff ); }); @@ -926,7 +912,7 @@ fn test_flag_block_error_succeeds() { let chain_ref: u32 = 1; let start_height: u32 = 10; let block_height: u32 = 100; - let block_header = hex::decode(sample_block_header()).unwrap(); + let block_header = hex::decode(sample_block_header_hex()).unwrap(); let rich_header = RichBlockHeader { block_hash: H256Le::zero(), @@ -971,7 +957,7 @@ fn test_flag_block_error_fails() { let chain_ref: u32 = 1; let start_height: u32 = 20; let block_height: u32 = 100; - let block_header = hex::decode(sample_block_header()).unwrap(); + let block_header = hex::decode(sample_block_header_hex()).unwrap(); let rich_header = RichBlockHeader { block_hash: H256Le::zero(), @@ -1003,7 +989,7 @@ fn test_clear_block_error_succeeds() { let chain_ref: u32 = 1; let start_height: u32 = 15; let block_height: u32 = 100; - let block_header = hex::decode(sample_block_header()).unwrap(); + let block_header = hex::decode(sample_block_header_hex()).unwrap(); let rich_header = RichBlockHeader { block_hash: H256Le::zero(), @@ -1051,7 +1037,7 @@ fn test_clear_block_error_fails() { let chain_ref: u32 = 1; let start_height: u32 = 20; let block_height: u32 = 100; - let block_header = hex::decode(sample_block_header()).unwrap(); + let block_header = hex::decode(sample_block_header_hex()).unwrap(); let rich_header = RichBlockHeader { block_hash: H256Le::zero(), @@ -1423,11 +1409,8 @@ fn get_empty_block_chain_from_chain_id_and_height( start_height: u32, block_height: u32, ) -> BlockChain { - let chain = BTreeMap::new(); - let blockchain = BlockChain { chain_id: chain_id, - chain: chain, start_height: start_height, max_height: block_height, no_data: BTreeSet::new(), @@ -1447,7 +1430,7 @@ fn store_blockchain_and_random_headers( // create and insert main chain headers for height in chain.start_height..chain.max_height + 1 { - let block_header = hex::decode(sample_block_header()).unwrap(); + let block_header = hex::decode(sample_block_header_hex()).unwrap(); let mut fake_block = height.to_be_bytes().repeat(7); fake_block.append(&mut id.to_be_bytes().to_vec()); let block_hash = H256Le::from_bytes_be(fake_block.as_slice()); @@ -1474,21 +1457,21 @@ fn sample_raw_genesis_header() -> String { } fn sample_parsed_genesis_header(chain_ref: u32, block_height: u32) -> RichBlockHeader { - let genesis_header = hex::decode(sample_raw_genesis_header()).unwrap(); + let genesis_header = RawBlockHeader::from_hex(sample_raw_genesis_header()).unwrap(); RichBlockHeader { - block_hash: BlockHeader::block_hash_le(&genesis_header), - block_header: BlockHeader::from_le_bytes(&genesis_header).unwrap(), + block_hash: genesis_header.hash(), + block_header: parse_block_header(&genesis_header).unwrap(), block_height: block_height, chain_ref: chain_ref, } } fn sample_raw_first_header_low_diff() -> String { - "01000000".to_owned() + + "01000000".to_owned() + "cb60e68ead74025dcfd4bf4673f3f71b1e678be9c6e6585f4544c79900000000" + - "c7f42be7f83eddf2005272412b01204352a5fddbca81942c115468c3c4ec2fff" + - "827ad949" + - "413b1417" + // high target + "c7f42be7f83eddf2005272412b01204352a5fddbca81942c115468c3c4ec2fff" + + "827ad949" + + "413b1417" + // high target "21e05e45" } @@ -1497,10 +1480,10 @@ fn sample_raw_first_header() -> String { } fn sample_parsed_first_block(chain_ref: u32, block_height: u32) -> RichBlockHeader { - let block_header = hex::decode(sample_raw_first_header()).unwrap(); + let block_header = RawBlockHeader::from_hex(sample_raw_first_header()).unwrap(); RichBlockHeader { - block_hash: BlockHeader::block_hash_le(&block_header), - block_header: BlockHeader::from_le_bytes(&block_header).unwrap(), + block_hash: block_header.hash(), + block_header: parse_block_header(&block_header).unwrap(), block_height: block_height, chain_ref: chain_ref, } @@ -1508,22 +1491,22 @@ fn sample_parsed_first_block(chain_ref: u32, block_height: u32) -> RichBlockHead fn sample_retarget_interval_increase() -> [RawBlockHeader; 3] { // block height 66528 - let last_retarget_header = header_from_bytes(&hex::decode("01000000".to_owned() + "4e8e5cf3c4e4b8f63a9cf88beb2dbaba1949182101ae4e5cf54ad100000000009f2a2344e8112b0d7bd8089414106ee5f17bb6cd64078883e1b661fa251aac6bed1d3c4cf4a3051c4dcd2b02").unwrap()); + let last_retarget_header = RawBlockHeader::from_hex("01000000".to_owned() + "4e8e5cf3c4e4b8f63a9cf88beb2dbaba1949182101ae4e5cf54ad100000000009f2a2344e8112b0d7bd8089414106ee5f17bb6cd64078883e1b661fa251aac6bed1d3c4cf4a3051c4dcd2b02").unwrap(); // block height 66543 - let prev_block_header = header_from_bytes(&hex::decode("01000000".to_owned() + "1e321d88cb25946c4ca521eece3752803c021f9403fc4e0171203a0500000000317057f8b50414848a5a3a26d9eb8ace3d6f5495df456d0104dd1421159faf5029293c4cf4a3051c73199005").unwrap()); + let prev_block_header = RawBlockHeader::from_hex("01000000".to_owned() + "1e321d88cb25946c4ca521eece3752803c021f9403fc4e0171203a0500000000317057f8b50414848a5a3a26d9eb8ace3d6f5495df456d0104dd1421159faf5029293c4cf4a3051c73199005").unwrap(); // block height 68544 - let curr_header = header_from_bytes(&hex::decode("01000000".to_owned() + "fb57c71ccd211b3de4ccc2e23b50a7cdb72aab91e60737b3a2bfdf030000000088a88ad9df68925e880e5d52b7e50cef225871c68b40a2cd0bca1084cd436037f388404cfd68011caeb1f801").unwrap()); + let curr_header = RawBlockHeader::from_hex("01000000".to_owned() + "fb57c71ccd211b3de4ccc2e23b50a7cdb72aab91e60737b3a2bfdf030000000088a88ad9df68925e880e5d52b7e50cef225871c68b40a2cd0bca1084cd436037f388404cfd68011caeb1f801").unwrap(); [last_retarget_header, prev_block_header, curr_header] } fn sample_retarget_interval_decrease() -> [RawBlockHeader; 3] { // block height 558432 - let last_retarget_header = header_from_bytes(&hex::decode("00c0ff2f".to_owned() + "6550b5dae76559589e3e3e135237072b6bc498949da6280000000000000000005988783435f506d2ccfbadb484e56d6f1d5dfdd480650acae1e3b43d3464ea73caf13b5c33d62f171d508fdb").unwrap()); + let last_retarget_header = RawBlockHeader::from_hex("00c0ff2f".to_owned() + "6550b5dae76559589e3e3e135237072b6bc498949da6280000000000000000005988783435f506d2ccfbadb484e56d6f1d5dfdd480650acae1e3b43d3464ea73caf13b5c33d62f171d508fdb").unwrap(); // block height 560447 - let prev_block_header = header_from_bytes(&hex::decode("00000020".to_owned() + "d8e8e54ca5e33522b94fbba5de736efc55ff75e832cf2300000000000000000007b395f80858ee022c9c3c2f0f5cee4bd807039f0729b0559ae4326c3ba77d6b209f4e5c33d62f1746ee356d").unwrap()); + let prev_block_header = RawBlockHeader::from_hex("00000020".to_owned() + "d8e8e54ca5e33522b94fbba5de736efc55ff75e832cf2300000000000000000007b395f80858ee022c9c3c2f0f5cee4bd807039f0729b0559ae4326c3ba77d6b209f4e5c33d62f1746ee356d").unwrap(); // block height 560448 - let curr_header = header_from_bytes(&hex::decode("00000020".to_owned() + "6b05bd2c4a06b3d8503a033c2593396a25a79e1dcadb140000000000000000001b08df3d42cd9a38d8b66adf9dc5eb464f503633bd861085ffff723634531596a1a24e5c35683017bf67b72a").unwrap()); + let curr_header = RawBlockHeader::from_hex("00000020".to_owned() + "6b05bd2c4a06b3d8503a033c2593396a25a79e1dcadb140000000000000000001b08df3d42cd9a38d8b66adf9dc5eb464f503633bd861085ffff723634531596a1a24e5c35683017bf67b72a").unwrap(); [last_retarget_header, prev_block_header, curr_header] } @@ -1532,7 +1515,7 @@ fn sample_accepted_transaction() -> String { "020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0502cb000101ffffffff02400606950000000017a91466c7060feb882664ae62ffad0051fe843e318e85870000000000000000266a24aa21a9ede5c17d15b8b1fa2811b7e6da66ffa5e1aaa05922c69068bf90cd585b95bb46750120000000000000000000000000000000000000000000000000000000000000000000000000".to_owned() } -fn sample_block_header() -> String { +fn sample_block_header_hex() -> String { "02000000".to_owned() + // ............... Block version: 2 "b6ff0b1b1680a2862a30ca44d346d9e8" + // "910d334beb48ca0c0000000000000000" + // ... Hash of previous block's header @@ -1544,10 +1527,10 @@ fn sample_block_header() -> String { } fn sample_rich_tx_block_header(chain_ref: u32, block_height: u32) -> RichBlockHeader { - let raw_header = hex::decode("0000003096cb3d93696c4f56c10da153963d35abf4692c07b2b3bf0702fb4cb32a8682211ee1fb90996ca1d5dcd12866ba9066458bf768641215933d7d8b3a10ef79d090e8a13a5effff7f2005000000".to_owned()).unwrap(); + let raw_header = RawBlockHeader::from_hex("0000003096cb3d93696c4f56c10da153963d35abf4692c07b2b3bf0702fb4cb32a8682211ee1fb90996ca1d5dcd12866ba9066458bf768641215933d7d8b3a10ef79d090e8a13a5effff7f2005000000".to_owned()).unwrap(); RichBlockHeader { - block_hash: BlockHeader::block_hash_le(&raw_header), - block_header: BlockHeader::from_le_bytes(&raw_header).unwrap(), + block_hash: raw_header.hash(), + block_header: parse_block_header(&raw_header).unwrap(), block_height: block_height, chain_ref: chain_ref, } diff --git a/crates/collateral/Cargo.toml b/crates/collateral/Cargo.toml new file mode 100644 index 0000000000..8706284280 --- /dev/null +++ b/crates/collateral/Cargo.toml @@ -0,0 +1,56 @@ +[package] +authors = ['Interlay'] +description = 'Collateral module' +edition = '2018' +homepage = 'https://interlay.gitlab.io/polkabtc-spec/spec/collateral.html' +name = 'collateral' +version = '0.1.0' + +[features] +default = ['std'] +std = [ + 'codec/std', + 'frame-support/std', + 'safe-mix/std', + 'system/std', + 'pallet-balances/std', +] + +[dependencies.codec] +default-features = false +features = ['derive'] +package = 'parity-scale-codec' +version = '1.0.0' + +[dependencies.frame-support] +default-features = false +version = '2.0.0-alpha.5' + +[dependencies.safe-mix] +default-features = false +version = '1.0.0' + +[dependencies.sp-core] +default-features = false +version = '2.0.0-alpha.5' + +[dependencies.sp-io] +default-features = false +version = '2.0.0-alpha.5' + +[dependencies.sp-runtime] +default-features = false +version = '2.0.0-alpha.5' + +[dependencies.system] +default-features = false +package = 'frame-system' +version = '2.0.0-alpha.5' + +[dependencies.pallet-balances] +default-features = false +package = 'pallet-balances' +version = '2.0.0-alpha.5' + +[dependencies.xclaim-core] +path = '../xclaim-core' diff --git a/crates/collateral/src/lib.rs b/crates/collateral/src/lib.rs new file mode 100644 index 0000000000..ecbe884a9a --- /dev/null +++ b/crates/collateral/src/lib.rs @@ -0,0 +1,152 @@ +#![deny(warnings)] +#![cfg_attr(test, feature(proc_macro_hygiene))] +#![cfg_attr(not(feature = "std"), no_std)] +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +use frame_support::traits::{Currency, ReservableCurrency}; +/// The Collateral module according to the specification at +/// https://interlay.gitlab.io/polkabtc-spec/spec/collateral.html +use frame_support::{decl_event, decl_module, decl_storage, ensure}; +use sp_runtime::ModuleId; + +use xclaim_core::Error; + +type BalanceOf = <::DOT as Currency<::AccountId>>::Balance; + +/// The collateral's module id, used for deriving its sovereign account ID. +const _MODULE_ID: ModuleId = ModuleId(*b"ily/cltl"); + +/// The pallet's configuration trait. +pub trait Trait: system::Trait { + /// The DOT currency + type DOT: Currency + ReservableCurrency; + + /// The overarching event type. + type Event: From> + Into<::Event>; +} + +// This pallet's storage items. +decl_storage! { + trait Store for Module as Collateral { + /// ## Storage + /// Note that account's balances and locked balances are handled + /// through the Balances module. + /// + /// Total locked DOT collateral + TotalCollateral: BalanceOf; + } +} + +// The pallet's events +decl_event!( + pub enum Event + where + AccountId = ::AccountId, + Balance = BalanceOf, + { + LockCollateral(AccountId, Balance), + ReleaseCollateral(AccountId, Balance), + SlashCollateral(AccountId, AccountId, Balance), + } +); + +decl_module! { + /// The module declaration. + pub struct Module for enum Call where origin: T::Origin { + // Initializing events + fn deposit_event() = default; + } +} + +impl Module { + /// Total supply of DOT + pub fn get_total_supply() -> BalanceOf { + T::DOT::total_issuance() + } + /// Total locked DOT collateral + pub fn get_total_collateral() -> BalanceOf { + >::get() + } + /// Increase the locked collateral + pub fn increase_total_collateral(amount: BalanceOf) { + let new_collateral = Self::get_total_collateral() + amount; + >::put(new_collateral); + } + /// Decrease the locked collateral + pub fn decrease_total_collateral(amount: BalanceOf) { + let new_collateral = Self::get_total_collateral() - amount; + >::put(new_collateral); + } + /// Locked balance of account + pub fn get_collateral_from_account(account: T::AccountId) -> BalanceOf { + T::DOT::reserved_balance(&account) + } + /// Lock DOT collateral + /// + /// # Arguments + /// + /// * `sender` - the account locking tokens + /// * `amount` - to be locked amount of DOT + pub fn lock_collateral(sender: T::AccountId, amount: BalanceOf) -> Result<(), Error> { + T::DOT::reserve(&sender, amount).map_err(|_| Error::InsufficientFunds)?; + + Self::increase_total_collateral(amount); + + Self::deposit_event(RawEvent::LockCollateral(sender, amount)); + Ok(()) + } + /// Release DOT collateral + /// + /// # Arguments + /// + /// * `sender` - the account releasing tokens + /// * `amount` - the to be released amount of DOT + pub fn release_collateral(sender: T::AccountId, amount: BalanceOf) -> Result<(), Error> { + ensure!( + T::DOT::reserved_balance(&sender) >= amount, + Error::InsufficientCollateralAvailable + ); + T::DOT::unreserve(&sender, amount); + + Self::decrease_total_collateral(amount); + + Self::deposit_event(RawEvent::ReleaseCollateral(sender, amount)); + + Ok(()) + } + /// Slash DOT collateral and assign to a receiver. Can only fail if + /// the sender account has too low collateral. + /// + /// # Arguments + /// + /// * `sender` - the account being slashed + /// * `receiver` - the receiver of the amount + /// * `amount` - the to be slashed amount + pub fn slash_collateral( + sender: T::AccountId, + receiver: T::AccountId, + amount: BalanceOf, + ) -> Result<(), Error> { + ensure!( + T::DOT::reserved_balance(&sender) >= amount, + Error::InsufficientCollateralAvailable + ); + + // slash the sender's collateral + let (slashed, _remainder) = T::DOT::slash_reserved(&sender, amount); + + // add slashed amount to receiver and create account if it does not exists + T::DOT::resolve_creating(&receiver, slashed); + + // reserve the created amount for the receiver + T::DOT::reserve(&receiver, amount).map_err(|_| Error::InsufficientFunds)?; + + Self::deposit_event(RawEvent::SlashCollateral(sender, receiver, amount)); + + Ok(()) + } +} diff --git a/crates/collateral/src/mock.rs b/crates/collateral/src/mock.rs new file mode 100644 index 0000000000..4c1cac09d4 --- /dev/null +++ b/crates/collateral/src/mock.rs @@ -0,0 +1,114 @@ +/// Mocking the test environment +use crate::{Module, Trait}; +use frame_support::{impl_outer_event, impl_outer_origin, parameter_types, weights::Weight}; +use pallet_balances as balances; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, IdentityLookup}, + Perbill, +}; + +impl_outer_origin! { + pub enum Origin for Test {} +} + +mod test_events { + pub use crate::Event; +} + +impl_outer_event! { + pub enum TestEvent for Test { + system, + test_events, + balances, + } +} + +// For testing the pallet, we construct most of a mock runtime. This means +// first constructing a configuration type (`Test`) which `impl`s each of the +// configuration traits of pallets we want to use. + +pub type AccountId = u64; +pub type Balance = u64; + +#[derive(Clone, Eq, PartialEq)] +pub struct Test; +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: Weight = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75); +} +impl system::Trait for Test { + type Origin = Origin; + type Call = (); + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type Event = TestEvent; + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; + type AvailableBlockRatio = AvailableBlockRatio; + type Version = (); + type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); +} +parameter_types! { + pub const ExistentialDeposit: u64 = 1; +} +impl pallet_balances::Trait for Test { + type Balance = Balance; + type Event = TestEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; +} + +impl Trait for Test { + type DOT = Balances; + type Event = TestEvent; +} + +pub type Error = crate::Error; + +pub type System = system::Module; +pub type Balances = pallet_balances::Module; +pub type Collateral = Module; + +pub const ALICE: AccountId = 1; +pub const BOB: AccountId = 2; +pub const ALICE_BALANCE: u64 = 1_000_000; +pub const BOB_BALANCE: u64 = 1_000_000; + +pub struct ExtBuilder; + +impl ExtBuilder { + pub fn build() -> sp_io::TestExternalities { + let mut storage = system::GenesisConfig::default() + .build_storage::() + .unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![(ALICE, ALICE_BALANCE), (BOB, BOB_BALANCE)], + } + .assimilate_storage(&mut storage) + .unwrap(); + + storage.into() + } +} + +pub fn run_test(test: T) -> () +where + T: FnOnce() -> (), +{ + ExtBuilder::build().execute_with(test); +} diff --git a/crates/collateral/src/tests.rs b/crates/collateral/src/tests.rs new file mode 100644 index 0000000000..64482bfec1 --- /dev/null +++ b/crates/collateral/src/tests.rs @@ -0,0 +1,241 @@ +/// Tests for Collateral +use crate::mock::*; +use crate::RawEvent; +use frame_support::{assert_err, assert_ok}; + +/// Total supply +#[test] +fn test_total_supply_correct() { + run_test(|| { + // initial supply + let desired_total = ALICE_BALANCE + BOB_BALANCE; + let total = Collateral::get_total_supply(); + + assert_eq!(desired_total, total); + }) +} + +/// Total collateral +#[test] +fn test_total_collateral_correct() { + run_test(|| { + // initial supply + let desired_total_collateral = 0; + let increase_amount: Balance = 5; + let decrease_amount: Balance = 3; + + let total_collateral = Collateral::get_total_collateral(); + assert_eq!(desired_total_collateral, total_collateral); + + Collateral::increase_total_collateral(increase_amount); + let increased_collateral = Collateral::get_total_collateral(); + assert_eq!(total_collateral + increase_amount, increased_collateral); + + Collateral::decrease_total_collateral(decrease_amount); + let decreased_collateral = Collateral::get_total_collateral(); + assert_eq!(increased_collateral - decrease_amount, decreased_collateral); + }) +} + +/// Lock collateral +#[test] +fn test_lock_collateral_succeeds() { + run_test(|| { + let sender = ALICE; + let amount: Balance = 5; + + let init_collateral = Collateral::get_collateral_from_account(ALICE); + let init_total = Collateral::get_total_collateral(); + + assert_ok!(Collateral::lock_collateral(sender, amount)); + let lock_event = TestEvent::test_events(RawEvent::LockCollateral(ALICE, amount)); + + assert!(System::events().iter().any(|a| a.event == lock_event)); + + let collateral = Collateral::get_collateral_from_account(ALICE); + let total = Collateral::get_total_collateral(); + + assert_eq!(collateral, init_collateral + amount); + assert_eq!(total, init_total + amount); + }) +} + +#[test] +fn test_lock_collateral_fails() { + run_test(|| { + let sender = ALICE; + let amount = ALICE_BALANCE + 5; + + let init_collateral = Collateral::get_collateral_from_account(ALICE); + let init_total = Collateral::get_total_collateral(); + + assert_err!( + Collateral::lock_collateral(sender, amount), + Error::InsufficientFunds + ); + + let collateral = Collateral::get_collateral_from_account(ALICE); + let total = Collateral::get_total_collateral(); + + assert_eq!(collateral, init_collateral); + assert_eq!(total, init_total); + }) +} + +/// Release collateral +#[test] +fn test_release_collateral_succeeds() { + run_test(|| { + let sender = ALICE; + let amount = ALICE_BALANCE; + + assert_ok!(Collateral::lock_collateral(sender, amount)); + + let init_collateral = Collateral::get_collateral_from_account(ALICE); + let init_total = Collateral::get_total_collateral(); + + assert_ok!(Collateral::release_collateral(sender, amount)); + let release_event = TestEvent::test_events(RawEvent::ReleaseCollateral(ALICE, amount)); + + assert!(System::events().iter().any(|a| a.event == release_event)); + + let collateral = Collateral::get_collateral_from_account(ALICE); + let total = Collateral::get_total_collateral(); + + assert_eq!(collateral, init_collateral - amount); + assert_eq!(total, init_total - amount); + }) +} + +#[test] +fn test_release_collateral_fails() { + run_test(|| { + let sender = ALICE; + let lock_amount = ALICE_BALANCE; + + let init_collateral = Collateral::get_collateral_from_account(ALICE); + let init_total = Collateral::get_total_collateral(); + + assert_err!( + Collateral::release_collateral(sender, lock_amount), + Error::InsufficientCollateralAvailable + ); + + let collateral = Collateral::get_collateral_from_account(ALICE); + let total = Collateral::get_total_collateral(); + + assert_eq!(collateral, init_collateral); + assert_eq!(total, init_total); + }) +} + +#[test] +fn test_release_collateral_partially_succeeds() { + run_test(|| { + let sender = ALICE; + let amount = ALICE_BALANCE; + let release_amount = ALICE_BALANCE - 10; + + assert_ok!(Collateral::lock_collateral(sender, amount)); + + let init_collateral = Collateral::get_collateral_from_account(ALICE); + let init_total = Collateral::get_total_collateral(); + + assert_ok!(Collateral::release_collateral(sender, release_amount)); + let release_event = + TestEvent::test_events(RawEvent::ReleaseCollateral(ALICE, release_amount)); + + assert!(System::events().iter().any(|a| a.event == release_event)); + + let collateral = Collateral::get_collateral_from_account(ALICE); + let total = Collateral::get_total_collateral(); + + assert_eq!(collateral, init_collateral - release_amount); + assert_eq!(total, init_total - release_amount); + }) +} + +/// Slash collateral +#[test] +fn test_slash_collateral_succeeds() { + run_test(|| { + let sender = ALICE; + let receiver = BOB; + let amount = ALICE_BALANCE; + + assert_ok!(Collateral::lock_collateral(sender, amount)); + + let init_collateral_alice = Collateral::get_collateral_from_account(ALICE); + let init_collateral_bob = Collateral::get_collateral_from_account(BOB); + let init_total = Collateral::get_total_collateral(); + + assert_ok!(Collateral::slash_collateral(sender, receiver, amount)); + let slash_event = TestEvent::test_events(RawEvent::SlashCollateral(ALICE, BOB, amount)); + + assert!(System::events().iter().any(|a| a.event == slash_event)); + + let collateral_alice = Collateral::get_collateral_from_account(ALICE); + let collateral_bob = Collateral::get_collateral_from_account(BOB); + let total = Collateral::get_total_collateral(); + + assert_eq!(collateral_alice, init_collateral_alice - amount); + assert_eq!(collateral_bob, init_collateral_bob + amount); + assert_eq!(total, init_total); + }) +} + +#[test] +fn test_slash_collateral_fails() { + run_test(|| { + let sender = ALICE; + let receiver = BOB; + let amount = ALICE_BALANCE; + + let init_collateral_alice = Collateral::get_collateral_from_account(ALICE); + let init_collateral_bob = Collateral::get_collateral_from_account(BOB); + let init_total = Collateral::get_total_collateral(); + + assert_err!( + Collateral::slash_collateral(sender, receiver, amount), + Error::InsufficientCollateralAvailable + ); + + let collateral_alice = Collateral::get_collateral_from_account(ALICE); + let collateral_bob = Collateral::get_collateral_from_account(BOB); + let total = Collateral::get_total_collateral(); + + assert_eq!(collateral_alice, init_collateral_alice); + assert_eq!(collateral_bob, init_collateral_bob); + assert_eq!(total, init_total); + }) +} + +#[test] +fn test_slash_collateral_partially_succeeds() { + run_test(|| { + let sender = ALICE; + let receiver = BOB; + let amount = ALICE_BALANCE; + let slash_amount = ALICE_BALANCE; + + assert_ok!(Collateral::lock_collateral(sender, amount)); + + let init_collateral_alice = Collateral::get_collateral_from_account(ALICE); + let init_collateral_bob = Collateral::get_collateral_from_account(BOB); + let init_total = Collateral::get_total_collateral(); + + assert_ok!(Collateral::slash_collateral(sender, receiver, slash_amount)); + let slash_event = + TestEvent::test_events(RawEvent::SlashCollateral(ALICE, BOB, slash_amount)); + + assert!(System::events().iter().any(|a| a.event == slash_event)); + + let collateral_alice = Collateral::get_collateral_from_account(ALICE); + let collateral_bob = Collateral::get_collateral_from_account(BOB); + let total = Collateral::get_total_collateral(); + + assert_eq!(collateral_alice, init_collateral_alice - slash_amount); + assert_eq!(collateral_bob, init_collateral_bob + slash_amount); + assert_eq!(total, init_total); + }) +} diff --git a/crates/exchange-rate-oracle/Cargo.toml b/crates/exchange-rate-oracle/Cargo.toml index dc4df5debb..edd971e6af 100644 --- a/crates/exchange-rate-oracle/Cargo.toml +++ b/crates/exchange-rate-oracle/Cargo.toml @@ -24,41 +24,29 @@ version = '1.0.0' [dependencies.sp-runtime] default-features = false -git = 'https://github.com/paritytech/substrate.git' -rev = '3e651110aa06aa835790df63410a29676243fc54' -version = '2.0.0' +version = '2.0.0-alpha.5' [dependencies.frame-support] default-features = false -git = 'https://github.com/paritytech/substrate.git' -rev = '3e651110aa06aa835790df63410a29676243fc54' -version = '2.0.0' +version = '2.0.0-alpha.5' [dependencies.system] default-features = false -git = 'https://github.com/paritytech/substrate.git' package = 'frame-system' -rev = '3e651110aa06aa835790df63410a29676243fc54' -version = '2.0.0' +version = '2.0.0-alpha.5' [dependencies.sp-io] default-features = false -git = 'https://github.com/paritytech/substrate.git' -rev = '3e651110aa06aa835790df63410a29676243fc54' -version = '2.0.0' +version = '2.0.0-alpha.5' [dependencies.sp-core] default-features = false -git = 'https://github.com/paritytech/substrate.git' -rev = '3e651110aa06aa835790df63410a29676243fc54' -version = '2.0.0' +version = '2.0.0-alpha.5' [dependencies.timestamp] default-features = false -git = 'https://github.com/paritytech/substrate.git' package = 'pallet-timestamp' -rev = '3e651110aa06aa835790df63410a29676243fc54' -version = '2.0.0' +version = '2.0.0-alpha.5' [dependencies.xclaim-core] path = '../xclaim-core' diff --git a/crates/exchange-rate-oracle/src/mock.rs b/crates/exchange-rate-oracle/src/mock.rs index e52d99d0b0..d0cfb7b4de 100644 --- a/crates/exchange-rate-oracle/src/mock.rs +++ b/crates/exchange-rate-oracle/src/mock.rs @@ -1,13 +1,13 @@ /// Mocking the test environment use crate::{Module, Trait}; use frame_support::{impl_outer_event, impl_outer_origin, parameter_types, weights::Weight}; +use mocktopus::mocking::clear_mocks; use sp_core::H256; use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup}, Perbill, }; -use mocktopus::mocking::clear_mocks; impl_outer_origin! { pub enum Origin for Test {} @@ -19,6 +19,7 @@ mod test_events { impl_outer_event! { pub enum TestEvent for Test { + system, test_events, } } @@ -51,6 +52,9 @@ impl system::Trait for Test { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); } impl Trait for Test { diff --git a/crates/security/src/lib.rs b/crates/security/src/lib.rs index 27b91adde6..ae5c948eef 100644 --- a/crates/security/src/lib.rs +++ b/crates/security/src/lib.rs @@ -1,31 +1,31 @@ -#![deny(warnings)] +//#![deny(warnings)] #![cfg_attr(not(feature = "std"), no_std)] #[cfg(test)] mod tests; +use codec::alloc::string::String; +use codec::{Decode, Encode}; /// # Security module implementation /// This is the implementation of the BTC Parachain Security module following the spec at: /// https://interlay.gitlab.io/polkabtc-spec/spec/security /// -use frame_support::{decl_module, decl_storage, decl_event, decl_error}; -use codec::{Encode, Decode}; -use codec::alloc::string::{String}; -use node_primitives::{BlockNumber, AccountId}; -use sp_core::{U256}; -use sp_std::fmt::Debug; +use frame_support::{decl_error, decl_event, decl_module, decl_storage}; +use node_primitives::{AccountId, BlockNumber}; +use sp_core::U256; use sp_std::collections::btree_set::BTreeSet; +use sp_std::fmt::Debug; use bitcoin::types::*; /// ## Configuration /// The pallet's configuration trait. pub trait Trait: system::Trait { - /// The overarching event type. + /// The overarching event type. type Event: From + Into<::Event>; - // Dot currency - // FIXME: Check if correct. DOT currently emulated, until DOT bridge becomes available - // type DotBalance: Parameter + Member + SimpleArithmetic + Codec + Default + Copy + MaybeSerializeDeserialize + Debug + From; + // Dot currency + // FIXME: Check if correct. DOT currently emulated, until DOT bridge becomes available + // type DotBalance: Parameter + Member + SimpleArithmetic + Codec + Default + Copy + MaybeSerializeDeserialize + Debug + From; } /// ## Constants @@ -40,186 +40,186 @@ pub const STAKED_RELAYER_STAKE: u64 = 10; #[derive(Encode, Decode, Clone, PartialEq, Eq)] #[cfg_attr(feature = "std", derive(Debug))] pub enum StatusCode { - /// BTC Parachain is fully operational - Running = 0, - /// An error has occurred. See Errors for more details. - Error = 1, - /// BTC Parachain operation has been fully suspended - Shutdown = 2 + /// BTC Parachain is fully operational + Running = 0, + /// An error has occurred. See Errors for more details. + Error = 1, + /// BTC Parachain operation has been fully suspended + Shutdown = 2, } impl Default for StatusCode { - fn default() -> Self { StatusCode::Running } + fn default() -> Self { + StatusCode::Running + } } /// Enum specifying errors which lead to the Error status, tacked in Errors #[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)] pub enum ErrorCode { - /// No error. Used as default value - None = 0, - /// Missing transactional data for a block header submitted to BTC-Relay + /// No error. Used as default value + None = 0, + /// Missing transactional data for a block header submitted to BTC-Relay NoDataBTCRelay = 1, - /// Invalid transaction was detected in a block header submitted to BTC-Relay - InvalidBTCRelay = 2, - /// The exchangeRateOracle experienced a liveness failure (no up-to-date exchange rate available) - OracleOffline = 3, - /// At least one Vault is being liquidated. Redeem requests paid out partially in collateral (DOT). - Liquidation = 4 + /// Invalid transaction was detected in a block header submitted to BTC-Relay + InvalidBTCRelay = 2, + /// The exchangeRateOracle experienced a liveness failure (no up-to-date exchange rate available) + OracleOffline = 3, + /// At least one Vault is being liquidated. Redeem requests paid out partially in collateral (DOT). + Liquidation = 4, } impl Default for ErrorCode { - fn default() -> Self { ErrorCode::None } + fn default() -> Self { + ErrorCode::None + } } // Indicates the state of a proposed StatusUpdate. #[derive(Encode, Decode, Clone, PartialEq)] #[cfg_attr(feature = "std", derive(Debug))] pub enum ProposalStatus { - /// StatusUpdate is current under review and is being voted upon - Pending = 0, - /// StatusUpdate has been accepted - Accepted = 1, - /// StatusUpdate has been rejected - Rejected = 2 + /// StatusUpdate is current under review and is being voted upon + Pending = 0, + /// StatusUpdate has been accepted + Accepted = 1, + /// StatusUpdate has been rejected + Rejected = 2, } impl Default for ProposalStatus { - fn default() -> Self { ProposalStatus::Pending } + fn default() -> Self { + ProposalStatus::Pending + } } /// ## Structs /// Struct storing information on a proposed parachain status update #[derive(Encode, Decode, Default, Clone, PartialEq)] #[cfg_attr(feature = "std", derive(Debug))] -pub struct StatusUpdate{ - /// New status of the BTC Parachain. - new_status_code: StatusCode, - /// Previous status of the BTC Parachain. - old_status_code: StatusCode, - /// If new_status_code is Error, specifies which errors are to be added to Errors - // FIXME: will need casting to ErrorCode enum - add_errors: BTreeSet, - /// Indicates which ErrorCode entries are to be removed from Errors (recovery). - // FIXME: will need casting to ErrorCode enum - remove_errors: BTreeSet, - /// Parachain block number at which this status update was suggested. - time: BlockNumber, - /// Status of the proposed status update. See ProposalStatus. - proposal_status: ProposalStatus, - /// Message providing more details on the change of status (detailed error message or recovery reason). - msg: String, - /// LE Block hash of the Bitcoin block where the error was detected, if related to BTC-Relay. - btc_block_hash: H256Le, - /// Set of accounts which have voted FOR this status update. This can be either Staked Relayers or the Governance Mechanism. - votes_yes: BTreeSet, - /// Set of accounts which have voted AGAINST this status update. This can be either Staked Relayers or the Governance Mechanism. - votes_no: BTreeSet +pub struct StatusUpdate { + /// New status of the BTC Parachain. + new_status_code: StatusCode, + /// Previous status of the BTC Parachain. + old_status_code: StatusCode, + /// If new_status_code is Error, specifies which errors are to be added to Errors + // FIXME: will need casting to ErrorCode enum + add_errors: BTreeSet, + /// Indicates which ErrorCode entries are to be removed from Errors (recovery). + // FIXME: will need casting to ErrorCode enum + remove_errors: BTreeSet, + /// Parachain block number at which this status update was suggested. + time: BlockNumber, + /// Status of the proposed status update. See ProposalStatus. + proposal_status: ProposalStatus, + /// Message providing more details on the change of status (detailed error message or recovery reason). + msg: String, + /// LE Block hash of the Bitcoin block where the error was detected, if related to BTC-Relay. + btc_block_hash: H256Le, + /// Set of accounts which have voted FOR this status update. This can be either Staked Relayers or the Governance Mechanism. + votes_yes: BTreeSet, + /// Set of accounts which have voted AGAINST this status update. This can be either Staked Relayers or the Governance Mechanism. + votes_no: BTreeSet, } #[derive(Encode, Decode, Default, Clone, PartialEq)] #[cfg_attr(feature = "std", derive(Debug))] pub struct StakedRelayer { - stake: u64 + stake: u64, } - // This pallet's storage items. decl_storage! { - trait Store for Module as SecurityModule { + trait Store for Module as SecurityModule { - /// Integer/Enum defining the current state of the BTC-Parachain - ParachainStatus get(parachain_status): StatusCode; + /// Integer/Enum defining the current state of the BTC-Parachain + ParachainStatus get(parachain_status): StatusCode; - /// Set of ErrorCodes, indicating the reason for an "Error" ParachainStatus - /// FIXME: type casting to enum necessary! - Errors get(fn error): BTreeSet; + /// Set of ErrorCodes, indicating the reason for an "Error" ParachainStatus + /// FIXME: type casting to enum necessary! + Errors get(fn error): BTreeSet; - /// Integer increment-only counter used to track status updates. - StatusCounter get(fn status_counter): U256; + /// Integer increment-only counter used to track status updates. + StatusCounter get(fn status_counter): U256; - /// Integer increment-only counter, used to prevent collisions when generating identifiers - /// for e.g. issue, redeem or replace requests (for OP_RETURN field in Bitcoin). - Nonce get(fn nonce): U256; + /// Integer increment-only counter, used to prevent collisions when generating identifiers + /// for e.g. issue, redeem or replace requests (for OP_RETURN field in Bitcoin). + Nonce get(fn nonce): U256; - /// Mapping from accounts of staked relayers to the StakedRelayer struct - StakedRelayers get(fn staked_relayer): map T::AccountId => StakedRelayer; + /// Mapping from accounts of staked relayers to the StakedRelayer struct + StakedRelayers get(fn staked_relayer): map T::AccountId => StakedRelayer; - /// Map of StatusUpdates, identified by an integer key - StatusUpdates get(fn status_update): map U256 => StatusUpdate; + /// Map of StatusUpdates, identified by an integer key + StatusUpdates get(fn status_update): map U256 => StatusUpdate; - /// Mapping of Bitcoin transaction identifiers (SHA256 hashes) to account - /// identifiers of Vaults accused of theft - TheftReports get(fn theft_report): map H256Le => BTreeSet; - } + /// Mapping of Bitcoin transaction identifiers (SHA256 hashes) to account + /// identifiers of Vaults accused of theft + TheftReports get(fn theft_report): map H256Le => BTreeSet; + } } - // The pallet's dispatchable functions. decl_module! { - pub struct Module for enum Call where origin: T::Origin { + pub struct Module for enum Call where origin: T::Origin { - } + } } - - - impl Module { + /// Checks if the ParachainStatus matches the provided StatusCode + /// + /// # Arguments + /// + /// * `status_code` - to-be-checked StatusCode enum + pub fn check_parachain_status(status_code: StatusCode) -> bool { + status_code == ::get() + } - /// Checks if the ParachainStatus matches the provided StatusCode - /// - /// # Arguments - /// - /// * `status_code` - to-be-checked StatusCode enum - pub fn check_parachain_status(status_code: StatusCode) -> bool { - return status_code == ::get(); - } - - /// Checks if the given ErrorCode is contains in Errors - /// - /// # Arguments - /// - /// * `error_code` - to-be-checked ErrorCode enum - pub fn check_parachain_error(error_code: ErrorCode) -> bool { - return ::get().contains(&(error_code as u8)); - } + /// Checks if the given ErrorCode is contains in Errors + /// + /// # Arguments + /// + /// * `error_code` - to-be-checked ErrorCode enum + pub fn check_parachain_error(error_code: ErrorCode) -> bool { + ::get().contains(&(error_code as u8)) + } /// Checks if a staked relayer is registered /// /// # Arguments /// /// * `relayer` - account id of the relayer pub fn check_relayer_registered(relayer: T::AccountId) -> bool { - return >::exists(relayer); + >::exists(relayer) } } decl_event!( - pub enum Event { + pub enum Event { RegisterStakedRelayer(AccountId, u64), DeRegisterStakedRelayer(AccountId), StatusUpdateSuggested(u8, BTreeSet, BTreeSet, String, AccountId), - VoteOnStatusUpdate(U256, AccountId, bool), - ExecuteStatusUpdate(u8, BTreeSet, BTreeSet, String), - RejectStatusUpdate(u8, BTreeSet, BTreeSet, String), - ForceStatusUpdate(u8, BTreeSet, BTreeSet, String), - SlashStakedRelayer(AccountId), - ReportVaultTheft(AccountId), - } + VoteOnStatusUpdate(U256, AccountId, bool), + ExecuteStatusUpdate(u8, BTreeSet, BTreeSet, String), + RejectStatusUpdate(u8, BTreeSet, BTreeSet, String), + ForceStatusUpdate(u8, BTreeSet, BTreeSet, String), + SlashStakedRelayer(AccountId), + ReportVaultTheft(AccountId), + } ); decl_error! { pub enum Error for Module { - AlreadyRegistered, - InsufficientStake, - NotRegistered, - GovernanceOnly, - StakedRelayersOnly, - StatusUpdateNotFound, - InsufficientYesVotes, - InsufficientNoVotes, - VaultAlreadyReported, - VaultNotFound, - VaultAlreadyLiquidated, - ValidRedeemOrReplace, - ValidMergeTransaction, - CollateralOk, - OracleOnline + AlreadyRegistered, + InsufficientStake, + NotRegistered, + GovernanceOnly, + StakedRelayersOnly, + StatusUpdateNotFound, + InsufficientYesVotes, + InsufficientNoVotes, + VaultAlreadyReported, + VaultNotFound, + VaultAlreadyLiquidated, + ValidRedeemOrReplace, + ValidMergeTransaction, + CollateralOk, + OracleOnline } } diff --git a/crates/security/src/tests.rs b/crates/security/src/tests.rs index 5640e6352a..ea0b68af28 100644 --- a/crates/security/src/tests.rs +++ b/crates/security/src/tests.rs @@ -1,14 +1,15 @@ /// Tests for Security Module - -use crate::{Trait}; -use sp_core::{H256}; -use frame_support::{impl_outer_origin, impl_outer_event, parameter_types, weights::Weight}; +use crate::Trait; +use frame_support::{impl_outer_event, impl_outer_origin, parameter_types, weights::Weight}; +use sp_core::H256; use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, testing::Header, Perbill, + testing::Header, + traits::{BlakeTwo256, IdentityLookup}, + Perbill, }; impl_outer_origin! { - pub enum Origin for Test {} + pub enum Origin for Test {} } mod test_events { @@ -27,32 +28,32 @@ impl_outer_event! { #[derive(Clone, Eq, PartialEq)] pub struct Test; parameter_types! { - pub const BlockHashCount: u64 = 250; - pub const MaximumBlockWeight: Weight = 1024; - pub const MaximumBlockLength: u32 = 2 * 1024; - pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75); + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: Weight = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75); } impl system::Trait for Test { - type Origin = Origin; - type Call = (); - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; - type Header = Header; - type Event = TestEvent; - type BlockHashCount = BlockHashCount; - type MaximumBlockWeight = MaximumBlockWeight; - type MaximumBlockLength = MaximumBlockLength; - type AvailableBlockRatio = AvailableBlockRatio; - type Version = (); - type ModuleToIndex = (); + type Origin = Origin; + type Call = (); + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type Event = TestEvent; + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; + type AvailableBlockRatio = AvailableBlockRatio; + type Version = (); + type ModuleToIndex = (); } impl Trait for Test { - type Event = TestEvent; + type Event = TestEvent; } // pub type System = system::Module; @@ -69,19 +70,13 @@ impl ExtBuilder { } } - // fn ExtBuilder::build() -> sp_io::TestExternalities { // system::GenesisConfig::default().build_storage::().unwrap().into() // } - /// Initialize Function #[test] fn dummy_test() { - ExtBuilder::build().execute_with(|| { - }); + ExtBuilder::build().execute_with(|| {}); } - - - diff --git a/crates/treasury/Cargo.toml b/crates/treasury/Cargo.toml new file mode 100644 index 0000000000..a582c2c9bf --- /dev/null +++ b/crates/treasury/Cargo.toml @@ -0,0 +1,67 @@ +[package] +authors = ['Interlay'] +description = 'Treasury module' +edition = '2018' +name = 'treasury' +version = '0.1.0' + +[features] +default = ['std'] +std = [ + 'codec/std', + 'frame-support/std', + 'sp-runtime/std', + 'sp-io/std', + 'sp-core/std', + 'safe-mix/std', + 'system/std', + 'sp-std/std', + 'pallet-balances/std', +] + +[dependencies.codec] +default-features = false +features = ['derive'] +package = 'parity-scale-codec' +version = '1.3.0' + +[dependencies.frame-support] +default-features = false +version = '2.0.0-alpha.5' + +[dependencies.safe-mix] +default-features = false +version = '1.0.0' + +[dependencies.sp-core] +default-features = false +version = '2.0.0-alpha.5' + +[dependencies.sp-io] +default-features = false +version = '2.0.0-alpha.5' + +[dependencies.sp-runtime] +default-features = false +version = '2.0.0-alpha.5' + +[dependencies.sp-std] +default-features = false +version = '2.0.0-alpha.5' + +[dependencies.system] +default-features = false +package = 'frame-system' +version = '2.0.0-alpha.5' + +[dependencies.pallet-balances] +default-features = false +package = 'pallet-balances' +version = '2.0.0-alpha.5' + +[dependencies.xclaim-core] +path = '../xclaim-core' + +[dev-dependencies] +mocktopus = "0.7.0" + diff --git a/crates/treasury/src/lib.rs b/crates/treasury/src/lib.rs new file mode 100644 index 0000000000..d9c118ba98 --- /dev/null +++ b/crates/treasury/src/lib.rs @@ -0,0 +1,178 @@ +#![deny(warnings)] +#![cfg_attr(test, feature(proc_macro_hygiene))] +#![cfg_attr(not(feature = "std"), no_std)] +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +#[cfg(test)] +extern crate mocktopus; + +// #[cfg(test)] +// use mocktopus::macros::mockable; + +use frame_support::traits::{Currency, ExistenceRequirement::KeepAlive, ReservableCurrency}; +/// # PolkaBTC Treasury implementation +/// The Treasury module according to the specification at +/// https://interlay.gitlab.io/polkabtc-spec/spec/treasury.html +// Substrate +use frame_support::{decl_event, decl_module, decl_storage, dispatch::DispatchResult, ensure}; +use sp_runtime::ModuleId; +use system::ensure_signed; + +use xclaim_core::Error; + +type BalanceOf = <::PolkaBTC as Currency<::AccountId>>::Balance; + +/// The treasury's module id, used for deriving its sovereign account ID. +const _MODULE_ID: ModuleId = ModuleId(*b"ily/trsy"); + +/// The pallet's configuration trait. +/// Instantiation of this pallet requires the existence of a module that +/// implements Currency and ReservableCurrency. The Balances module can be used +/// for this. The Balances module then gives functions for total supply, balances +/// of accounts, and any function defined by the Currency and ReservableCurrency +/// traits. +pub trait Trait: system::Trait { + /// The PolkaBTC currency + type PolkaBTC: Currency + ReservableCurrency; + + /// The overarching event type. + type Event: From> + Into<::Event>; +} + +// This pallet's storage items. +decl_storage! { + trait Store for Module as Treasury { + /// ## Storage + /// Note that account's balances and locked balances are handled + /// through the Balances module. + /// + /// Total locked PolkaDOT + TotalLocked: BalanceOf; + } +} + +// The pallet's events +decl_event!( + pub enum Event + where + AccountId = ::AccountId, + Balance = BalanceOf, + { + Transfer(AccountId, AccountId, Balance), + Mint(AccountId, Balance), + Lock(AccountId, Balance), + Burn(AccountId, Balance), + } +); + +// The pallet's dispatchable functions. +decl_module! { + /// The module declaration. + pub struct Module for enum Call where origin: T::Origin { + // Initializing events + // this is needed only if you are using events in your pallet + fn deposit_event() = default; + + /// Transfer an amount of PolkaBTC (without fees) + /// + /// # Arguments + /// + /// * `origin` - sender of the transaction + /// * `receiver` - receiver of the transaction + /// * `amount` - amount of PolkaBTC + fn transfer(origin, receiver: T::AccountId, amount: BalanceOf) + -> DispatchResult + { + let sender = ensure_signed(origin)?; + + T::PolkaBTC::transfer(&sender, &receiver, amount, KeepAlive) + .map_err(|_| Error::InsufficientFunds)?; + + Self::deposit_event(RawEvent::Transfer(sender, receiver, amount)); + + Ok(()) + } + } +} + +impl Module { + /// Total supply of PolkaBTC + pub fn get_total_supply() -> BalanceOf { + T::PolkaBTC::total_issuance() + } + /// Balance of an account (wrapper) + pub fn get_balance_from_account(account: T::AccountId) -> BalanceOf { + T::PolkaBTC::free_balance(&account) + } + /// Locked balance of an account (wrapper) + pub fn get_locked_balance_from_account(account: T::AccountId) -> BalanceOf { + T::PolkaBTC::reserved_balance(&account) + } + /// Increase the supply of locked PolkaBTC + pub fn increase_total_locked(amount: BalanceOf) { + let new_locked = >::get() + amount; + >::put(new_locked); + } + /// Decrease the supply of locked PolkaBTC + pub fn decrease_total_locked(amount: BalanceOf) { + let new_locked = >::get() - amount; + >::put(new_locked); + } + /// Mint new tokens + /// + /// # Arguments + /// + /// * `requester` - PolkaBTC user requesting new tokens + /// * `amount` - to be issued amount of PolkaBTC + pub fn mint(requester: T::AccountId, amount: BalanceOf) { + // adds the amount to the total balance of tokens + let minted_tokens = T::PolkaBTC::issue(amount); + // adds the added amount to the requester's balance + T::PolkaBTC::resolve_creating(&requester, minted_tokens); + + Self::deposit_event(RawEvent::Mint(requester, amount)); + } + /// Lock PolkaBTC tokens to burn them. Note: this removes them from the + /// free balance of PolkaBTC and adds them to the locked supply of PolkaBTC. + /// + /// # Arguments + /// + /// * `redeemer` - the account redeeming tokens + /// * `amount` - to be locked amount of PolkaBTC + pub fn lock(redeemer: T::AccountId, amount: BalanceOf) -> Result<(), Error> { + T::PolkaBTC::reserve(&redeemer, amount).map_err(|_| Error::InsufficientFunds)?; + + // update total locked balance + Self::increase_total_locked(amount); + + Self::deposit_event(RawEvent::Lock(redeemer, amount)); + Ok(()) + } + /// Burn previously locked PolkaBTC tokens + /// + /// # Arguments + /// + /// * `redeemer` - the account redeeming tokens + /// * `amount` - the to be burned amount of PolkaBTC + pub fn burn(redeemer: T::AccountId, amount: BalanceOf) -> Result<(), Error> { + ensure!( + T::PolkaBTC::reserved_balance(&redeemer) >= amount, + Error::InsufficientLockedFunds + ); + + // burn the tokens from the locked balance + Self::decrease_total_locked(amount); + + // burn the tokens for the redeemer + // remainder should always be 0 and is checked above + let (_burned_tokens, _remainder) = T::PolkaBTC::slash_reserved(&redeemer, amount); + + Self::deposit_event(RawEvent::Burn(redeemer, amount)); + + Ok(()) + } +} diff --git a/crates/treasury/src/mock.rs b/crates/treasury/src/mock.rs new file mode 100644 index 0000000000..91fdac7fb0 --- /dev/null +++ b/crates/treasury/src/mock.rs @@ -0,0 +1,119 @@ +/// Mocking the test environment +use crate::{Module, Trait}; +use frame_support::{impl_outer_event, impl_outer_origin, parameter_types, weights::Weight}; +use pallet_balances as balances; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, IdentityLookup}, + Perbill, +}; + +use mocktopus::mocking::clear_mocks; + +impl_outer_origin! { + pub enum Origin for Test {} +} + +mod test_events { + pub use crate::Event; +} + +impl_outer_event! { + pub enum TestEvent for Test { + system, + test_events, + balances, + } +} + +// For testing the pallet, we construct most of a mock runtime. This means +// first constructing a configuration type (`Test`) which `impl`s each of the +// configuration traits of pallets we want to use. + +pub type AccountId = u64; +pub type Balance = u64; + +#[derive(Clone, Eq, PartialEq)] +pub struct Test; +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: Weight = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75); +} +impl system::Trait for Test { + type Origin = Origin; + type Call = (); + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type Event = TestEvent; + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; + type AvailableBlockRatio = AvailableBlockRatio; + type Version = (); + type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); +} +parameter_types! { + pub const ExistentialDeposit: u64 = 1; +} +impl pallet_balances::Trait for Test { + type Balance = Balance; + type Event = TestEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; +} + +impl Trait for Test { + type PolkaBTC = Balances; + type Event = TestEvent; +} + +pub type Error = crate::Error; + +pub type System = system::Module; +pub type Balances = pallet_balances::Module; +pub type Treasury = Module; + +pub const ALICE: AccountId = 1; +pub const BOB: AccountId = 2; +pub const ALICE_BALANCE: u64 = 1_000_000; +pub const BOB_BALANCE: u64 = 1_000_000; + +pub struct ExtBuilder; + +impl ExtBuilder { + pub fn build() -> sp_io::TestExternalities { + let mut storage = system::GenesisConfig::default() + .build_storage::() + .unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![(ALICE, ALICE_BALANCE), (BOB, BOB_BALANCE)], + } + .assimilate_storage(&mut storage) + .unwrap(); + + storage.into() + + // sp_io::TestExternalities::from(storage) + } +} + +pub fn run_test(test: T) -> () +where + T: FnOnce() -> (), +{ + clear_mocks(); + ExtBuilder::build().execute_with(test); +} diff --git a/crates/treasury/src/tests.rs b/crates/treasury/src/tests.rs new file mode 100644 index 0000000000..5fb145a43e --- /dev/null +++ b/crates/treasury/src/tests.rs @@ -0,0 +1,221 @@ +/// Tests for Treasury +use crate::mock::*; +use crate::RawEvent; +use frame_support::{assert_err, assert_ok}; + +// use mocktopus::mocking::*; +/// Total supply +#[test] +fn test_total_supply_correct() { + run_test(|| { + // initial supply + let desired_total_supply = ALICE_BALANCE + BOB_BALANCE; + let total_supply = Treasury::get_total_supply(); + + assert_eq!(desired_total_supply, total_supply); + }) +} + +/// Transfer +#[test] +fn test_transfer_succeeds() { + run_test(|| { + let sender = Origin::signed(ALICE); + let receiver = BOB; + let amount: Balance = 3; + + let init_balance_alice = Treasury::get_balance_from_account(ALICE); + let init_balance_bob = Treasury::get_balance_from_account(BOB); + let init_total_supply = Treasury::get_total_supply(); + + assert_ok!(Treasury::transfer(sender, receiver, amount)); + let transfer_event = TestEvent::test_events(RawEvent::Transfer(ALICE, BOB, amount)); + + assert!(System::events().iter().any(|a| a.event == transfer_event)); + + let balance_alice = Treasury::get_balance_from_account(ALICE); + let balance_bob = Treasury::get_balance_from_account(BOB); + let total_supply = Treasury::get_total_supply(); + + assert_eq!(balance_alice, init_balance_alice - amount); + assert_eq!(balance_bob, init_balance_bob + amount); + assert_eq!(total_supply, init_total_supply); + }) +} + +#[test] +fn test_transfer_fails() { + run_test(|| { + let sender = Origin::signed(ALICE); + let receiver = BOB; + let amount = ALICE_BALANCE + 10; + + let init_balance_alice = Treasury::get_balance_from_account(ALICE); + let init_balance_bob = Treasury::get_balance_from_account(BOB); + let init_total_supply = Treasury::get_total_supply(); + + assert_err!( + Treasury::transfer(sender, receiver, amount), + Error::InsufficientFunds + ); + + let balance_alice = Treasury::get_balance_from_account(ALICE); + let balance_bob = Treasury::get_balance_from_account(BOB); + let total_supply = Treasury::get_total_supply(); + + assert_eq!(balance_alice, init_balance_alice); + assert_eq!(balance_bob, init_balance_bob); + assert_eq!(total_supply, init_total_supply); + }) +} + +/// Mint +#[test] +fn test_mint_succeeds() { + run_test(|| { + let requester = ALICE; + let amount: Balance = 5; + + let init_balance_alice = Treasury::get_balance_from_account(ALICE); + let init_total_supply = Treasury::get_total_supply(); + + Treasury::mint(requester, amount); + let mint_event = TestEvent::test_events(RawEvent::Mint(ALICE, amount)); + + assert!(System::events().iter().any(|a| a.event == mint_event)); + + let balance_alice = Treasury::get_balance_from_account(ALICE); + let total_supply = Treasury::get_total_supply(); + + assert_eq!(balance_alice, init_balance_alice + amount); + assert_eq!(total_supply, init_total_supply + amount); + }) +} + +/// Lock +#[test] +fn test_lock_succeeds() { + run_test(|| { + let redeemer = ALICE; + let amount = ALICE_BALANCE; + + let init_balance = Treasury::get_balance_from_account(ALICE); + let init_locked_balance = Treasury::get_locked_balance_from_account(ALICE); + let init_total_supply = Treasury::get_total_supply(); + + assert_ok!(Treasury::lock(redeemer, amount)); + let lock_event = TestEvent::test_events(RawEvent::Lock(ALICE, amount)); + + assert!(System::events().iter().any(|a| a.event == lock_event)); + + let balance = Treasury::get_balance_from_account(ALICE); + let locked_balance = Treasury::get_locked_balance_from_account(ALICE); + let total_supply = Treasury::get_total_supply(); + + assert_eq!(balance, init_balance - amount); + assert_eq!(locked_balance, init_locked_balance + amount); + assert_eq!(total_supply, init_total_supply); + }) +} + +#[test] +fn test_lock_fails() { + run_test(|| { + let redeemer = ALICE; + let amount = ALICE_BALANCE + 5; + + let init_balance = Treasury::get_balance_from_account(ALICE); + let init_locked_balance = Treasury::get_locked_balance_from_account(ALICE); + let init_total_supply = Treasury::get_total_supply(); + + assert_err!(Treasury::lock(redeemer, amount), Error::InsufficientFunds); + + let balance = Treasury::get_balance_from_account(ALICE); + let locked_balance = Treasury::get_locked_balance_from_account(ALICE); + let total_supply = Treasury::get_total_supply(); + + assert_eq!(balance, init_balance); + assert_eq!(locked_balance, init_locked_balance); + assert_eq!(total_supply, init_total_supply); + }) +} + +/// Burn +#[test] +fn test_burn_succeeds() { + run_test(|| { + let redeemer = ALICE; + let amount = ALICE_BALANCE; + + let init_balance = Treasury::get_balance_from_account(ALICE); + let init_locked_balance = Treasury::get_locked_balance_from_account(ALICE); + let init_total_supply = Treasury::get_total_supply(); + + assert_ok!(Treasury::lock(redeemer, amount)); + assert_ok!(Treasury::burn(redeemer, amount)); + let burn_event = TestEvent::test_events(RawEvent::Burn(ALICE, amount)); + + assert!(System::events().iter().any(|a| a.event == burn_event)); + + let balance = Treasury::get_balance_from_account(ALICE); + let locked_balance = Treasury::get_locked_balance_from_account(ALICE); + let total_supply = Treasury::get_total_supply(); + + assert_eq!(balance, init_balance - amount); + assert_eq!(locked_balance, init_locked_balance); + assert_eq!(total_supply, init_total_supply - amount); + }) +} + +#[test] +fn test_burn_fails() { + run_test(|| { + let redeemer = ALICE; + let amount = ALICE_BALANCE; + + let init_balance = Treasury::get_balance_from_account(ALICE); + let init_locked_balance = Treasury::get_locked_balance_from_account(ALICE); + let init_total_supply = Treasury::get_total_supply(); + + assert_err!( + Treasury::burn(redeemer, amount), + Error::InsufficientLockedFunds + ); + + let balance = Treasury::get_balance_from_account(ALICE); + let locked_balance = Treasury::get_locked_balance_from_account(ALICE); + let total_supply = Treasury::get_total_supply(); + + assert_eq!(balance, init_balance); + assert_eq!(locked_balance, init_locked_balance); + assert_eq!(total_supply, init_total_supply); + }) +} + +#[test] +fn test_burn_partially_succeeds() { + run_test(|| { + let redeemer = ALICE; + let amount = ALICE_BALANCE; + let burn_amount = amount - 10; + + let init_balance = Treasury::get_balance_from_account(ALICE); + let init_locked_balance = Treasury::get_locked_balance_from_account(ALICE); + let init_total_supply = Treasury::get_total_supply(); + + assert_ok!(Treasury::lock(redeemer, amount)); + assert_ok!(Treasury::burn(redeemer, burn_amount)); + let burn_event = TestEvent::test_events(RawEvent::Burn(ALICE, burn_amount)); + + assert!(System::events().iter().any(|a| a.event == burn_event)); + + let balance = Treasury::get_balance_from_account(ALICE); + let locked_balance = Treasury::get_locked_balance_from_account(ALICE); + let total_supply = Treasury::get_total_supply(); + + assert_eq!(balance, init_balance - amount); // balance is locked + // part of the balance is still locked + assert_eq!(locked_balance, init_locked_balance + (amount - burn_amount)); + assert_eq!(total_supply, init_total_supply - burn_amount); + }) +} diff --git a/crates/vault-registry/.gitattributes b/crates/vault-registry/.gitattributes new file mode 100644 index 0000000000..dfe0770424 --- /dev/null +++ b/crates/vault-registry/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/crates/vault-registry/.gitignore b/crates/vault-registry/.gitignore new file mode 100644 index 0000000000..53eaa21960 --- /dev/null +++ b/crates/vault-registry/.gitignore @@ -0,0 +1,2 @@ +/target +**/*.rs.bk diff --git a/crates/vault-registry/Cargo.toml b/crates/vault-registry/Cargo.toml new file mode 100644 index 0000000000..55454c235e --- /dev/null +++ b/crates/vault-registry/Cargo.toml @@ -0,0 +1,68 @@ +[package] +name = "vault-registry" +version = "0.0.1" +authors = ["Interlay Ltd"] +edition = "2018" + +[features] +default = ['std'] +std = [ + 'codec/std', + 'frame-support/std', + 'sp-runtime/std', + 'sp-io/std', + 'system/std', + 'timestamp/std', + 'sp-core/std', + 'pallet-balances/std', + 'node-primitives/std' +] + +[dependencies.codec] +default-features = false +features = ['derive'] +package = 'parity-scale-codec' +version = '1.0.0' + +[dependencies.sp-runtime] +default-features = false +version = '2.0.0-alpha.5' + +[dependencies.frame-support] +default-features = false +version = '2.0.0-alpha.5' + +[dependencies.system] +default-features = false +package = 'frame-system' +version = '2.0.0-alpha.5' + +[dependencies.sp-io] +default-features = false +version = '2.0.0-alpha.5' + +[dependencies.sp-core] +default-features = false +version = '2.0.0-alpha.5' + +[dependencies.timestamp] +default-features = false +package = 'pallet-timestamp' +version = '2.0.0-alpha.5' + +[dependencies.pallet-balances] +default-features = false +package = 'pallet-balances' +version = '2.0.0-alpha.5' + +[dependencies.node-primitives] +default-features = false +version = '2.0.0' +git = 'https://github.com/paritytech/substrate.git' +rev = '3e651110aa06aa835790df63410a29676243fc54' + +[dependencies.xclaim-core] +path = '../xclaim-core' + +[dev-dependencies] +mocktopus = "0.7.0" diff --git a/crates/vault-registry/README.md b/crates/vault-registry/README.md new file mode 100644 index 0000000000..c0af09b9cb --- /dev/null +++ b/crates/vault-registry/README.md @@ -0,0 +1,58 @@ +# Vault Registry + +Based on the Vault Registry specification [https://interlay.gitlab.io/polkabtc-spec/spec/vaultregistry.html](https://interlay.gitlab.io/polkabtc-spec/spec/vaultregistry.html). + +## Installation + +Run `cargo build` from the root folder of this directory. + +## Testing + +Run `cargo test` from the root folder of this directory. + + +## Integration into Runtimes + +### Runtime `Cargo.toml` + +To add this pallet to your runtime, simply include the following to your runtime's `Cargo.toml` file: + +```TOML +[dependencies.btc-relay] +default_features = false +git = '../creates/vault-registry' +``` + +and update your runtime's `std` feature to include this pallet: + +```TOML +std = [ + # --snip-- + 'vault-registry/std', +] +``` + +### Runtime `lib.rs` + +You should implement it's trait like so: + +```rust +/// Used for test_module +impl VaultRegistry::Trait for Runtime { + type Event = Event; +} +``` + +and include it in your `construct_runtime!` macro: + +```rust +VaultRegistry: vault-registry::{Module, Call, Storage, Event}, +``` + +## Reference Docs + +You can view the reference docs for this pallet by running: + +``` +cargo doc --open +``` diff --git a/crates/vault-registry/src/lib.rs b/crates/vault-registry/src/lib.rs new file mode 100644 index 0000000000..4865b3b93e --- /dev/null +++ b/crates/vault-registry/src/lib.rs @@ -0,0 +1,175 @@ +#![deny(warnings)] +#![cfg_attr(test, feature(proc_macro_hygiene))] +#![cfg_attr(not(feature = "std"), no_std)] +#[cfg(test)] +mod tests; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +extern crate mocktopus; + +#[cfg(test)] +use mocktopus::macros::mockable; + +/// # Vault Registry implementation +/// This is the implementation of the Vault Registry following the spec at: +/// https://interlay.gitlab.io/polkabtc-spec/spec/vaultregistry.html +// Substrate +use frame_support::{decl_event, decl_module, decl_storage /*, ensure */}; +// use std::time::SystemTime; +// use system::ensure_signed; +// use frame_support::dispatch::DispatchResult; +use codec::{Decode, Encode}; +use frame_support::traits::{Currency, ReservableCurrency}; +use node_primitives::{AccountId, Balance, BlockNumber}; +use sp_core::H160; + +use xclaim_core::Error; + +// FIXME: for mocking purposes PolkaBTC is declared here. This should be imported +// from the Treasury module in the future. +type BalanceOf = <::PolkaBTC as Currency<::AccountId>>::Balance; + +/// ## Configuration and Constants +/// The pallet's configuration trait. +pub trait Trait: system::Trait { + /// The overarching event type. + type Event: From> + Into<::Event>; + /// The DOT currency + type DOT: Currency + ReservableCurrency; + /// The PolkaBTC currency + type PolkaBTC: Currency + ReservableCurrency; +} + +/// Granularity of `SecureCollateralThreshold`, `AuctionCollateralThreshold`, +/// `LiquidationCollateralThreshold`, and `PunishmentFee` +pub const GRANULARITY: u128 = 5; + +#[derive(Encode, Decode, Default, Clone, PartialEq)] +#[cfg_attr(feature = "std", derive(Debug))] +pub struct Vault { + // Account identifier of the Vault + vault: AccountId, + // Number of PolkaBTC tokens pending issue + to_be_issued_tokens: Balance, + // Number of issued PolkaBTC tokens + issued_tokens: Balance, + // Number of PolkaBTC tokens pending redeem + to_be_redeemed_tokens: Balance, + // DOT collateral locked by this Vault + collateral: Balance, + // Bitcoin address of this Vault (P2PKH, P2SH, P2PKH, P2WSH) + btc_address: H160, + // Block height until which this Vault is banned from being + // used for Issue, Redeem (except during automatic liquidation) and Replace . + banned_until: BlockNumber, +} + +// This pallet's storage items. +decl_storage! { + trait Store for Module as VaultRegistry { + /// ## Storage + /// The minimum collateral (DOT) a Vault needs to provide + /// to participate in the issue process. + MinimumCollateralVault: BalanceOf; + + /// If a Vault misbehaves in either the redeem or replace protocol by + /// failing to prove that it sent the correct amount of BTC to the + /// correct address within the time limit, a vault is punished. + /// The punishment is the equivalent value of BTC in DOT + /// (valued at the current exchange rate via `getExchangeRate`) plus a + /// fixed `PunishmentFee` that is added as a percentage on top + /// to compensate the damaged party for its loss. + /// For example, if the `PunishmentFee` is set to 50000, + /// it is equivalent to 50%. + PunishmentFee: u128; + + /// If a Vault fails to execute a correct redeem or replace, + /// it is temporarily banned from further issue, redeem or replace requests. + PunishmentDelay: T::BlockNumber; + + /// If a Vault is running low on collateral and falls below + /// `PremiumRedeemThreshold`, users are allocated a premium in DOT + /// when redeeming with the Vault - as defined by this parameter. + /// For example, if the RedeemPremiumFee is set to 5000, it is equivalent to 5%. + RedeemPremiumFee: u128; + + /// Determines the over-collareralization rate for DOT collateral locked + /// by Vaults, necessary for issuing PolkaBTC. Must to be strictly + /// greater than 100000 and LiquidationCollateralThreshold. + SecureCollateralThreshold: u128; + + /// Determines the rate for the collateral rate of Vaults, at which the + /// BTC backed by the Vault are opened up for auction to other Vaults + AuctionCollateralThreshold: u128; + + /// Determines the rate for the collateral rate of Vaults, + /// at which users receive a premium in DOT, allocated from the + /// Vault’s collateral, when performing a redeem with this Vault. + /// Must to be strictly greater than 100000 and LiquidationCollateralThreshold. + PremiumRedeemThreshold: u128; + + /// Determines the lower bound for the collateral rate in PolkaBTC. + /// Must be strictly greater than 100000. If a Vault’s collateral rate + /// drops below this, automatic liquidation (forced Redeem) is triggered. + LiquidationCollateralThreshold: u128; + + /// Account identifier of an artificial Vault maintained by the VaultRegistry + /// to handle polkaBTC balances and DOT collateral of liquidated Vaults. + /// That is, when a Vault is liquidated, its balances are transferred to + /// LiquidationVault and claims are later handled via the LiquidationVault. + LiquidationVault: T::AccountId; + + /// Mapping of Vaults, using the respective Vault account identifier as key. + Vaults: map hasher(blake2_128_concat) AccountId => Vault; + } +} + +// The pallet's dispatchable functions. +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + // Initializing events + fn deposit_event() = default; + } +} + +#[cfg_attr(test, mockable)] +impl Module { + /// Public getters + + pub fn get_vault_from_id(id: AccountId) -> Vault { + ::get(id) + } + + pub fn mutate_vault_from_id(id: AccountId, vault: Vault) { + ::mutate(id, |v| *v = vault) + } + + pub fn insert_vault(id: AccountId, vault: Vault) { + ::insert(id, vault) + } + /// Private getters and setters + + /// Other helpers + /// Returns an error if the parachain is not in running state + fn _ensure_parachain_running() -> Result<(), Error> { + // TODO: integrate security module + // ensure!( + // !>::check_parachain_status( + // StatusCode::Shutdown), + // Error::Shutdown + // ); + Ok(()) + } +} + +decl_event! { + /// ## Events + pub enum Event where + AccountId = ::AccountId, + Balance = BalanceOf { + RegisterVault(AccountId, Balance), + } +} diff --git a/crates/vault-registry/src/mock.rs b/crates/vault-registry/src/mock.rs new file mode 100644 index 0000000000..29253dbe1e --- /dev/null +++ b/crates/vault-registry/src/mock.rs @@ -0,0 +1,105 @@ +/// Mocking the test environment +use crate::{Module, Trait}; +use frame_support::{impl_outer_event, impl_outer_origin, parameter_types, weights::Weight}; +use mocktopus::mocking::clear_mocks; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, IdentityLookup}, + Perbill, +}; + +impl_outer_origin! { + pub enum Origin for Test {} +} + +mod test_events { + pub use crate::Event; +} + +impl_outer_event! { + pub enum TestEvent for Test { + system, + test_events, + pallet_balances, + } +} + +// For testing the pallet, we construct most of a mock runtime. This means +// first constructing a configuration type (`Test`) which `impl`s each of the +// configuration traits of modules we want to use. +#[derive(Clone, Eq, PartialEq)] +pub struct Test; +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: Weight = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75); +} +impl system::Trait for Test { + type Origin = Origin; + type Call = (); + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type Event = TestEvent; + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; + type AvailableBlockRatio = AvailableBlockRatio; + type Version = (); + type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); +} + +pub type Balance = u64; + +parameter_types! { + pub const ExistentialDeposit: u64 = 1; +} + +impl pallet_balances::Trait for Test { + type Balance = Balance; + type Event = TestEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; +} + +impl Trait for Test { + type Event = TestEvent; + type PolkaBTC = Balances; + type DOT = Balances; +} + +pub type Balances = pallet_balances::Module; + +// pub type Error = crate::Error; + +pub type System = system::Module; +pub type _VaultRegistry = Module; + +pub struct ExtBuilder; + +impl ExtBuilder { + pub fn build() -> sp_io::TestExternalities { + let storage = system::GenesisConfig::default() + .build_storage::() + .unwrap(); + sp_io::TestExternalities::from(storage) + } +} + +pub fn run_test(test: T) -> () +where + T: FnOnce() -> (), +{ + clear_mocks(); + ExtBuilder::build().execute_with(test); +} diff --git a/crates/vault-registry/src/tests.rs b/crates/vault-registry/src/tests.rs new file mode 100644 index 0000000000..582915b066 --- /dev/null +++ b/crates/vault-registry/src/tests.rs @@ -0,0 +1,37 @@ +// use crate::mock::{run_test, Origin, System, Test, TestEvent, VaultRegistry}; +use crate::mock::run_test; +// use crate::Error; + +// use frame_support::{assert_err, assert_ok}; +// use mocktopus::mocking::*; + +// type Event = crate::Event; + +// // use macro to avoid messing up stack trace +// macro_rules! assert_emitted { +// ($event:expr) => { +// let test_event = TestEvent::test_events($event); +// assert!(System::events().iter().any(|a| a.event == test_event)); +// }; +// } + +// macro_rules! assert_not_emitted { +// ($event:expr) => { +// let test_event = TestEvent::test_events($event); +// assert!(!System::events().iter().any(|a| a.event == test_event)); +// }; +// } + +#[test] +fn set_exchange_rate_success() { + run_test(|| { + // VaultRegistry::get_authorized_oracle.mock_safe(|| MockResult::Return(3)); + // let result = ExchangeRateOracle::set_exchange_rate(Origin::signed(3), 100); + // assert_ok!(result); + + // let exchange_rate = ExchangeRateOracle::get_exchange_rate().unwrap(); + // assert_eq!(exchange_rate, 100); + + // assert_emitted!(Event::SetExchangeRate(3, 100)); + }); +} diff --git a/crates/xclaim-core/Cargo.toml b/crates/xclaim-core/Cargo.toml index fb6ef0982f..0ecd7c7547 100644 --- a/crates/xclaim-core/Cargo.toml +++ b/crates/xclaim-core/Cargo.toml @@ -13,12 +13,8 @@ std = [ [dependencies.sp-runtime] default-features = false -git = 'https://github.com/paritytech/substrate.git' -rev = '3e651110aa06aa835790df63410a29676243fc54' -version = '2.0.0' +version = '2.0.0-alpha.5' [dependencies.frame-support] default-features = false -git = 'https://github.com/paritytech/substrate.git' -rev = '3e651110aa06aa835790df63410a29676243fc54' -version = '2.0.0' +version = '2.0.0-alpha.5' diff --git a/crates/xclaim-core/src/lib.rs b/crates/xclaim-core/src/lib.rs index 383d883ba4..b236a04b46 100644 --- a/crates/xclaim-core/src/lib.rs +++ b/crates/xclaim-core/src/lib.rs @@ -5,6 +5,9 @@ use frame_support::dispatch::DispatchError; pub enum Error { MissingExchangeRate, InvalidOracleSource, + InsufficientFunds, + InsufficientLockedFunds, + InsufficientCollateralAvailable, /// use only for errors which means something /// going very wrong and which do not match any other error @@ -12,11 +15,20 @@ pub enum Error { } impl Error { - pub fn message(&self) -> &'static str { + pub fn message(self) -> &'static str { match self { Error::MissingExchangeRate => "Exchange rate not set", Error::InvalidOracleSource => "Invalid oracle account", - Error::RuntimeError => "Runtim error", + Error::InsufficientFunds => { + "The balance of this account is insufficient to complete the transaction." + } + Error::InsufficientLockedFunds => { + "The locked token balance of this account is insufficient to burn the tokens." + } + Error::InsufficientCollateralAvailable => { + "The sender’s collateral balance is below the requested amount." + } + Error::RuntimeError => "Runtime error", } } } @@ -27,7 +39,6 @@ impl ToString for Error { } } - impl std::convert::From for DispatchError { fn from(error: Error) -> Self { DispatchError::Module {